@mohasinac/appkit 3.2.12 → 3.2.14

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.
@@ -2,6 +2,8 @@ import { cache } from "react";
2
2
  import { productRepository } from "../../../../repositories";
3
3
  import { reviewRepository } from "../../../../repositories";
4
4
  import { getAdminDb } from "../../../../providers/db-firebase";
5
+ import { normalizeError } from "../../../../errors/normalize";
6
+ import { serverLogger } from "../../../../monitoring/server-logger";
5
7
  import { PRODUCT_COLLECTION } from "../../../../features/products/schemas/firestore";
6
8
  import { PRODUCTS_SITEMAP_LIMIT } from "../../../shared/features/products/config";
7
9
  import { PRODUCT_FIELDS } from "../../../../constants/field-names";
@@ -46,7 +48,9 @@ export async function listSitemapProducts() {
46
48
  };
47
49
  });
48
50
  }
49
- catch {
51
+ catch (err) {
52
+ void normalizeError(err);
53
+ serverLogger.warn("products-data: listSitemapProducts failed — returning empty", { error: err instanceof Error ? err.message : String(err) });
50
54
  return [];
51
55
  }
52
56
  }
@@ -83,7 +83,8 @@ function decodeCursor(cursor) {
83
83
  const page = Number(parsed.page);
84
84
  return Number.isFinite(page) && page > 0 ? page : null;
85
85
  }
86
- catch {
86
+ catch (_err) {
87
+ // Malformed base64 or JSON — treat as no cursor (start from first page)
87
88
  return null;
88
89
  }
89
90
  }
@@ -21,8 +21,8 @@ async function persist(name, err, ctxLike) {
21
21
  occurredAt: (ctxLike?.now ?? new Date()).getTime(),
22
22
  });
23
23
  }
24
- catch {
25
- /* logging-the-logging-failure is forbidden */
24
+ catch (_err) {
25
+ // Logging the logging failure would cause infinite recursion — intentionally swallowed
26
26
  }
27
27
  }
28
28
  export function wrapScheduleHandler(name, handler) {
@@ -36,8 +36,8 @@ export const actionTracker = {
36
36
  result.catch((err) => { void normalizeError(err); });
37
37
  }
38
38
  }
39
- catch {
40
- // Intentionally swallowedtelemetry must never break callers.
39
+ catch (_err) {
40
+ // Telemetry sink threw synchronously swallowed so the caller action always completes
41
41
  }
42
42
  },
43
43
  };
@@ -62,8 +62,9 @@ export async function sendNotification(input) {
62
62
  userChannelPrefs = fetchedUserDoc?.notificationPreferences?.channels ?? {};
63
63
  userTypePrefs = fetchedUserDoc?.notificationPreferences?.types ?? {};
64
64
  }
65
- catch {
66
- // non-fatal: fall through with empty prefs (all enabled)
65
+ catch (err) {
66
+ void normalizeError(err);
67
+ serverLogger.warn("notification-actions: user prefs lookup failed — defaulting to all channels enabled", { userId: input.userId, error: err instanceof Error ? err.message : String(err) });
67
68
  }
68
69
  // If caller didn't supply userEmail, try to decrypt it from the already-fetched user doc.
69
70
  const resolvedEmail = userEmail ??
@@ -181,8 +181,8 @@ class ChatRepository extends BaseRepository {
181
181
  try {
182
182
  await getAdminRealtimeDb().ref(`/chat/${chatId}`).remove();
183
183
  }
184
- catch {
185
- // Non-fatal — RTDB subtree cleanup is best-effort
184
+ catch (_err) {
185
+ // RTDB cleanup is best-effort — Firestore doc already deleted, messages will be orphaned until TTL cleanup
186
186
  }
187
187
  return "deleted";
188
188
  }
@@ -210,8 +210,8 @@ class ChatRepository extends BaseRepository {
210
210
  try {
211
211
  await getAdminRealtimeDb().ref(`/chat/${chatId}`).remove();
212
212
  }
213
- catch {
214
- // Non-fatal
213
+ catch (_err) {
214
+ // RTDB cleanup is best-effort — adminDeleted flag in Firestore already revokes access
215
215
  }
216
216
  }
217
217
  catch (error) {
@@ -1,4 +1,5 @@
1
1
  import { normalizeError } from "../../../errors/normalize";
2
+ import { serverLogger } from "../../../monitoring/server-logger";
2
3
  /**
3
4
  * Site Settings Repository
4
5
  *
@@ -131,8 +132,9 @@ export class SiteSettingsRepository extends BaseRepository {
131
132
  try {
132
133
  result[key] = decryptSecret(value);
133
134
  }
134
- catch {
135
- result[key] = ""; // auth-tag mismatch or malformed blob treat as missing
135
+ catch (_err) {
136
+ // Auth-tag mismatch or malformed ciphertext (e.g., key rotation) — treat as missing credential
137
+ result[key] = "";
136
138
  }
137
139
  }
138
140
  }
@@ -210,8 +212,10 @@ export class SiteSettingsRepository extends BaseRepository {
210
212
  .get();
211
213
  return doc.exists;
212
214
  }
213
- catch {
214
- return false;
215
+ catch (err) {
216
+ void normalizeError(err);
217
+ serverLogger.error("site-settings-repo: exists() Firestore read failed — rethrowing to avoid masking missing settings", { error: err instanceof Error ? err.message : String(err) });
218
+ throw err;
215
219
  }
216
220
  }
217
221
  /**
@@ -1,5 +1,7 @@
1
1
  import { sieveFilter, sieveAnd, SIEVE_OP } from "@mohasinac/appkit";
2
2
  import { createCronJob } from "../registry";
3
+ import { normalizeError } from "../../../errors/normalize";
4
+ import { serverLogger } from "../../../monitoring/server-logger";
3
5
  export function createAuctionExpiryJob(auctionRepo) {
4
6
  return createCronJob({
5
7
  name: "auctions_expire",
@@ -26,7 +28,9 @@ export function createAuctionExpiryJob(auctionRepo) {
26
28
  });
27
29
  processed++;
28
30
  }
29
- catch {
31
+ catch (err) {
32
+ void normalizeError(err);
33
+ serverLogger.warn("auction-expiry: per-auction close failed", { auctionId: auction.id, error: err instanceof Error ? err.message : String(err) });
30
34
  errors++;
31
35
  }
32
36
  }
@@ -1,5 +1,7 @@
1
1
  import { sieveFilter, sieveAnd, SIEVE_OP } from "@mohasinac/appkit";
2
2
  import { createCronJob } from "../registry";
3
+ import { normalizeError } from "../../../errors/normalize";
4
+ import { serverLogger } from "../../../monitoring/server-logger";
3
5
  export function createPreOrderReminderJob(preOrderRepo) {
4
6
  const sevenDaysFromNow = () => {
5
7
  const d = new Date();
@@ -28,7 +30,9 @@ export function createPreOrderReminderJob(preOrderRepo) {
28
30
  });
29
31
  processed++;
30
32
  }
31
- catch {
33
+ catch (err) {
34
+ void normalizeError(err);
35
+ serverLogger.warn("preorder-reminder: per-order update failed", { orderId: order.id, error: err instanceof Error ? err.message : String(err) });
32
36
  errors++;
33
37
  }
34
38
  }
@@ -1,4 +1,5 @@
1
1
  import { normalizeError } from "../../../errors/normalize";
2
+ import { serverLogger } from "../../../monitoring/server-logger";
2
3
  /**
3
4
  * Coupons Repository
4
5
  *
@@ -270,8 +271,10 @@ export class CouponsRepository extends BaseRepository {
270
271
  const data = doc.data();
271
272
  return data.usageCount ?? 1;
272
273
  }
273
- catch {
274
- return 0;
274
+ catch (err) {
275
+ void normalizeError(err);
276
+ serverLogger.error("coupons-repo: getUserCouponUsageCount failed — rethrowing to prevent coupon limit bypass", { userId, couponId, error: err instanceof Error ? err.message : String(err) });
277
+ throw err;
275
278
  }
276
279
  }
277
280
  /**
@@ -1,4 +1,5 @@
1
1
  import { MEDIA_ENDPOINTS } from "../constants/api-endpoints";
2
+ import { normalizeError } from "../errors/normalize";
2
3
  /**
3
4
  * Single source of truth for the Firebase Storage host literal. Every other
4
5
  * appkit + consumer module that needs to detect or allowlist this host
@@ -31,7 +32,8 @@ export function resolveMediaUrl(url) {
31
32
  }
32
33
  return MEDIA_ENDPOINTS.EXT_URL(url);
33
34
  }
34
- catch {
35
+ catch (_err) {
36
+ void normalizeError(_err); // URL constructor throws for non-URL strings (e.g., relative paths) — return original
35
37
  return url;
36
38
  }
37
39
  }
@@ -1,12 +1,14 @@
1
1
  /**
2
2
  * URL Validation Utilities
3
3
  */
4
+ import { normalizeError } from "../errors/normalize";
4
5
  export function isValidUrl(url) {
5
6
  try {
6
7
  new URL(url);
7
8
  return true;
8
9
  }
9
- catch {
10
+ catch (_err) {
11
+ void normalizeError(_err); // URL constructor throws TypeError for invalid URL syntax
10
12
  return false;
11
13
  }
12
14
  }
@@ -17,7 +19,8 @@ export function isValidUrlWithProtocol(url, protocols = ["http", "https"]) {
17
19
  const urlObj = new URL(url);
18
20
  return protocols.includes(urlObj.protocol.replace(":", ""));
19
21
  }
20
- catch {
22
+ catch (_err) {
23
+ void normalizeError(_err); // URL constructor throws TypeError for invalid URL syntax
21
24
  return false;
22
25
  }
23
26
  }
@@ -30,7 +33,8 @@ export function isExternalUrl(url, currentDomain) {
30
33
  (typeof window !== "undefined" ? window.location.hostname : "");
31
34
  return urlObj.hostname !== currentHost;
32
35
  }
33
- catch {
36
+ catch (_err) {
37
+ void normalizeError(_err); // URL constructor throws TypeError for invalid URL syntax
34
38
  return false;
35
39
  }
36
40
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mohasinac/appkit",
3
- "version": "3.2.12",
3
+ "version": "3.2.14",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"