@mohasinac/appkit 4.41.0 → 4.41.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.
Files changed (166) hide show
  1. package/dist/features/shell/SectionForm.js +44 -5
  2. package/package.json +2 -2
  3. package/dist/_internal/server/features/classified/actions.d.ts +0 -12
  4. package/dist/_internal/server/features/classified/actions.js +0 -33
  5. package/dist/_internal/server/features/digital-code/actions.d.ts +0 -12
  6. package/dist/_internal/server/features/digital-code/actions.js +0 -33
  7. package/dist/_internal/server/features/homepage/data.d.ts +0 -18
  8. package/dist/_internal/server/features/homepage/data.js +0 -70
  9. package/dist/_internal/server/features/live/actions.d.ts +0 -6
  10. package/dist/_internal/server/features/live/actions.js +0 -27
  11. package/dist/_internal/server/features/messages/actions.d.ts +0 -6
  12. package/dist/_internal/server/features/messages/actions.js +0 -6
  13. package/dist/_internal/server/features/messages/data.d.ts +0 -10
  14. package/dist/_internal/server/features/messages/data.js +0 -24
  15. package/dist/_internal/server/features/messages/index.d.ts +0 -2
  16. package/dist/_internal/server/features/messages/index.js +0 -2
  17. package/dist/_internal/server/jobs/core/testerSandboxRefresh.d.ts +0 -12
  18. package/dist/_internal/server/jobs/core/testerSandboxRefresh.js +0 -112
  19. package/dist/_internal/server/jobs/handlers/testerSandboxRefresh.d.ts +0 -2
  20. package/dist/_internal/server/jobs/handlers/testerSandboxRefresh.js +0 -2
  21. package/dist/_internal/shared/features/homepage/section-gate.d.ts +0 -14
  22. package/dist/_internal/shared/features/homepage/section-gate.js +0 -51
  23. package/dist/_internal/shared/features/lottery/config-form.d.ts +0 -58
  24. package/dist/_internal/shared/features/lottery/config-form.js +0 -71
  25. package/dist/client/api/useFeatureFlags.d.ts +0 -22
  26. package/dist/client/api/useFeatureFlags.js +0 -27
  27. package/dist/features/account/address-validation.d.ts +0 -20
  28. package/dist/features/account/address-validation.js +0 -35
  29. package/dist/features/account/components/ChatList.d.ts +0 -14
  30. package/dist/features/account/components/ChatList.js +0 -9
  31. package/dist/features/account/components/ChatWindow.d.ts +0 -17
  32. package/dist/features/account/components/ChatWindow.js +0 -6
  33. package/dist/features/account/components/MessagesView.d.ts +0 -16
  34. package/dist/features/account/components/MessagesView.js +0 -5
  35. package/dist/features/account/components/UserOffersView.d.ts +0 -10
  36. package/dist/features/account/components/UserOffersView.js +0 -5
  37. package/dist/features/account/constants/addresses.d.ts +0 -13
  38. package/dist/features/account/constants/addresses.js +0 -43
  39. package/dist/features/account/hooks/useAddressForm.d.ts +0 -24
  40. package/dist/features/account/hooks/useAddressForm.js +0 -58
  41. package/dist/features/admin/actions/chat-actions.d.ts +0 -29
  42. package/dist/features/admin/actions/chat-actions.js +0 -101
  43. package/dist/features/admin/components/AdminFeatureFlagsView.d.ts +0 -9
  44. package/dist/features/admin/components/AdminFeatureFlagsView.js +0 -109
  45. package/dist/features/admin/components/AdminSiteView.d.ts +0 -10
  46. package/dist/features/admin/components/AdminSiteView.js +0 -52
  47. package/dist/features/admin/hooks/useChat.d.ts +0 -24
  48. package/dist/features/admin/hooks/useChat.js +0 -100
  49. package/dist/features/admin/permission-map.d.ts +0 -3
  50. package/dist/features/admin/permission-map.js +0 -26
  51. package/dist/features/admin/repository/chat.repository.d.ts +0 -50
  52. package/dist/features/admin/repository/chat.repository.js +0 -223
  53. package/dist/features/auth/actions/password-change-otp-actions.d.ts +0 -18
  54. package/dist/features/auth/actions/password-change-otp-actions.js +0 -88
  55. package/dist/features/auth/actions/realtime-token-actions.d.ts +0 -11
  56. package/dist/features/auth/actions/realtime-token-actions.js +0 -38
  57. package/dist/features/auth/password-change-otp.d.ts +0 -34
  58. package/dist/features/auth/password-change-otp.js +0 -56
  59. package/dist/features/classified/components/ClassifiedContactSellerPanel.d.ts +0 -12
  60. package/dist/features/classified/components/ClassifiedContactSellerPanel.js +0 -49
  61. package/dist/features/events/api/route.d.ts +0 -12
  62. package/dist/features/events/api/route.js +0 -90
  63. package/dist/features/faq/api/route.d.ts +0 -11
  64. package/dist/features/faq/api/route.js +0 -52
  65. package/dist/features/faq/hooks/useFAQs.d.ts +0 -22
  66. package/dist/features/faq/hooks/useFAQs.js +0 -41
  67. package/dist/features/media/upload/video-poster.test.d.ts +0 -1
  68. package/dist/features/media/upload/video-poster.test.js +0 -42
  69. package/dist/features/messages/actions/index.d.ts +0 -2
  70. package/dist/features/messages/actions/index.js +0 -2
  71. package/dist/features/messages/actions/messages-actions.d.ts +0 -15
  72. package/dist/features/messages/actions/messages-actions.js +0 -33
  73. package/dist/features/messages/actions/ping-rtdb.d.ts +0 -2
  74. package/dist/features/messages/actions/ping-rtdb.js +0 -26
  75. package/dist/features/messages/hooks/useConversation.d.ts +0 -22
  76. package/dist/features/messages/hooks/useConversation.js +0 -155
  77. package/dist/features/messages/hooks/useConversations.d.ts +0 -15
  78. package/dist/features/messages/hooks/useConversations.js +0 -104
  79. package/dist/features/messages/index.d.ts +0 -6
  80. package/dist/features/messages/index.js +0 -4
  81. package/dist/features/messages/realtime.d.ts +0 -32
  82. package/dist/features/messages/realtime.js +0 -39
  83. package/dist/features/messages/repository/conversations.repository.d.ts +0 -71
  84. package/dist/features/messages/repository/conversations.repository.js +0 -284
  85. package/dist/features/messages/schemas/firestore.d.ts +0 -34
  86. package/dist/features/messages/schemas/firestore.js +0 -16
  87. package/dist/features/messages/schemas/index.d.ts +0 -177
  88. package/dist/features/messages/schemas/index.js +0 -35
  89. package/dist/features/messages/server.d.ts +0 -6
  90. package/dist/features/messages/server.js +0 -6
  91. package/dist/features/products/components/MakeOfferForm.d.ts +0 -20
  92. package/dist/features/products/components/MakeOfferForm.js +0 -9
  93. package/dist/features/reviews/api/route.d.ts +0 -21
  94. package/dist/features/reviews/api/route.js +0 -174
  95. package/dist/features/seller/api/offers/route.d.ts +0 -17
  96. package/dist/features/seller/api/offers/route.js +0 -63
  97. package/dist/features/seller/components/SellerAuctionsView.d.ts +0 -9
  98. package/dist/features/seller/components/SellerAuctionsView.js +0 -113
  99. package/dist/features/seller/components/SellerOffersPanel.d.ts +0 -21
  100. package/dist/features/seller/components/SellerOffersPanel.js +0 -160
  101. package/dist/features/seller/components/SellerPreOrdersView.d.ts +0 -6
  102. package/dist/features/seller/components/SellerPreOrdersView.js +0 -148
  103. package/dist/features/seller/components/SellerPrizeDrawsView.d.ts +0 -6
  104. package/dist/features/seller/components/SellerPrizeDrawsView.js +0 -152
  105. package/dist/features/seller/permission-map.d.ts +0 -5
  106. package/dist/features/seller/permission-map.js +0 -17
  107. package/dist/features/shell/StepForm.d.ts +0 -80
  108. package/dist/features/shell/StepForm.js +0 -136
  109. package/dist/features/stores/api/route.d.ts +0 -12
  110. package/dist/features/stores/api/route.js +0 -110
  111. package/dist/features/tester/seed-data/authored/account-auth__testing-program--admin.d.ts +0 -2
  112. package/dist/features/tester/seed-data/authored/account-auth__testing-program--admin.js +0 -33
  113. package/dist/features/tester/seed-data/authored/account-auth__testing-program.d.ts +0 -2
  114. package/dist/features/tester/seed-data/authored/account-auth__testing-program.js +0 -138
  115. package/dist/features/tester/seed-data/bids-tester-seed-data.d.ts +0 -2
  116. package/dist/features/tester/seed-data/bids-tester-seed-data.js +0 -37
  117. package/dist/features/tester/seed-data/blog-tester-seed-data.d.ts +0 -2
  118. package/dist/features/tester/seed-data/blog-tester-seed-data.js +0 -40
  119. package/dist/features/tester/seed-data/carts-tester-seed-data.d.ts +0 -3
  120. package/dist/features/tester/seed-data/carts-tester-seed-data.js +0 -70
  121. package/dist/features/tester/seed-data/categories-tester-seed-data.d.ts +0 -2
  122. package/dist/features/tester/seed-data/categories-tester-seed-data.js +0 -218
  123. package/dist/features/tester/seed-data/coupons-tester-seed-data.d.ts +0 -2
  124. package/dist/features/tester/seed-data/coupons-tester-seed-data.js +0 -76
  125. package/dist/features/tester/seed-data/events-tester-seed-data.d.ts +0 -2
  126. package/dist/features/tester/seed-data/events-tester-seed-data.js +0 -193
  127. package/dist/features/tester/seed-data/offers-tester-seed-data.d.ts +0 -2
  128. package/dist/features/tester/seed-data/offers-tester-seed-data.js +0 -191
  129. package/dist/features/tester/seed-data/orders-tester-seed-data.d.ts +0 -2
  130. package/dist/features/tester/seed-data/orders-tester-seed-data.js +0 -329
  131. package/dist/features/tester/seed-data/products-tester-seed-data.d.ts +0 -2
  132. package/dist/features/tester/seed-data/products-tester-seed-data.js +0 -815
  133. package/dist/features/tester/seed-data/stores-tester-seed-data.d.ts +0 -2
  134. package/dist/features/tester/seed-data/stores-tester-seed-data.js +0 -79
  135. package/dist/features/wishlist/components/WishlistToggleButton.d.ts +0 -12
  136. package/dist/features/wishlist/components/WishlistToggleButton.js +0 -36
  137. package/dist/monitoring/analytics.d.ts +0 -80
  138. package/dist/monitoring/analytics.js +0 -176
  139. package/dist/next/middleware/pii-redactor.d.ts +0 -32
  140. package/dist/next/middleware/pii-redactor.js +0 -84
  141. package/dist/providers/db-firebase/realtime.d.ts +0 -42
  142. package/dist/providers/db-firebase/realtime.js +0 -138
  143. package/dist/security/rbac/Can.d.ts +0 -31
  144. package/dist/security/rbac/Can.js +0 -22
  145. package/dist/security/rbac/default-roles.d.ts +0 -2
  146. package/dist/security/rbac/default-roles.js +0 -83
  147. package/dist/security/rbac/hook.d.ts +0 -20
  148. package/dist/security/rbac/hook.js +0 -30
  149. package/dist/security/rbac/index.d.ts +0 -8
  150. package/dist/security/rbac/index.js +0 -6
  151. package/dist/security/rbac/middleware.d.ts +0 -51
  152. package/dist/security/rbac/middleware.js +0 -51
  153. package/dist/security/rbac/resolver.d.ts +0 -5
  154. package/dist/security/rbac/resolver.js +0 -38
  155. package/dist/security/rbac/server.d.ts +0 -24
  156. package/dist/security/rbac/server.js +0 -42
  157. package/dist/security/rbac/types.d.ts +0 -22
  158. package/dist/security/rbac/types.js +0 -2
  159. package/dist/seed/conversations-seed-data.d.ts +0 -2
  160. package/dist/seed/conversations-seed-data.js +0 -292
  161. package/dist/utils/filter.helper.d.ts +0 -18
  162. package/dist/utils/filter.helper.js +0 -21
  163. package/dist/utils/pagination.helper.d.ts +0 -23
  164. package/dist/utils/pagination.helper.js +0 -24
  165. package/dist/utils/search-tokens.d.ts +0 -2
  166. package/dist/utils/search-tokens.js +0 -21
@@ -1,2 +0,0 @@
1
- import type { StoreDocument } from "../../stores/schemas";
2
- export declare const storesTesterSeedData: Partial<StoreDocument>[];
@@ -1,79 +0,0 @@
1
- /*
2
- * WHY: Shared tester sandbox — one auto-approved test store testers browse/buy from and
3
- * use as a reference when creating their own listings. Auto-expires after 7 days
4
- * (testerSandboxCleanup) and is invisible to non-testers (application-layer filter).
5
- * WHAT: Exports 2 StoreDocument partials tagged isTestData:true — the shared sandbox
6
- * (owned by admin) and the automated runner's OWN store.
7
- *
8
- * 🛑 TWO STORES, TWO OWNERS, AND THE SPLIT IS LOAD-BEARING.
9
- *
10
- * `store-tester-sandbox` stays owned by `user-admin-letitrip`. Several authored cases
11
- * name that owner explicitly — buying__bidding.ts:211 says "Sign in as admin@letitrip.in,
12
- * the owner of store-tester-sandbox", and :228 warns that signing in as anyone else
13
- * "gives a 403 that reads like a permissions bug and is not one". Repointing it would
14
- * turn those cases into false failures.
15
- *
16
- * `store-claude-tester` exists because `user-claude-tester` owned NO store while its user
17
- * document carried `storeId: "store-tester-qa-seller"` — a store whose `ownerId` is
18
- * `user-tester-qa`. The app resolves sellers by `ownerId`, so every /api/store/* call for
19
- * the runner returned 403 "No store found for this account" while the full Store Panel
20
- * rendered over it. That one-way pointer blocked 36 checklist cases (measured, run
21
- * 1789300124915). Root Cause #42's denormalised-mirror shape on the user<->store axis.
22
- *
23
- * It is deliberately EMPTY of listings. The seller cases it unblocks are create/CRUD
24
- * flows (listing-lifecycle, coupons, bundles, classifieds), which want a clean store;
25
- * cases that need pre-existing stock still use the shared sandbox.
26
- *
27
- * EXPORTS:
28
- * storesTesterSeedData — Array of 2 Partial<StoreDocument> for the seed runner
29
- *
30
- * @tag domain:stores,tester
31
- * @tag layer:seed
32
- * @tag pattern:none
33
- * @tag access:server-only
34
- * @tag consumers:seed/index.ts,seed-cli.mjs
35
- * @tag sideEffects:none
36
- */
37
- import { STORE_FIELDS } from "../../../constants/field-names";
38
- import { seedPhoto } from "../../../seed/_helpers/media";
39
- import { testDataExpiresAt } from "./tester-ttl";
40
- export const storesTesterSeedData = [
41
- {
42
- id: "store-tester-sandbox",
43
- storeSlug: "store-tester-sandbox",
44
- ownerId: "user-admin-letitrip",
45
- storeName: "Tester Sandbox Store",
46
- storeDescription: "Shared test store for the tester QA program. Products here are disposable and auto-expire — buy, bid, and review freely.",
47
- storeCategory: "category-tester-sandbox",
48
- storeLogoURL: seedPhoto("store-logo-tester-sandbox-20260101", 400, 400),
49
- storeBannerURL: seedPhoto("store-banner-tester-sandbox-20260101", 1600, 400),
50
- status: STORE_FIELDS.STATUS_VALUES.ACTIVE,
51
- isPublic: true,
52
- bio: "This store exists only for tester QA. Everything in it is disposable test data.",
53
- returnPolicy: "N/A — test data.",
54
- shippingPolicy: "N/A — test data.",
55
- isTestData: true,
56
- testDataExpiresAt: testDataExpiresAt(),
57
- createdAt: new Date(),
58
- updatedAt: new Date(),
59
- },
60
- {
61
- id: "store-claude-tester",
62
- storeSlug: "store-claude-tester",
63
- ownerId: "user-claude-tester",
64
- storeName: "Claude Tester Store",
65
- storeDescription: "The automated checklist runner's own store. Exists so seller-side create/edit flows can be exercised against a store the runner actually owns. Disposable test data.",
66
- storeCategory: "category-tester-sandbox",
67
- storeLogoURL: seedPhoto("store-logo-claude-tester-20260101", 400, 400),
68
- storeBannerURL: seedPhoto("store-banner-claude-tester-20260101", 1600, 400),
69
- status: STORE_FIELDS.STATUS_VALUES.ACTIVE,
70
- isPublic: true,
71
- bio: "Automated tester's store. Everything in it is disposable test data.",
72
- returnPolicy: "N/A — test data.",
73
- shippingPolicy: "N/A — test data.",
74
- isTestData: true,
75
- testDataExpiresAt: testDataExpiresAt(),
76
- createdAt: new Date(),
77
- updatedAt: new Date(),
78
- },
79
- ];
@@ -1,12 +0,0 @@
1
- import type { MouseEvent } from "react";
2
- interface WishlistToggleButtonProps {
3
- inWishlist: boolean;
4
- isLoading?: boolean;
5
- onToggle: (e: MouseEvent) => void | Promise<void>;
6
- addLabel: string;
7
- removeLabel: string;
8
- className?: string;
9
- size?: "sm" | "md" | "lg";
10
- }
11
- export declare function WishlistToggleButton({ inWishlist, isLoading, onToggle, addLabel, removeLabel, className, size, }: WishlistToggleButtonProps): import("react").JSX.Element;
12
- export {};
@@ -1,36 +0,0 @@
1
- "use client";
2
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useState } from "react";
4
- import { Button, LoginRequiredModal } from "../../../ui";
5
- import { isAuthError } from "../../../utils/auth-error";
6
- import { normalizeError } from "../../../errors/normalize";
7
- const sizeClasses = {
8
- sm: "w-7 h-7",
9
- md: "w-9 h-9",
10
- lg: "w-11 h-11",
11
- };
12
- export function WishlistToggleButton({ inWishlist, isLoading = false, onToggle, addLabel, removeLabel, className = "", size = "md", }) {
13
- const label = inWishlist ? removeLabel : addLabel;
14
- const [showLoginModal, setShowLoginModal] = useState(false);
15
- async function handleClick(e) {
16
- try {
17
- await onToggle(e);
18
- }
19
- catch (err) {
20
- void normalizeError(err);
21
- if (isAuthError(err)) {
22
- setShowLoginModal(true);
23
- }
24
- }
25
- }
26
- return (_jsxs(_Fragment, { children: [_jsx(LoginRequiredModal, { isOpen: showLoginModal, onClose: () => setShowLoginModal(false), message: "You need to be signed in to save items to your wishlist. Please log in or create an account." }), _jsx(Button, { type: "button", onClick: handleClick, disabled: isLoading, "aria-label": label, title: label, rounded: "full", className: `
27
- flex items-[center] justify-[center]
28
- transition-all duration-150
29
- ${inWishlist
30
- ? "bg-error-surface text-error hover:bg-error-surface"
31
- : "bg-[rgba(255,255,255,0.8)] text-[var(--appkit-color-text-faint)] hover:text-error"}
32
- ${sizeClasses[size]}
33
- ${isLoading ? "opacity-50 cursor-not-allowed" : "cursor-pointer"}
34
- ${className}
35
- `, children: _jsx("svg", { className: "w-4/6 h-4/6", viewBox: "0 0 24 24", fill: inWishlist ? "currentColor" : "none", stroke: "currentColor", strokeWidth: 2, children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" }) }) })] }));
36
- }
@@ -1,80 +0,0 @@
1
- import type { JsonValue } from "@mohasinac/appkit";
2
- export declare const ANALYTICS_EVENTS: {
3
- readonly PAGE_VIEW: "page_view";
4
- readonly LOGIN: "login";
5
- readonly SIGN_UP: "sign_up";
6
- readonly LOGOUT: "logout";
7
- readonly VIEW_ITEM: "view_item";
8
- readonly ADD_TO_CART: "add_to_cart";
9
- readonly PURCHASE: "purchase";
10
- readonly PLACE_BID: "place_bid";
11
- readonly WIN_AUCTION: "win_auction";
12
- readonly SEARCH: "search";
13
- readonly VIEW_FAQ: "view_faq";
14
- };
15
- export type AnalyticsEvent = (typeof ANALYTICS_EVENTS)[keyof typeof ANALYTICS_EVENTS];
16
- export declare function initializeAnalytics(params: {
17
- app: JsonValue;
18
- enabled?: boolean;
19
- currency?: string;
20
- }): Promise<void>;
21
- export declare const trackEvent: (eventName: string, eventParams?: Record<string, JsonValue>) => void;
22
- export declare const setAnalyticsUserId: (userId: string | null) => void;
23
- export declare const setAnalyticsUserProperties: (properties: Record<string, string | number | boolean>) => void;
24
- export declare const trackPageView: (page_path: string, page_title: string) => void;
25
- export declare const trackAuth: {
26
- login: (method: "email" | "google" | "apple") => void;
27
- register: (method: "email" | "google" | "apple") => void;
28
- logout: () => void;
29
- };
30
- export declare const trackEcommerce: {
31
- viewProduct: (product: {
32
- id: string;
33
- name: string;
34
- category: string;
35
- price: number;
36
- currency?: string;
37
- }) => void;
38
- addToCart: (product: {
39
- id: string;
40
- name: string;
41
- category: string;
42
- price: number;
43
- quantity: number;
44
- currency?: string;
45
- }) => void;
46
- purchase: (order: {
47
- id: string;
48
- total: number;
49
- tax?: number;
50
- shipping?: number;
51
- currency?: string;
52
- items: Array<{
53
- id: string;
54
- name: string;
55
- category: string;
56
- price: number;
57
- quantity: number;
58
- }>;
59
- }) => void;
60
- placeBid: (product: {
61
- id: string;
62
- name: string;
63
- bidAmount: number;
64
- }) => void;
65
- winAuction: (product: {
66
- id: string;
67
- name: string;
68
- finalBid: number;
69
- }) => void;
70
- };
71
- export declare const trackContent: {
72
- search: (searchTerm: string, resultsCount: number) => void;
73
- viewFaq: (faqId: string, question: string) => void;
74
- };
75
- export declare const trackForm: {
76
- submit: (formName: string) => void;
77
- };
78
- export declare const trackAdmin: {
79
- action: (action: string, target: string) => void;
80
- };
@@ -1,176 +0,0 @@
1
- import { normalizeError } from "../errors/normalize";
2
- import { getAnalytics, isSupported, logEvent, setUserId, setUserProperties, } from "firebase/analytics";
3
- import { logger } from "../core/Logger";
4
- let analytics = null;
5
- let analyticsEnabled = false;
6
- let ecommerceCurrency = "USD";
7
- export const ANALYTICS_EVENTS = {
8
- PAGE_VIEW: "page_view",
9
- LOGIN: "login",
10
- SIGN_UP: "sign_up",
11
- LOGOUT: "logout",
12
- VIEW_ITEM: "view_item",
13
- ADD_TO_CART: "add_to_cart",
14
- PURCHASE: "purchase",
15
- PLACE_BID: "place_bid",
16
- WIN_AUCTION: "win_auction",
17
- SEARCH: "search",
18
- VIEW_FAQ: "view_faq",
19
- };
20
- export async function initializeAnalytics(params) {
21
- analyticsEnabled = params.enabled ?? true;
22
- if (params.currency) {
23
- ecommerceCurrency = params.currency;
24
- }
25
- if (!analyticsEnabled)
26
- return;
27
- try {
28
- const supported = await isSupported();
29
- if (!supported)
30
- return;
31
- analytics = getAnalytics(params.app);
32
- }
33
- catch (error) {
34
- void normalizeError(error);
35
- analytics = null;
36
- logger.warn("Firebase Analytics disabled", { error });
37
- }
38
- }
39
- export const trackEvent = (eventName, eventParams) => {
40
- if (!analytics || !analyticsEnabled)
41
- return;
42
- try {
43
- logEvent(analytics, eventName, eventParams);
44
- }
45
- catch (error) {
46
- void normalizeError(error);
47
- logger.error(`Failed to log event ${eventName}`, { error });
48
- }
49
- };
50
- export const setAnalyticsUserId = (userId) => {
51
- if (!analytics || !analyticsEnabled)
52
- return;
53
- try {
54
- setUserId(analytics, userId);
55
- }
56
- catch (error) {
57
- void normalizeError(error);
58
- logger.error("Failed to set user ID", { error });
59
- }
60
- };
61
- export const setAnalyticsUserProperties = (properties) => {
62
- if (!analytics || !analyticsEnabled)
63
- return;
64
- try {
65
- setUserProperties(analytics, properties);
66
- }
67
- catch (error) {
68
- void normalizeError(error);
69
- logger.error("Failed to set user properties", { error });
70
- }
71
- };
72
- export const trackPageView = (page_path, page_title) => {
73
- trackEvent(ANALYTICS_EVENTS.PAGE_VIEW, {
74
- page_path,
75
- page_title,
76
- });
77
- };
78
- export const trackAuth = {
79
- login: (method) => {
80
- trackEvent(ANALYTICS_EVENTS.LOGIN, { method });
81
- },
82
- register: (method) => {
83
- trackEvent(ANALYTICS_EVENTS.SIGN_UP, { method });
84
- },
85
- logout: () => {
86
- trackEvent(ANALYTICS_EVENTS.LOGOUT);
87
- },
88
- };
89
- export const trackEcommerce = {
90
- viewProduct: (product) => {
91
- const currency = product.currency ?? ecommerceCurrency;
92
- trackEvent(ANALYTICS_EVENTS.VIEW_ITEM, {
93
- currency,
94
- value: product.price,
95
- items: [
96
- {
97
- item_id: product.id,
98
- item_name: product.name,
99
- item_category: product.category,
100
- price: product.price,
101
- },
102
- ],
103
- });
104
- },
105
- addToCart: (product) => {
106
- const currency = product.currency ?? ecommerceCurrency;
107
- trackEvent(ANALYTICS_EVENTS.ADD_TO_CART, {
108
- currency,
109
- value: product.price * product.quantity,
110
- items: [
111
- {
112
- item_id: product.id,
113
- item_name: product.name,
114
- item_category: product.category,
115
- price: product.price,
116
- quantity: product.quantity,
117
- },
118
- ],
119
- });
120
- },
121
- purchase: (order) => {
122
- const currency = order.currency ?? ecommerceCurrency;
123
- trackEvent(ANALYTICS_EVENTS.PURCHASE, {
124
- transaction_id: order.id,
125
- currency,
126
- value: order.total,
127
- tax: order.tax || 0,
128
- shipping: order.shipping || 0,
129
- items: order.items.map((item) => ({
130
- item_id: item.id,
131
- item_name: item.name,
132
- item_category: item.category,
133
- price: item.price,
134
- quantity: item.quantity,
135
- })),
136
- });
137
- },
138
- placeBid: (product) => {
139
- trackEvent(ANALYTICS_EVENTS.PLACE_BID, {
140
- product_id: product.id,
141
- product_name: product.name,
142
- bid_amount: product.bidAmount,
143
- });
144
- },
145
- winAuction: (product) => {
146
- trackEvent(ANALYTICS_EVENTS.WIN_AUCTION, {
147
- product_id: product.id,
148
- product_name: product.name,
149
- final_bid: product.finalBid,
150
- });
151
- },
152
- };
153
- export const trackContent = {
154
- search: (searchTerm, resultsCount) => {
155
- trackEvent(ANALYTICS_EVENTS.SEARCH, {
156
- search_term: searchTerm,
157
- results_count: resultsCount,
158
- });
159
- },
160
- viewFaq: (faqId, question) => {
161
- trackEvent(ANALYTICS_EVENTS.VIEW_FAQ, {
162
- faq_id: faqId,
163
- question,
164
- });
165
- },
166
- };
167
- export const trackForm = {
168
- submit: (formName) => {
169
- trackEvent("form_submit", { form_name: formName });
170
- },
171
- };
172
- export const trackAdmin = {
173
- action: (action, target) => {
174
- trackEvent("admin_action", { action, target });
175
- },
176
- };
@@ -1,32 +0,0 @@
1
- import type { AuthRequestContext, Middleware } from "./types";
2
- export interface PiiRedactionRule {
3
- /** Fields to redact from the top-level object or items[] */
4
- fields: string[];
5
- /**
6
- * If `true` (default), replace the value with a masked string like `a***@domain.com`.
7
- * If `false`, remove the field entirely.
8
- */
9
- mask?: boolean;
10
- /** Only redact if the caller does NOT have this permission string */
11
- unless?: string;
12
- }
13
- /**
14
- * createPiiRedactorMiddleware — field-level PII redaction for non-owners.
15
- *
16
- * Removes or masks declared PII fields from the response body unless the
17
- * calling user holds the specified permission.
18
- *
19
- * @example
20
- * ```ts
21
- * const userListRedactor = createPiiRedactorMiddleware([
22
- * { fields: ["email", "phone"], mask: true, unless: "admin:users:pii" },
23
- * { fields: ["bankAccount"], mask: false, unless: "admin:payouts" },
24
- * ]);
25
- *
26
- * export const GET = createApiMiddleware({
27
- * permission: "admin:users",
28
- * middlewares: [userListRedactor],
29
- * })(async (input, ctx) => { ... });
30
- * ```
31
- */
32
- export declare function createPiiRedactorMiddleware(rules: PiiRedactionRule[]): Middleware<AuthRequestContext>;
@@ -1,84 +0,0 @@
1
- import { normalizeError } from "../../errors/normalize";
2
- import { NextResponse } from "next/server";
3
- function maskPii(value) {
4
- if (value.includes("@")) {
5
- const [local, domain] = value.split("@");
6
- return `${local?.[0] ?? ""}***@${domain}`;
7
- }
8
- if (/^\+?\d{7,}$/.test(value)) {
9
- return value.slice(0, 3) + "***" + value.slice(-2);
10
- }
11
- return "***";
12
- }
13
- function hasPermission(permissions, required) {
14
- return permissions.has("*") || permissions.has(required);
15
- }
16
- function redactObject(obj, fields, mask) {
17
- return fields.reduce((acc, field) => {
18
- if (!(field in acc))
19
- return acc;
20
- const current = acc[field];
21
- const redacted = { ...acc };
22
- if (mask && typeof current === "string") {
23
- redacted[field] = maskPii(current);
24
- }
25
- else {
26
- delete redacted[field];
27
- }
28
- return redacted;
29
- }, obj);
30
- }
31
- /**
32
- * createPiiRedactorMiddleware — field-level PII redaction for non-owners.
33
- *
34
- * Removes or masks declared PII fields from the response body unless the
35
- * calling user holds the specified permission.
36
- *
37
- * @example
38
- * ```ts
39
- * const userListRedactor = createPiiRedactorMiddleware([
40
- * { fields: ["email", "phone"], mask: true, unless: "admin:users:pii" },
41
- * { fields: ["bankAccount"], mask: false, unless: "admin:payouts" },
42
- * ]);
43
- *
44
- * export const GET = createApiMiddleware({
45
- * permission: "admin:users",
46
- * middlewares: [userListRedactor],
47
- * })(async (input, ctx) => { ... });
48
- * ```
49
- */
50
- export function createPiiRedactorMiddleware(rules) {
51
- return async (_req, ctx, next) => {
52
- const response = await next();
53
- const contentType = response.headers.get("content-type") ?? "";
54
- if (!contentType.includes("application/json"))
55
- return response;
56
- let body;
57
- try {
58
- body = (await response.json());
59
- }
60
- catch (_err) {
61
- void normalizeError(_err);
62
- return response;
63
- }
64
- for (const { fields, mask = true, unless } of rules) {
65
- // Skip redaction if caller has the authorising permission
66
- if (unless && hasPermission(ctx.user.permissions, unless))
67
- continue;
68
- // Handle both paginated { data: [] } and single-object responses
69
- if (Array.isArray(body.data)) {
70
- body = {
71
- ...body,
72
- data: body.data.map((item) => redactObject(item, fields, mask)),
73
- };
74
- }
75
- else {
76
- body = redactObject(body, fields, mask);
77
- }
78
- }
79
- return NextResponse.json(body, {
80
- status: response.status,
81
- headers: response.headers,
82
- });
83
- };
84
- }
@@ -1,42 +0,0 @@
1
- /**
2
- * FirebaseRealtimeRepository<T>
3
- *
4
- * Abstract base implementing `IRealtimeRepository<T>` from `@mohasinac/contracts`
5
- * against Firebase Realtime Database (Admin SDK).
6
- *
7
- * Realtime DB does not support Firestore-style queries; `findAll()` loads the
8
- * entire collection node. Use this only for small, high-churn datasets (e.g.
9
- * presence, chat, live-bid prices).
10
- *
11
- * Usage:
12
- * ```ts
13
- * class PresenceRepository extends FirebaseRealtimeRepository<PresenceEntry> {
14
- * constructor() { super("presence"); }
15
- * }
16
- * ```
17
- */
18
- import type { IRealtimeRepository, PagedResult, SieveQuery, WhereOp } from "../../contracts";
19
- import type { DocumentData } from "firebase-admin/firestore";
20
- export declare abstract class FirebaseRealtimeRepository<T extends DocumentData> implements IRealtimeRepository<T> {
21
- protected readonly path: string;
22
- constructor(rtdbPath: string);
23
- protected get rtdb(): import("firebase-admin/database").Database;
24
- protected nodeRef(id?: string): import("@firebase/database-types").Reference;
25
- findById(id: string): Promise<T | null>;
26
- findAll(query?: SieveQuery): Promise<PagedResult<T>>;
27
- findWhere(field: keyof T, _op: WhereOp, value: unknown): Promise<T[]>;
28
- create(data: Omit<T, "id" | "createdAt" | "updatedAt">): Promise<T>;
29
- update(id: string, data: Partial<T>): Promise<T>;
30
- delete(id: string): Promise<void>;
31
- batchCreate(items: Array<Omit<T, "id" | "createdAt" | "updatedAt">>): Promise<T[]>;
32
- batchDelete(ids: string[]): Promise<void>;
33
- /**
34
- * Subscribe to a single RTDB node.
35
- * Returns an unsubscribe function.
36
- *
37
- * NOTE: The Admin SDK `onValue` callback runs in-process but is not browser-side.
38
- * For browser real-time, use the client SDK directly.
39
- */
40
- subscribe(id: string, cb: (data: T | null) => void): () => void;
41
- subscribeWhere(field: keyof T, value: unknown, cb: (items: T[]) => void): () => void;
42
- }
@@ -1,138 +0,0 @@
1
- /**
2
- * FirebaseRealtimeRepository<T>
3
- *
4
- * Abstract base implementing `IRealtimeRepository<T>` from `@mohasinac/contracts`
5
- * against Firebase Realtime Database (Admin SDK).
6
- *
7
- * Realtime DB does not support Firestore-style queries; `findAll()` loads the
8
- * entire collection node. Use this only for small, high-churn datasets (e.g.
9
- * presence, chat, live-bid prices).
10
- *
11
- * Usage:
12
- * ```ts
13
- * class PresenceRepository extends FirebaseRealtimeRepository<PresenceEntry> {
14
- * constructor() { super("presence"); }
15
- * }
16
- * ```
17
- */
18
- import { getAdminRealtimeDb } from "./admin";
19
- export class FirebaseRealtimeRepository {
20
- constructor(rtdbPath) {
21
- this.path = rtdbPath;
22
- }
23
- get rtdb() {
24
- return getAdminRealtimeDb();
25
- }
26
- nodeRef(id) {
27
- return id ? this.rtdb.ref(`${this.path}/${id}`) : this.rtdb.ref(this.path);
28
- }
29
- // -- IReadRepository --------------------------------------------------------
30
- async findById(id) {
31
- const snap = await this.nodeRef(id).get();
32
- if (!snap.exists())
33
- return null;
34
- return { id, ...snap.val() };
35
- }
36
- async findAll(query) {
37
- const snap = await this.nodeRef().get();
38
- if (!snap.exists()) {
39
- return {
40
- data: [],
41
- total: 0,
42
- page: 1,
43
- perPage: query?.perPage ?? 20,
44
- totalPages: 0,
45
- };
46
- }
47
- const raw = snap.val();
48
- let data = Object.entries(raw).map(([id, val]) => ({ id, ...val }));
49
- // Basic server-side sort (RTDB has no native multi-field sort)
50
- if (query?.sort) {
51
- const key = query.sort;
52
- const dir = query.order === "desc" ? -1 : 1;
53
- data = data.slice().sort((a, b) => {
54
- const av = a[key];
55
- const bv = b[key];
56
- if (av === bv)
57
- return 0;
58
- return (av < bv ? -1 : 1) * dir;
59
- });
60
- }
61
- const total = data.length;
62
- const perPage = Math.max(1, query?.perPage ?? 20);
63
- const page = Math.max(1, query?.page ?? 1);
64
- const offset = (page - 1) * perPage;
65
- const totalPages = total === 0 ? 0 : Math.max(1, Math.ceil(total / perPage));
66
- return {
67
- data: data.slice(offset, offset + perPage),
68
- total,
69
- page,
70
- perPage,
71
- totalPages,
72
- };
73
- }
74
- async findWhere(field, _op, value) {
75
- // RTDB Admin SDK supports orderByChild + equalTo for == only
76
- const snap = await this.nodeRef()
77
- .orderByChild(field)
78
- .equalTo(value)
79
- .get();
80
- if (!snap.exists())
81
- return [];
82
- const raw = snap.val();
83
- return Object.entries(raw).map(([id, val]) => ({ id, ...val }));
84
- }
85
- // -- IWriteRepository -------------------------------------------------------
86
- async create(data) {
87
- const now = Date.now();
88
- const payload = { ...data, createdAt: now, updatedAt: now };
89
- const ref = await this.nodeRef().push(payload);
90
- return { id: ref.key, ...payload };
91
- }
92
- async update(id, data) {
93
- const payload = { ...data, updatedAt: Date.now() };
94
- await this.nodeRef(id).update(payload);
95
- const snap = await this.nodeRef(id).get();
96
- return { id, ...snap.val() };
97
- }
98
- async delete(id) {
99
- await this.nodeRef(id).remove();
100
- }
101
- async batchCreate(items) {
102
- return Promise.all(items.map((item) => this.create(item)));
103
- }
104
- async batchDelete(ids) {
105
- await Promise.all(ids.map((id) => this.delete(id)));
106
- }
107
- // -- IRealtimeRepository ---------------------------------------------------
108
- /**
109
- * Subscribe to a single RTDB node.
110
- * Returns an unsubscribe function.
111
- *
112
- * NOTE: The Admin SDK `onValue` callback runs in-process but is not browser-side.
113
- * For browser real-time, use the client SDK directly.
114
- */
115
- subscribe(id, cb) {
116
- const ref = this.nodeRef(id);
117
- const handler = (snap) => {
118
- cb(snap.exists() ? { id, ...snap.val() } : null);
119
- };
120
- ref.on("value", handler);
121
- return () => ref.off("value", handler);
122
- }
123
- subscribeWhere(field, value, cb) {
124
- const ref = this.nodeRef()
125
- .orderByChild(field)
126
- .equalTo(value);
127
- const handler = (snap) => {
128
- if (!snap.exists()) {
129
- cb([]);
130
- return;
131
- }
132
- const raw = snap.val();
133
- cb(Object.entries(raw).map(([id, v]) => ({ id, ...v })));
134
- };
135
- ref.on("value", handler);
136
- return () => ref.off("value", handler);
137
- }
138
- }