@timardex/cluemart-server-shared 1.0.149 → 1.0.151

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.
@@ -1,4 +1,4 @@
1
- import { f as SchemaCreateBulkNotificationInput, O as ObjectId } from '../Chat-G9w7Bdl9.mjs';
1
+ import { f as SchemaCreateBulkNotificationInput, O as ObjectId } from '../Chat-Ctmtatmi.mjs';
2
2
  import { EnumUserLicence } from '@timardex/cluemart-shared';
3
3
  import 'mongoose';
4
4
  import '@timardex/cluemart-shared/types';
@@ -1,4 +1,4 @@
1
- import { f as SchemaCreateBulkNotificationInput, O as ObjectId } from '../Chat-G9w7Bdl9.js';
1
+ import { f as SchemaCreateBulkNotificationInput, O as ObjectId } from '../Chat-Ctmtatmi.js';
2
2
  import { EnumUserLicence } from '@timardex/cluemart-shared';
3
3
  import 'mongoose';
4
4
  import '@timardex/cluemart-shared/types';
@@ -1,6 +1,8 @@
1
1
  import {
2
2
  AdModel,
3
+ ChatModel,
3
4
  EnumAdStatus,
5
+ EnumChatType,
4
6
  EnumResourceType,
5
7
  EnumUserLicence,
6
8
  EventModel,
@@ -9,7 +11,7 @@ import {
9
11
  PushTokenModel,
10
12
  UserModel,
11
13
  VendorModel
12
- } from "../chunk-XT6SQI4D.mjs";
14
+ } from "../chunk-QVELNUI7.mjs";
13
15
  import "../chunk-3QS3WKRC.mjs";
14
16
 
15
17
  // src/service/database.ts
@@ -204,10 +206,44 @@ async function updateAdStatuses() {
204
206
  );
205
207
  }
206
208
 
209
+ // src/service/vendor.ts
210
+ import mongoose3 from "mongoose";
211
+
207
212
  // src/service/associate.ts
208
- async function removeAssociateFromResource(resourceId, resourceType) {
213
+ import mongoose2 from "mongoose";
214
+ async function removeAssociateFromResource({
215
+ resourceId,
216
+ resourceOwnerId,
217
+ resourceType
218
+ }) {
209
219
  const normalizedResourceId = resourceId.toString();
220
+ const normalizedOwnerId = typeof resourceOwnerId === "string" ? new mongoose2.Types.ObjectId(resourceOwnerId) : resourceOwnerId;
221
+ const session = await mongoose2.startSession();
210
222
  try {
223
+ session.startTransaction();
224
+ const usersWithAssociates = await UserModel.find(
225
+ {
226
+ associates: {
227
+ $elemMatch: {
228
+ resourceId: normalizedResourceId,
229
+ resourceType
230
+ }
231
+ }
232
+ },
233
+ {
234
+ associates: 1
235
+ // Only fetch associates field for efficiency
236
+ }
237
+ ).lean().session(session);
238
+ const associateEmails = usersWithAssociates.flatMap(
239
+ (user) => (user.associates ?? []).filter(
240
+ (assoc) => assoc.resourceId === normalizedResourceId && assoc.resourceType === resourceType
241
+ ).map((assoc) => assoc.email)
242
+ );
243
+ if (associateEmails.length === 0) {
244
+ await session.commitTransaction();
245
+ return;
246
+ }
211
247
  await UserModel.updateMany(
212
248
  {
213
249
  associates: {
@@ -224,34 +260,67 @@ async function removeAssociateFromResource(resourceId, resourceType) {
224
260
  resourceType
225
261
  }
226
262
  }
263
+ },
264
+ { session }
265
+ );
266
+ await ChatModel.updateMany(
267
+ {
268
+ active: true,
269
+ chatType: EnumChatType.RELATION,
270
+ deletedAt: null,
271
+ participants: {
272
+ $elemMatch: {
273
+ userId: normalizedOwnerId
274
+ }
275
+ }
276
+ },
277
+ {
278
+ $set: {
279
+ "participants.$[assoc].active": false
280
+ }
281
+ },
282
+ {
283
+ arrayFilters: [
284
+ {
285
+ "assoc.isAssociate": true,
286
+ "assoc.userEmail": { $in: associateEmails }
287
+ }
288
+ ],
289
+ session
227
290
  }
228
- ).exec();
291
+ );
292
+ await session.commitTransaction();
229
293
  } catch (error) {
294
+ await session.abortTransaction();
230
295
  console.error(
231
- `[removeAssociateFromResource] Failed to remove associates for resourceId=${normalizedResourceId}, resourceType=${resourceType}`,
296
+ `[removeAssociateFromResource] Failed for resourceId=${normalizedResourceId}, resourceType=${resourceType}`,
232
297
  error
233
298
  );
234
299
  throw error;
300
+ } finally {
301
+ session.endSession();
235
302
  }
236
303
  }
237
304
 
238
305
  // src/service/vendor.ts
239
306
  async function updateVendorBasedOnUserLicense(userId, licenceType) {
307
+ const session = await mongoose3.startSession();
240
308
  try {
241
- const user = await UserModel.findById(userId).exec();
242
- if (!user) {
309
+ session.startTransaction();
310
+ const user = await UserModel.findById(userId).select("vendor").lean().session(session);
311
+ if (!user?.vendor) {
312
+ console.warn(`[updateVendor] No vendor for userId=${userId}`);
313
+ await session.abortTransaction();
243
314
  return;
244
315
  }
245
- const userVendor = await VendorModel.findById(user.vendor).exec();
246
- if (!userVendor) {
247
- return;
248
- }
249
- const selectedLicence = licenceType;
250
- if (selectedLicence === void 0) {
316
+ const vendor = await VendorModel.findById(user.vendor).lean().session(session);
317
+ if (!vendor) {
318
+ console.warn(`[updateVendor] Vendor not found for id=${user.vendor}`);
319
+ await session.abortTransaction();
251
320
  return;
252
321
  }
253
322
  const vendorUpdateData = {};
254
- if (selectedLicence === EnumUserLicence.STANDARD_VENDOR) {
323
+ if (licenceType === EnumUserLicence.STANDARD_VENDOR) {
255
324
  vendorUpdateData.associates = [];
256
325
  vendorUpdateData.availability = {
257
326
  corporate: false,
@@ -260,46 +329,50 @@ async function updateVendorBasedOnUserLicense(userId, licenceType) {
260
329
  };
261
330
  vendorUpdateData.products = {
262
331
  active: false,
263
- productsList: userVendor.products?.productsList || []
332
+ productsList: vendor.products?.productsList ?? []
264
333
  };
265
334
  vendorUpdateData.calendar = {
266
335
  active: false,
267
- calendarData: userVendor.calendar?.calendarData || []
336
+ calendarData: vendor.calendar?.calendarData ?? []
268
337
  };
269
- vendorUpdateData.images = (userVendor.images || []).map((img, index) => ({
270
- active: index < 6,
271
- // first 6 will be true, the rest false
272
- source: img.source,
273
- title: img.title
338
+ vendorUpdateData.images = (vendor.images ?? []).map((img, index) => ({
339
+ ...img,
340
+ active: index < 6
274
341
  }));
275
342
  } else {
276
- vendorUpdateData.images = (userVendor.images || []).map((img) => ({
277
- active: true,
278
- // all images will be true
279
- source: img.source,
280
- title: img.title
343
+ vendorUpdateData.images = (vendor.images ?? []).map((img) => ({
344
+ ...img,
345
+ active: true
281
346
  }));
282
347
  }
283
- await VendorModel.findByIdAndUpdate(
284
- userVendor._id,
285
- {
286
- $set: vendorUpdateData
287
- },
288
- { new: true }
348
+ await VendorModel.updateOne(
349
+ { _id: vendor._id },
350
+ { $set: vendorUpdateData },
351
+ { session }
289
352
  );
290
- await removeAssociateFromResource(userVendor._id, EnumResourceType.VENDOR);
353
+ if (licenceType === EnumUserLicence.STANDARD_VENDOR) {
354
+ await removeAssociateFromResource({
355
+ resourceId: vendor._id,
356
+ resourceOwnerId: vendor.owner.userId,
357
+ resourceType: EnumResourceType.VENDOR
358
+ });
359
+ }
360
+ await session.commitTransaction();
291
361
  } catch (error) {
292
- console.error("Error updating vendor based on user license:", error);
362
+ await session.abortTransaction();
363
+ console.error("[updateVendorBasedOnUserLicense] Failed:", error);
364
+ } finally {
365
+ session.endSession();
293
366
  }
294
367
  }
295
368
 
296
369
  // src/service/objectIdToString.ts
297
- import mongoose2 from "mongoose";
370
+ import mongoose4 from "mongoose";
298
371
  function convertObjectIdsToStrings(obj) {
299
372
  if (obj === null || obj === void 0) {
300
373
  return obj;
301
374
  }
302
- if (obj instanceof mongoose2.Types.ObjectId) {
375
+ if (obj instanceof mongoose4.Types.ObjectId) {
303
376
  return obj.toString();
304
377
  }
305
378
  if (Array.isArray(obj)) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/service/database.ts","../../src/service/saveNotificationsInDb.ts","../../src/service/sendPushNotifications.ts","../../src/service/updateAdStatus.ts","../../src/service/associate.ts","../../src/service/vendor.ts","../../src/service/objectIdToString.ts","../../src/service/event.ts"],"sourcesContent":["import mongoose from \"mongoose\";\n\n/**\n * Connect to MongoDB using Mongoose.\n * Supports both local MongoDB (via MONGODB_URI) and MongoDB Atlas (via individual env vars).\n */\nexport const connectToDatabase = async ({\n appName,\n dbName,\n dbPassword,\n dbUser,\n mongodbUri,\n}: {\n appName: string;\n dbName: string;\n dbPassword: string;\n dbUser: string;\n mongodbUri: string;\n}) => {\n try {\n // Check if MONGODB_URI is provided (for local Docker MongoDB)\n const mongoUri = mongodbUri\n ? mongodbUri\n : // Fallback to MongoDB Atlas connection string\n `mongodb+srv://${dbUser}:${dbPassword}@${dbName}.mongodb.net/?retryWrites=true&w=majority&appName=${appName}`;\n\n await mongoose.connect(mongoUri);\n\n const connectionType = mongodbUri ? \"Local MongoDB\" : \"MongoDB Atlas\";\n console.log(\n `${connectionType} connected from server/src/service/database.ts`,\n );\n } catch (err) {\n console.error(\"Error connecting to MongoDB:\", err);\n throw err; // You can throw the error if you want to stop the server in case of connection failure\n }\n};\n","import {\n SchemaCreateBulkNotificationInput,\n NotificationModel,\n} from \"src/mongoose/Notification\";\nimport { ObjectId } from \"src/types\";\n\n/**\n * Create notifications in the database for multiple users\n * This is typically called when sending push notifications\n */\nexport async function saveNotificationsInDb(\n payload: SchemaCreateBulkNotificationInput,\n): Promise<ObjectId[]> {\n const { data, message, title, type, userIds } = payload;\n try {\n const notifications = userIds.map((userId) => ({\n data,\n isRead: false,\n message,\n title,\n type,\n userId,\n }));\n\n // Save notifications to database\n await NotificationModel.insertMany(notifications);\n console.log(\n `Created ${notifications.length} notifications for ${userIds.length} users`,\n );\n\n return [...new Set(userIds)];\n } catch (error) {\n console.error(\"Failed to create notifications:\", error);\n return [];\n //throw new Error(`Failed to create notifications: ${error}`);\n }\n}\n","import { NotificationDataType } from \"@timardex/cluemart-shared\";\nimport { Expo, ExpoPushMessage, ExpoPushTicket } from \"expo-server-sdk\";\n\nimport { SchemaCreateBulkNotificationInput } from \"src/mongoose/Notification\";\nimport { PushTokenModel } from \"src/mongoose/PushToken\";\n\nconst expo = new Expo();\n\n/**\n * Safely extract tokens from ExpoPushMessage handling both string and array cases\n */\nfunction extractTokensFromMessage(message: ExpoPushMessage): string[] {\n return Array.isArray(message.to) ? message.to : [message.to];\n}\n\ninterface CreatePushMessagesOptions {\n tokens: string[];\n message: string;\n title: string;\n data: NotificationDataType;\n}\n\n/**\n * Create push messages from valid tokens\n */\nfunction createPushMessages({\n tokens,\n message,\n title,\n data,\n}: CreatePushMessagesOptions): {\n messages: ExpoPushMessage[];\n invalidTokens: string[];\n} {\n const messages: ExpoPushMessage[] = [];\n const invalidTokens: string[] = [];\n\n for (const token of tokens) {\n if (!Expo.isExpoPushToken(token)) {\n invalidTokens.push(token);\n continue;\n }\n\n messages.push({\n body: message,\n data: { ...data },\n sound: \"default\",\n title,\n to: token,\n });\n }\n\n return { invalidTokens, messages };\n}\n\n/**\n * Process chunk results and extract failed tokens\n */\nfunction processChunkResults(\n tickets: ExpoPushTicket[],\n chunk: ExpoPushMessage[],\n): { successCount: number; failedTokens: string[] } {\n let successCount = 0;\n const failedTokens: string[] = [];\n\n for (const [ticketIndex, ticket] of tickets.entries()) {\n if (ticket.status === \"error\") {\n const message = chunk[ticketIndex];\n if (message) {\n const tokens = extractTokensFromMessage(message);\n if (ticket.details?.error === \"DeviceNotRegistered\") {\n failedTokens.push(...tokens);\n }\n console.log(\"Push notification error\", {\n error: ticket.details?.error,\n tokens,\n });\n }\n } else {\n successCount++;\n }\n }\n\n return { failedTokens, successCount };\n}\n\n/**\n * Send a single chunk of push notifications\n */\nasync function sendChunk(\n chunk: ExpoPushMessage[],\n chunkIndex: number,\n): Promise<{ successCount: number; failedTokens: string[] }> {\n try {\n const tickets = await expo.sendPushNotificationsAsync(chunk);\n const { successCount, failedTokens } = processChunkResults(tickets, chunk);\n\n console.log(\n `Chunk ${chunkIndex + 1}: Sent ${successCount}/${chunk.length} notifications successfully`,\n );\n\n return { failedTokens, successCount };\n } catch (error) {\n console.log(\"Error sending Expo push notification chunk\", {\n chunkIndex,\n chunkSize: chunk.length,\n error: error instanceof Error ? error.message : String(error),\n });\n return { failedTokens: [], successCount: 0 };\n }\n}\n\nexport async function sendPushNotifications({\n data,\n message,\n title,\n userIds,\n}: SchemaCreateBulkNotificationInput) {\n const pushTokens = await PushTokenModel.find({ userId: { $in: userIds } });\n const expoTokens = pushTokens.map((token) => token.token);\n\n if (!data) return;\n\n const { messages, invalidTokens } = createPushMessages({\n data,\n message,\n title,\n tokens: expoTokens,\n });\n\n // Log invalid tokens\n if (invalidTokens.length > 0) {\n console.log(`Found ${invalidTokens.length} invalid push tokens`);\n }\n\n if (messages.length === 0) {\n console.log(\"No valid messages to send after filtering tokens\");\n return;\n }\n\n // Send notifications in chunks\n const chunks = expo.chunkPushNotifications(messages);\n let totalSuccessCount = 0;\n const allFailedTokens: string[] = [];\n\n for (const [chunkIndex, chunk] of chunks.entries()) {\n const { successCount, failedTokens } = await sendChunk(\n chunk,\n chunkIndex + 1,\n );\n totalSuccessCount += successCount;\n allFailedTokens.push(...failedTokens);\n }\n\n // Log final results\n console.log(\n `Sent push notification to ${totalSuccessCount}/${messages.length} tokens across ${chunks.length} chunks`,\n );\n\n if (allFailedTokens.length > 0) {\n console.log(`Found ${allFailedTokens.length} failed push tokens`);\n }\n}\n","import { EnumAdStatus } from \"@timardex/cluemart-shared\";\n\nimport { AdModel } from \"src/mongoose\";\n\n/**\n * Updates ad statuses based on start/end dates and validity\n */\nexport async function updateAdStatuses(): Promise<void> {\n const now = new Date();\n\n // invalid\n const invalidResult = await AdModel.updateMany(\n {\n $or: [\n { start: { $exists: false } },\n { end: { $exists: false } },\n { $expr: { $gt: [\"$start\", \"$end\"] } },\n ],\n status: { $ne: EnumAdStatus.PAUSED },\n },\n { $set: { status: EnumAdStatus.PAUSED } },\n );\n\n // expired\n const expiredResult = await AdModel.updateMany(\n { end: { $lte: now }, status: { $ne: EnumAdStatus.EXPIRED } },\n { $set: { status: EnumAdStatus.EXPIRED } },\n );\n\n // active\n const activeResult = await AdModel.updateMany(\n {\n end: { $gt: now },\n start: { $lte: now },\n status: { $ne: EnumAdStatus.ACTIVE },\n },\n { $set: { status: EnumAdStatus.ACTIVE } },\n );\n\n // paused\n const pausedResult = await AdModel.updateMany(\n { start: { $gt: now }, status: { $ne: EnumAdStatus.PAUSED } },\n { $set: { status: EnumAdStatus.PAUSED } },\n );\n\n console.log(\n `✅ Ad statuses updated: invalid=${invalidResult.modifiedCount}, expired=${expiredResult.modifiedCount}, active=${activeResult.modifiedCount}, paused=${pausedResult.modifiedCount}`,\n );\n}\n","import { EnumResourceType } from \"@timardex/cluemart-shared\";\n\nimport { UserModel } from \"src/mongoose\";\nimport { ObjectId } from \"src/types\";\n\n/**\n * Removes all associate references for a specific resource\n * from every user document.\n *\n * Uses a single atomic update instead of loading + saving each user.\n */\nexport async function removeAssociateFromResource(\n resourceId: string | ObjectId,\n resourceType: EnumResourceType,\n): Promise<void> {\n const normalizedResourceId = resourceId.toString();\n try {\n await UserModel.updateMany(\n {\n associates: {\n $elemMatch: {\n resourceId: normalizedResourceId,\n resourceType,\n },\n },\n },\n {\n $pull: {\n associates: {\n resourceId: normalizedResourceId,\n resourceType,\n },\n },\n },\n ).exec();\n } catch (error) {\n console.error(\n `[removeAssociateFromResource] Failed to remove associates for resourceId=${normalizedResourceId}, resourceType=${resourceType}`,\n error,\n );\n\n throw error;\n }\n}\n","import { EnumResourceType, EnumUserLicence } from \"@timardex/cluemart-shared\";\n\nimport { SchemaVendorType, UserModel, VendorModel } from \"src/mongoose\";\nimport { ObjectId } from \"src/types\";\n\nimport { removeAssociateFromResource } from \"./associate\";\n\n/**\n *\n * This function updates the vendor's features based on the user's license.\n * It checks the type of license and updates the vendor's associates and availability accordingly.\n * If the user has a Pro Vendor license, they can have multiple associates and access to all availability types.\n * If they have a Standard Vendor license, their associates are removed and availability is restricted.\n * @param userId\n * @param licenceType\n * @returns\n */\nexport async function updateVendorBasedOnUserLicense(\n userId: ObjectId,\n licenceType: EnumUserLicence,\n) {\n try {\n const user = await UserModel.findById(userId).exec();\n\n if (!user) {\n return;\n }\n\n const userVendor = await VendorModel.findById(user.vendor).exec();\n\n if (!userVendor) {\n return;\n }\n\n const selectedLicence = licenceType;\n\n // Only update vendor features if selectedLicence is provided\n // This handles the case where selectedLicence might be undefined (e.g., in Stripe webhook cancellation)\n if (selectedLicence === undefined) {\n // If selectedLicence is undefined, we won't change the associates or availability\n return;\n }\n const vendorUpdateData: Partial<SchemaVendorType> = {};\n\n if (selectedLicence === EnumUserLicence.STANDARD_VENDOR) {\n vendorUpdateData.associates = [];\n vendorUpdateData.availability = {\n corporate: false,\n private: false,\n school: false,\n };\n vendorUpdateData.products = {\n active: false,\n productsList: userVendor.products?.productsList || [],\n };\n vendorUpdateData.calendar = {\n active: false,\n calendarData: userVendor.calendar?.calendarData || [],\n };\n vendorUpdateData.images = (userVendor.images || []).map((img, index) => ({\n active: index < 6, // first 6 will be true, the rest false\n source: img.source,\n title: img.title,\n }));\n } else {\n vendorUpdateData.images = (userVendor.images || []).map((img) => ({\n active: true, // all images will be true\n source: img.source,\n title: img.title,\n }));\n }\n\n await VendorModel.findByIdAndUpdate(\n userVendor._id,\n {\n $set: vendorUpdateData,\n },\n { new: true },\n );\n\n await removeAssociateFromResource(userVendor._id, EnumResourceType.VENDOR);\n } catch (error) {\n console.error(\"Error updating vendor based on user license:\", error);\n }\n}\n","import mongoose from \"mongoose\";\n\n/**\n * Recursively converts all ObjectId fields to strings in an object\n * This is needed because GraphQL expects string IDs, not ObjectIds\n */\nexport function convertObjectIdsToStrings(obj: any): any {\n if (obj === null || obj === undefined) {\n return obj;\n }\n\n if (obj instanceof mongoose.Types.ObjectId) {\n return obj.toString();\n }\n\n if (Array.isArray(obj)) {\n return obj.map(convertObjectIdsToStrings);\n }\n\n if (typeof obj === \"object\") {\n const converted: any = {};\n for (const [key, value] of Object.entries(obj)) {\n converted[key] = convertObjectIdsToStrings(value);\n }\n return converted;\n }\n\n return obj;\n}\n","import { EventModel, GoogleImportedMarketModel } from \"src/mongoose\";\nimport { ObjectId } from \"src/types\";\n\nimport { convertObjectIdsToStrings } from \"./objectIdToString\";\n\ntype EventOrMarket = { _id: ObjectId | string; name: string } | null;\n\n/**\n * This function attempts to find an Event or a Google Imported Market by the given resource ID.\n * It first normalizes the resource ID to a string format, then performs parallel queries to both collections.\n * If an Event is found, it returns that; otherwise, it checks for a Google Imported Market and returns it if found.\n * If neither is found, it returns null.\n * @param resourceId - The ID of the resource to find, which can be an ObjectId, string, null, or undefined.\n * @returns A promise that resolves to either an Event or a Google Imported Market object containing _id and name, or null if not found.\n */\n\nexport async function findEventOrImportedMarketById(\n resourceId: ObjectId | string | null | undefined,\n): Promise<EventOrMarket> {\n if (!resourceId) {\n return null;\n }\n\n const normalizedId = convertObjectIdsToStrings(resourceId) as string;\n\n const [eventDoc, googleImportedDoc] = await Promise.all([\n EventModel.findById(normalizedId).select(\"_id name\").lean().exec(),\n GoogleImportedMarketModel.findById(normalizedId)\n .select(\"_id name\")\n .lean()\n .exec(),\n ]);\n\n return eventDoc ?? googleImportedDoc;\n}\n"],"mappings":"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","names":["mongoose"]}
1
+ {"version":3,"sources":["../../src/service/database.ts","../../src/service/saveNotificationsInDb.ts","../../src/service/sendPushNotifications.ts","../../src/service/updateAdStatus.ts","../../src/service/vendor.ts","../../src/service/associate.ts","../../src/service/objectIdToString.ts","../../src/service/event.ts"],"sourcesContent":["import mongoose from \"mongoose\";\n\n/**\n * Connect to MongoDB using Mongoose.\n * Supports both local MongoDB (via MONGODB_URI) and MongoDB Atlas (via individual env vars).\n */\nexport const connectToDatabase = async ({\n appName,\n dbName,\n dbPassword,\n dbUser,\n mongodbUri,\n}: {\n appName: string;\n dbName: string;\n dbPassword: string;\n dbUser: string;\n mongodbUri: string;\n}) => {\n try {\n // Check if MONGODB_URI is provided (for local Docker MongoDB)\n const mongoUri = mongodbUri\n ? mongodbUri\n : // Fallback to MongoDB Atlas connection string\n `mongodb+srv://${dbUser}:${dbPassword}@${dbName}.mongodb.net/?retryWrites=true&w=majority&appName=${appName}`;\n\n await mongoose.connect(mongoUri);\n\n const connectionType = mongodbUri ? \"Local MongoDB\" : \"MongoDB Atlas\";\n console.log(\n `${connectionType} connected from server/src/service/database.ts`,\n );\n } catch (err) {\n console.error(\"Error connecting to MongoDB:\", err);\n throw err; // You can throw the error if you want to stop the server in case of connection failure\n }\n};\n","import {\n SchemaCreateBulkNotificationInput,\n NotificationModel,\n} from \"src/mongoose/Notification\";\nimport { ObjectId } from \"src/types\";\n\n/**\n * Create notifications in the database for multiple users\n * This is typically called when sending push notifications\n */\nexport async function saveNotificationsInDb(\n payload: SchemaCreateBulkNotificationInput,\n): Promise<ObjectId[]> {\n const { data, message, title, type, userIds } = payload;\n try {\n const notifications = userIds.map((userId) => ({\n data,\n isRead: false,\n message,\n title,\n type,\n userId,\n }));\n\n // Save notifications to database\n await NotificationModel.insertMany(notifications);\n console.log(\n `Created ${notifications.length} notifications for ${userIds.length} users`,\n );\n\n return [...new Set(userIds)];\n } catch (error) {\n console.error(\"Failed to create notifications:\", error);\n return [];\n //throw new Error(`Failed to create notifications: ${error}`);\n }\n}\n","import { NotificationDataType } from \"@timardex/cluemart-shared\";\nimport { Expo, ExpoPushMessage, ExpoPushTicket } from \"expo-server-sdk\";\n\nimport { SchemaCreateBulkNotificationInput } from \"src/mongoose/Notification\";\nimport { PushTokenModel } from \"src/mongoose/PushToken\";\n\nconst expo = new Expo();\n\n/**\n * Safely extract tokens from ExpoPushMessage handling both string and array cases\n */\nfunction extractTokensFromMessage(message: ExpoPushMessage): string[] {\n return Array.isArray(message.to) ? message.to : [message.to];\n}\n\ninterface CreatePushMessagesOptions {\n tokens: string[];\n message: string;\n title: string;\n data: NotificationDataType;\n}\n\n/**\n * Create push messages from valid tokens\n */\nfunction createPushMessages({\n tokens,\n message,\n title,\n data,\n}: CreatePushMessagesOptions): {\n messages: ExpoPushMessage[];\n invalidTokens: string[];\n} {\n const messages: ExpoPushMessage[] = [];\n const invalidTokens: string[] = [];\n\n for (const token of tokens) {\n if (!Expo.isExpoPushToken(token)) {\n invalidTokens.push(token);\n continue;\n }\n\n messages.push({\n body: message,\n data: { ...data },\n sound: \"default\",\n title,\n to: token,\n });\n }\n\n return { invalidTokens, messages };\n}\n\n/**\n * Process chunk results and extract failed tokens\n */\nfunction processChunkResults(\n tickets: ExpoPushTicket[],\n chunk: ExpoPushMessage[],\n): { successCount: number; failedTokens: string[] } {\n let successCount = 0;\n const failedTokens: string[] = [];\n\n for (const [ticketIndex, ticket] of tickets.entries()) {\n if (ticket.status === \"error\") {\n const message = chunk[ticketIndex];\n if (message) {\n const tokens = extractTokensFromMessage(message);\n if (ticket.details?.error === \"DeviceNotRegistered\") {\n failedTokens.push(...tokens);\n }\n console.log(\"Push notification error\", {\n error: ticket.details?.error,\n tokens,\n });\n }\n } else {\n successCount++;\n }\n }\n\n return { failedTokens, successCount };\n}\n\n/**\n * Send a single chunk of push notifications\n */\nasync function sendChunk(\n chunk: ExpoPushMessage[],\n chunkIndex: number,\n): Promise<{ successCount: number; failedTokens: string[] }> {\n try {\n const tickets = await expo.sendPushNotificationsAsync(chunk);\n const { successCount, failedTokens } = processChunkResults(tickets, chunk);\n\n console.log(\n `Chunk ${chunkIndex + 1}: Sent ${successCount}/${chunk.length} notifications successfully`,\n );\n\n return { failedTokens, successCount };\n } catch (error) {\n console.log(\"Error sending Expo push notification chunk\", {\n chunkIndex,\n chunkSize: chunk.length,\n error: error instanceof Error ? error.message : String(error),\n });\n return { failedTokens: [], successCount: 0 };\n }\n}\n\nexport async function sendPushNotifications({\n data,\n message,\n title,\n userIds,\n}: SchemaCreateBulkNotificationInput) {\n const pushTokens = await PushTokenModel.find({ userId: { $in: userIds } });\n const expoTokens = pushTokens.map((token) => token.token);\n\n if (!data) return;\n\n const { messages, invalidTokens } = createPushMessages({\n data,\n message,\n title,\n tokens: expoTokens,\n });\n\n // Log invalid tokens\n if (invalidTokens.length > 0) {\n console.log(`Found ${invalidTokens.length} invalid push tokens`);\n }\n\n if (messages.length === 0) {\n console.log(\"No valid messages to send after filtering tokens\");\n return;\n }\n\n // Send notifications in chunks\n const chunks = expo.chunkPushNotifications(messages);\n let totalSuccessCount = 0;\n const allFailedTokens: string[] = [];\n\n for (const [chunkIndex, chunk] of chunks.entries()) {\n const { successCount, failedTokens } = await sendChunk(\n chunk,\n chunkIndex + 1,\n );\n totalSuccessCount += successCount;\n allFailedTokens.push(...failedTokens);\n }\n\n // Log final results\n console.log(\n `Sent push notification to ${totalSuccessCount}/${messages.length} tokens across ${chunks.length} chunks`,\n );\n\n if (allFailedTokens.length > 0) {\n console.log(`Found ${allFailedTokens.length} failed push tokens`);\n }\n}\n","import { EnumAdStatus } from \"@timardex/cluemart-shared\";\n\nimport { AdModel } from \"src/mongoose\";\n\n/**\n * Updates ad statuses based on start/end dates and validity\n */\nexport async function updateAdStatuses(): Promise<void> {\n const now = new Date();\n\n // invalid\n const invalidResult = await AdModel.updateMany(\n {\n $or: [\n { start: { $exists: false } },\n { end: { $exists: false } },\n { $expr: { $gt: [\"$start\", \"$end\"] } },\n ],\n status: { $ne: EnumAdStatus.PAUSED },\n },\n { $set: { status: EnumAdStatus.PAUSED } },\n );\n\n // expired\n const expiredResult = await AdModel.updateMany(\n { end: { $lte: now }, status: { $ne: EnumAdStatus.EXPIRED } },\n { $set: { status: EnumAdStatus.EXPIRED } },\n );\n\n // active\n const activeResult = await AdModel.updateMany(\n {\n end: { $gt: now },\n start: { $lte: now },\n status: { $ne: EnumAdStatus.ACTIVE },\n },\n { $set: { status: EnumAdStatus.ACTIVE } },\n );\n\n // paused\n const pausedResult = await AdModel.updateMany(\n { start: { $gt: now }, status: { $ne: EnumAdStatus.PAUSED } },\n { $set: { status: EnumAdStatus.PAUSED } },\n );\n\n console.log(\n `✅ Ad statuses updated: invalid=${invalidResult.modifiedCount}, expired=${expiredResult.modifiedCount}, active=${activeResult.modifiedCount}, paused=${pausedResult.modifiedCount}`,\n );\n}\n","import { EnumResourceType, EnumUserLicence } from \"@timardex/cluemart-shared\";\nimport mongoose from \"mongoose\";\n\nimport { SchemaVendorType, UserModel, VendorModel } from \"src/mongoose\";\nimport { ObjectId } from \"src/types\";\n\nimport { removeAssociateFromResource } from \"./associate\";\n\n/**\n *\n * This function updates the vendor's features based on the user's license.\n * It checks the type of license and updates the vendor's associates and availability accordingly.\n * If the user has a Pro Vendor license, they can have multiple associates and access to all availability types.\n * If they have a Standard Vendor license, their associates are removed and availability is restricted.\n * @param userId\n * @param licenceType\n * @returns\n */\nexport async function updateVendorBasedOnUserLicense(\n userId: ObjectId,\n licenceType: EnumUserLicence,\n): Promise<void> {\n const session = await mongoose.startSession();\n\n try {\n session.startTransaction();\n\n /**\n * Fetch vendor (only required fields)\n */\n const user = await UserModel.findById(userId)\n .select(\"vendor\")\n .lean()\n .session(session);\n\n if (!user?.vendor) {\n console.warn(`[updateVendor] No vendor for userId=${userId}`);\n await session.abortTransaction();\n return;\n }\n\n const vendor = await VendorModel.findById(user.vendor)\n .lean()\n .session(session);\n\n if (!vendor) {\n console.warn(`[updateVendor] Vendor not found for id=${user.vendor}`);\n await session.abortTransaction();\n return;\n }\n\n /**\n * Build update payload\n */\n\n const vendorUpdateData: Partial<SchemaVendorType> = {};\n\n if (licenceType === EnumUserLicence.STANDARD_VENDOR) {\n vendorUpdateData.associates = [];\n\n vendorUpdateData.availability = {\n corporate: false,\n private: false,\n school: false,\n };\n\n vendorUpdateData.products = {\n active: false,\n productsList: vendor.products?.productsList ?? [],\n };\n\n vendorUpdateData.calendar = {\n active: false,\n calendarData: vendor.calendar?.calendarData ?? [],\n };\n\n vendorUpdateData.images = (vendor.images ?? []).map((img, index) => ({\n ...img,\n active: index < 6,\n }));\n } else {\n vendorUpdateData.images = (vendor.images ?? []).map((img) => ({\n ...img,\n active: true,\n }));\n }\n\n /**\n * Update vendor\n */\n await VendorModel.updateOne(\n { _id: vendor._id },\n { $set: vendorUpdateData },\n { session },\n );\n\n /**\n * Only remove associates for STANDARD licence\n */\n if (licenceType === EnumUserLicence.STANDARD_VENDOR) {\n await removeAssociateFromResource({\n resourceId: vendor._id,\n resourceOwnerId: vendor.owner.userId,\n resourceType: EnumResourceType.VENDOR,\n });\n }\n\n await session.commitTransaction();\n } catch (error) {\n await session.abortTransaction();\n console.error(\"[updateVendorBasedOnUserLicense] Failed:\", error);\n } finally {\n session.endSession();\n }\n}\n","import { EnumChatType, EnumResourceType } from \"@timardex/cluemart-shared\";\nimport mongoose from \"mongoose\";\n\nimport { ChatModel, UserModel } from \"src/mongoose\";\nimport { ObjectId } from \"src/types\";\n\ninterface RemoveAssociateFromResourceParams {\n resourceId: string | ObjectId;\n resourceOwnerId: string | ObjectId;\n resourceType: EnumResourceType;\n}\n\n/**\n * Removes all associates linked to a specific resource\n * and deactivates corresponding chat participants.\n *\n * Flow:\n * 1. Find all associates (emails) BEFORE removing them\n * 2. Remove associates from User documents\n * 3. Deactivate matching participants in chats\n *\n * Uses a MongoDB transaction to ensure consistency.\n */\nexport async function removeAssociateFromResource({\n resourceId,\n resourceOwnerId,\n resourceType,\n}: RemoveAssociateFromResourceParams): Promise<void> {\n const normalizedResourceId = resourceId.toString();\n const normalizedOwnerId =\n typeof resourceOwnerId === \"string\"\n ? new mongoose.Types.ObjectId(resourceOwnerId)\n : resourceOwnerId;\n\n const session = await mongoose.startSession();\n\n try {\n session.startTransaction();\n\n /**\n * STEP 1: Fetch all users that have matching associates\n * We do this BEFORE removal because MongoDB does not return\n * removed elements from `$pull`\n */\n const usersWithAssociates = await UserModel.find(\n {\n associates: {\n $elemMatch: {\n resourceId: normalizedResourceId,\n resourceType,\n },\n },\n },\n {\n associates: 1, // Only fetch associates field for efficiency\n },\n )\n .lean()\n .session(session);\n\n /**\n * Extract emails of associates that match the resource\n */\n const associateEmails = usersWithAssociates.flatMap((user) =>\n (user.associates ?? [])\n .filter(\n (assoc) =>\n assoc.resourceId === normalizedResourceId &&\n assoc.resourceType === resourceType,\n )\n .map((assoc) => assoc.email),\n );\n\n /**\n * Early exit: no associates found → nothing to update\n */\n if (associateEmails.length === 0) {\n await session.commitTransaction();\n return;\n }\n\n /**\n * STEP 2: Remove associates from all users\n */\n await UserModel.updateMany(\n {\n associates: {\n $elemMatch: {\n resourceId: normalizedResourceId,\n resourceType,\n },\n },\n },\n {\n $pull: {\n associates: {\n resourceId: normalizedResourceId,\n resourceType,\n },\n },\n },\n { session },\n );\n\n /**\n * STEP 3: Deactivate matching participants in chats\n *\n * Conditions:\n * - Only RELATION chats\n * - Chat must be active and not deleted\n * - Must include the resource owner as a participant\n * - Only deactivate participants:\n * - with matching email\n * - where isAssociate === true\n */\n await ChatModel.updateMany(\n {\n active: true,\n chatType: EnumChatType.RELATION,\n deletedAt: null,\n participants: {\n $elemMatch: {\n userId: normalizedOwnerId,\n },\n },\n },\n {\n $set: {\n \"participants.$[assoc].active\": false,\n },\n },\n {\n arrayFilters: [\n {\n \"assoc.isAssociate\": true,\n \"assoc.userEmail\": { $in: associateEmails },\n },\n ],\n session,\n },\n );\n\n /**\n * Commit transaction if everything succeeded\n */\n await session.commitTransaction();\n } catch (error) {\n /**\n * Rollback all changes if anything fails\n */\n await session.abortTransaction();\n\n console.error(\n `[removeAssociateFromResource] Failed for resourceId=${normalizedResourceId}, resourceType=${resourceType}`,\n error,\n );\n\n throw error; // Re-throw so caller can handle it\n } finally {\n /**\n * Always end the session\n */\n session.endSession();\n }\n}\n","import mongoose from \"mongoose\";\n\n/**\n * Recursively converts all ObjectId fields to strings in an object\n * This is needed because GraphQL expects string IDs, not ObjectIds\n */\nexport function convertObjectIdsToStrings(obj: any): any {\n if (obj === null || obj === undefined) {\n return obj;\n }\n\n if (obj instanceof mongoose.Types.ObjectId) {\n return obj.toString();\n }\n\n if (Array.isArray(obj)) {\n return obj.map(convertObjectIdsToStrings);\n }\n\n if (typeof obj === \"object\") {\n const converted: any = {};\n for (const [key, value] of Object.entries(obj)) {\n converted[key] = convertObjectIdsToStrings(value);\n }\n return converted;\n }\n\n return obj;\n}\n","import { EventModel, GoogleImportedMarketModel } from \"src/mongoose\";\nimport { ObjectId } from \"src/types\";\n\nimport { convertObjectIdsToStrings } from \"./objectIdToString\";\n\ntype EventOrMarket = { _id: ObjectId | string; name: string } | null;\n\n/**\n * This function attempts to find an Event or a Google Imported Market by the given resource ID.\n * It first normalizes the resource ID to a string format, then performs parallel queries to both collections.\n * If an Event is found, it returns that; otherwise, it checks for a Google Imported Market and returns it if found.\n * If neither is found, it returns null.\n * @param resourceId - The ID of the resource to find, which can be an ObjectId, string, null, or undefined.\n * @returns A promise that resolves to either an Event or a Google Imported Market object containing _id and name, or null if not found.\n */\n\nexport async function findEventOrImportedMarketById(\n resourceId: ObjectId | string | null | undefined,\n): Promise<EventOrMarket> {\n if (!resourceId) {\n return null;\n }\n\n const normalizedId = convertObjectIdsToStrings(resourceId) as string;\n\n const [eventDoc, googleImportedDoc] = await Promise.all([\n EventModel.findById(normalizedId).select(\"_id name\").lean().exec(),\n GoogleImportedMarketModel.findById(normalizedId)\n .select(\"_id name\")\n .lean()\n .exec(),\n ]);\n\n return eventDoc ?? googleImportedDoc;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAAA,OAAO,cAAc;AAMd,IAAM,oBAAoB,OAAO;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAMM;AACJ,MAAI;AAEF,UAAM,WAAW,aACb;AAAA;AAAA,MAEA,iBAAiB,MAAM,IAAI,UAAU,IAAI,MAAM,qDAAqD,OAAO;AAAA;AAE/G,UAAM,SAAS,QAAQ,QAAQ;AAE/B,UAAM,iBAAiB,aAAa,kBAAkB;AACtD,YAAQ;AAAA,MACN,GAAG,cAAc;AAAA,IACnB;AAAA,EACF,SAAS,KAAK;AACZ,YAAQ,MAAM,gCAAgC,GAAG;AACjD,UAAM;AAAA,EACR;AACF;;;AC1BA,eAAsB,sBACpB,SACqB;AACrB,QAAM,EAAE,MAAM,SAAS,OAAO,MAAM,QAAQ,IAAI;AAChD,MAAI;AACF,UAAM,gBAAgB,QAAQ,IAAI,CAAC,YAAY;AAAA,MAC7C;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE;AAGF,UAAM,kBAAkB,WAAW,aAAa;AAChD,YAAQ;AAAA,MACN,WAAW,cAAc,MAAM,sBAAsB,QAAQ,MAAM;AAAA,IACrE;AAEA,WAAO,CAAC,GAAG,IAAI,IAAI,OAAO,CAAC;AAAA,EAC7B,SAAS,OAAO;AACd,YAAQ,MAAM,mCAAmC,KAAK;AACtD,WAAO,CAAC;AAAA,EAEV;AACF;;;ACnCA,SAAS,YAA6C;AAKtD,IAAM,OAAO,IAAI,KAAK;AAKtB,SAAS,yBAAyB,SAAoC;AACpE,SAAO,MAAM,QAAQ,QAAQ,EAAE,IAAI,QAAQ,KAAK,CAAC,QAAQ,EAAE;AAC7D;AAYA,SAAS,mBAAmB;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAGE;AACA,QAAM,WAA8B,CAAC;AACrC,QAAM,gBAA0B,CAAC;AAEjC,aAAW,SAAS,QAAQ;AAC1B,QAAI,CAAC,KAAK,gBAAgB,KAAK,GAAG;AAChC,oBAAc,KAAK,KAAK;AACxB;AAAA,IACF;AAEA,aAAS,KAAK;AAAA,MACZ,MAAM;AAAA,MACN,MAAM,EAAE,GAAG,KAAK;AAAA,MAChB,OAAO;AAAA,MACP;AAAA,MACA,IAAI;AAAA,IACN,CAAC;AAAA,EACH;AAEA,SAAO,EAAE,eAAe,SAAS;AACnC;AAKA,SAAS,oBACP,SACA,OACkD;AAClD,MAAI,eAAe;AACnB,QAAM,eAAyB,CAAC;AAEhC,aAAW,CAAC,aAAa,MAAM,KAAK,QAAQ,QAAQ,GAAG;AACrD,QAAI,OAAO,WAAW,SAAS;AAC7B,YAAM,UAAU,MAAM,WAAW;AACjC,UAAI,SAAS;AACX,cAAM,SAAS,yBAAyB,OAAO;AAC/C,YAAI,OAAO,SAAS,UAAU,uBAAuB;AACnD,uBAAa,KAAK,GAAG,MAAM;AAAA,QAC7B;AACA,gBAAQ,IAAI,2BAA2B;AAAA,UACrC,OAAO,OAAO,SAAS;AAAA,UACvB;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,OAAO;AACL;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,cAAc,aAAa;AACtC;AAKA,eAAe,UACb,OACA,YAC2D;AAC3D,MAAI;AACF,UAAM,UAAU,MAAM,KAAK,2BAA2B,KAAK;AAC3D,UAAM,EAAE,cAAc,aAAa,IAAI,oBAAoB,SAAS,KAAK;AAEzE,YAAQ;AAAA,MACN,SAAS,aAAa,CAAC,UAAU,YAAY,IAAI,MAAM,MAAM;AAAA,IAC/D;AAEA,WAAO,EAAE,cAAc,aAAa;AAAA,EACtC,SAAS,OAAO;AACd,YAAQ,IAAI,8CAA8C;AAAA,MACxD;AAAA,MACA,WAAW,MAAM;AAAA,MACjB,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,IAC9D,CAAC;AACD,WAAO,EAAE,cAAc,CAAC,GAAG,cAAc,EAAE;AAAA,EAC7C;AACF;AAEA,eAAsB,sBAAsB;AAAA,EAC1C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAsC;AACpC,QAAM,aAAa,MAAM,eAAe,KAAK,EAAE,QAAQ,EAAE,KAAK,QAAQ,EAAE,CAAC;AACzE,QAAM,aAAa,WAAW,IAAI,CAAC,UAAU,MAAM,KAAK;AAExD,MAAI,CAAC,KAAM;AAEX,QAAM,EAAE,UAAU,cAAc,IAAI,mBAAmB;AAAA,IACrD;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,EACV,CAAC;AAGD,MAAI,cAAc,SAAS,GAAG;AAC5B,YAAQ,IAAI,SAAS,cAAc,MAAM,sBAAsB;AAAA,EACjE;AAEA,MAAI,SAAS,WAAW,GAAG;AACzB,YAAQ,IAAI,kDAAkD;AAC9D;AAAA,EACF;AAGA,QAAM,SAAS,KAAK,uBAAuB,QAAQ;AACnD,MAAI,oBAAoB;AACxB,QAAM,kBAA4B,CAAC;AAEnC,aAAW,CAAC,YAAY,KAAK,KAAK,OAAO,QAAQ,GAAG;AAClD,UAAM,EAAE,cAAc,aAAa,IAAI,MAAM;AAAA,MAC3C;AAAA,MACA,aAAa;AAAA,IACf;AACA,yBAAqB;AACrB,oBAAgB,KAAK,GAAG,YAAY;AAAA,EACtC;AAGA,UAAQ;AAAA,IACN,6BAA6B,iBAAiB,IAAI,SAAS,MAAM,kBAAkB,OAAO,MAAM;AAAA,EAClG;AAEA,MAAI,gBAAgB,SAAS,GAAG;AAC9B,YAAQ,IAAI,SAAS,gBAAgB,MAAM,qBAAqB;AAAA,EAClE;AACF;;;AC3JA,eAAsB,mBAAkC;AACtD,QAAM,MAAM,oBAAI,KAAK;AAGrB,QAAM,gBAAgB,MAAM,QAAQ;AAAA,IAClC;AAAA,MACE,KAAK;AAAA,QACH,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE;AAAA,QAC5B,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE;AAAA,QAC1B,EAAE,OAAO,EAAE,KAAK,CAAC,UAAU,MAAM,EAAE,EAAE;AAAA,MACvC;AAAA,MACA,QAAQ,EAAE,KAAK,aAAa,OAAO;AAAA,IACrC;AAAA,IACA,EAAE,MAAM,EAAE,QAAQ,aAAa,OAAO,EAAE;AAAA,EAC1C;AAGA,QAAM,gBAAgB,MAAM,QAAQ;AAAA,IAClC,EAAE,KAAK,EAAE,MAAM,IAAI,GAAG,QAAQ,EAAE,KAAK,aAAa,QAAQ,EAAE;AAAA,IAC5D,EAAE,MAAM,EAAE,QAAQ,aAAa,QAAQ,EAAE;AAAA,EAC3C;AAGA,QAAM,eAAe,MAAM,QAAQ;AAAA,IACjC;AAAA,MACE,KAAK,EAAE,KAAK,IAAI;AAAA,MAChB,OAAO,EAAE,MAAM,IAAI;AAAA,MACnB,QAAQ,EAAE,KAAK,aAAa,OAAO;AAAA,IACrC;AAAA,IACA,EAAE,MAAM,EAAE,QAAQ,aAAa,OAAO,EAAE;AAAA,EAC1C;AAGA,QAAM,eAAe,MAAM,QAAQ;AAAA,IACjC,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,QAAQ,EAAE,KAAK,aAAa,OAAO,EAAE;AAAA,IAC5D,EAAE,MAAM,EAAE,QAAQ,aAAa,OAAO,EAAE;AAAA,EAC1C;AAEA,UAAQ;AAAA,IACN,uCAAkC,cAAc,aAAa,aAAa,cAAc,aAAa,YAAY,aAAa,aAAa,YAAY,aAAa,aAAa;AAAA,EACnL;AACF;;;AC/CA,OAAOA,eAAc;;;ACArB,OAAOC,eAAc;AAsBrB,eAAsB,4BAA4B;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AACF,GAAqD;AACnD,QAAM,uBAAuB,WAAW,SAAS;AACjD,QAAM,oBACJ,OAAO,oBAAoB,WACvB,IAAIC,UAAS,MAAM,SAAS,eAAe,IAC3C;AAEN,QAAM,UAAU,MAAMA,UAAS,aAAa;AAE5C,MAAI;AACF,YAAQ,iBAAiB;AAOzB,UAAM,sBAAsB,MAAM,UAAU;AAAA,MAC1C;AAAA,QACE,YAAY;AAAA,UACV,YAAY;AAAA,YACV,YAAY;AAAA,YACZ;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,MACA;AAAA,QACE,YAAY;AAAA;AAAA,MACd;AAAA,IACF,EACG,KAAK,EACL,QAAQ,OAAO;AAKlB,UAAM,kBAAkB,oBAAoB;AAAA,MAAQ,CAAC,UAClD,KAAK,cAAc,CAAC,GAClB;AAAA,QACC,CAAC,UACC,MAAM,eAAe,wBACrB,MAAM,iBAAiB;AAAA,MAC3B,EACC,IAAI,CAAC,UAAU,MAAM,KAAK;AAAA,IAC/B;AAKA,QAAI,gBAAgB,WAAW,GAAG;AAChC,YAAM,QAAQ,kBAAkB;AAChC;AAAA,IACF;AAKA,UAAM,UAAU;AAAA,MACd;AAAA,QACE,YAAY;AAAA,UACV,YAAY;AAAA,YACV,YAAY;AAAA,YACZ;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,MACA;AAAA,QACE,OAAO;AAAA,UACL,YAAY;AAAA,YACV,YAAY;AAAA,YACZ;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,MACA,EAAE,QAAQ;AAAA,IACZ;AAaA,UAAM,UAAU;AAAA,MACd;AAAA,QACE,QAAQ;AAAA,QACR,UAAU,aAAa;AAAA,QACvB,WAAW;AAAA,QACX,cAAc;AAAA,UACZ,YAAY;AAAA,YACV,QAAQ;AAAA,UACV;AAAA,QACF;AAAA,MACF;AAAA,MACA;AAAA,QACE,MAAM;AAAA,UACJ,gCAAgC;AAAA,QAClC;AAAA,MACF;AAAA,MACA;AAAA,QACE,cAAc;AAAA,UACZ;AAAA,YACE,qBAAqB;AAAA,YACrB,mBAAmB,EAAE,KAAK,gBAAgB;AAAA,UAC5C;AAAA,QACF;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAKA,UAAM,QAAQ,kBAAkB;AAAA,EAClC,SAAS,OAAO;AAId,UAAM,QAAQ,iBAAiB;AAE/B,YAAQ;AAAA,MACN,uDAAuD,oBAAoB,kBAAkB,YAAY;AAAA,MACzG;AAAA,IACF;AAEA,UAAM;AAAA,EACR,UAAE;AAIA,YAAQ,WAAW;AAAA,EACrB;AACF;;;ADlJA,eAAsB,+BACpB,QACA,aACe;AACf,QAAM,UAAU,MAAMC,UAAS,aAAa;AAE5C,MAAI;AACF,YAAQ,iBAAiB;AAKzB,UAAM,OAAO,MAAM,UAAU,SAAS,MAAM,EACzC,OAAO,QAAQ,EACf,KAAK,EACL,QAAQ,OAAO;AAElB,QAAI,CAAC,MAAM,QAAQ;AACjB,cAAQ,KAAK,uCAAuC,MAAM,EAAE;AAC5D,YAAM,QAAQ,iBAAiB;AAC/B;AAAA,IACF;AAEA,UAAM,SAAS,MAAM,YAAY,SAAS,KAAK,MAAM,EAClD,KAAK,EACL,QAAQ,OAAO;AAElB,QAAI,CAAC,QAAQ;AACX,cAAQ,KAAK,0CAA0C,KAAK,MAAM,EAAE;AACpE,YAAM,QAAQ,iBAAiB;AAC/B;AAAA,IACF;AAMA,UAAM,mBAA8C,CAAC;AAErD,QAAI,gBAAgB,gBAAgB,iBAAiB;AACnD,uBAAiB,aAAa,CAAC;AAE/B,uBAAiB,eAAe;AAAA,QAC9B,WAAW;AAAA,QACX,SAAS;AAAA,QACT,QAAQ;AAAA,MACV;AAEA,uBAAiB,WAAW;AAAA,QAC1B,QAAQ;AAAA,QACR,cAAc,OAAO,UAAU,gBAAgB,CAAC;AAAA,MAClD;AAEA,uBAAiB,WAAW;AAAA,QAC1B,QAAQ;AAAA,QACR,cAAc,OAAO,UAAU,gBAAgB,CAAC;AAAA,MAClD;AAEA,uBAAiB,UAAU,OAAO,UAAU,CAAC,GAAG,IAAI,CAAC,KAAK,WAAW;AAAA,QACnE,GAAG;AAAA,QACH,QAAQ,QAAQ;AAAA,MAClB,EAAE;AAAA,IACJ,OAAO;AACL,uBAAiB,UAAU,OAAO,UAAU,CAAC,GAAG,IAAI,CAAC,SAAS;AAAA,QAC5D,GAAG;AAAA,QACH,QAAQ;AAAA,MACV,EAAE;AAAA,IACJ;AAKA,UAAM,YAAY;AAAA,MAChB,EAAE,KAAK,OAAO,IAAI;AAAA,MAClB,EAAE,MAAM,iBAAiB;AAAA,MACzB,EAAE,QAAQ;AAAA,IACZ;AAKA,QAAI,gBAAgB,gBAAgB,iBAAiB;AACnD,YAAM,4BAA4B;AAAA,QAChC,YAAY,OAAO;AAAA,QACnB,iBAAiB,OAAO,MAAM;AAAA,QAC9B,cAAc,iBAAiB;AAAA,MACjC,CAAC;AAAA,IACH;AAEA,UAAM,QAAQ,kBAAkB;AAAA,EAClC,SAAS,OAAO;AACd,UAAM,QAAQ,iBAAiB;AAC/B,YAAQ,MAAM,4CAA4C,KAAK;AAAA,EACjE,UAAE;AACA,YAAQ,WAAW;AAAA,EACrB;AACF;;;AElHA,OAAOC,eAAc;AAMd,SAAS,0BAA0B,KAAe;AACvD,MAAI,QAAQ,QAAQ,QAAQ,QAAW;AACrC,WAAO;AAAA,EACT;AAEA,MAAI,eAAeA,UAAS,MAAM,UAAU;AAC1C,WAAO,IAAI,SAAS;AAAA,EACtB;AAEA,MAAI,MAAM,QAAQ,GAAG,GAAG;AACtB,WAAO,IAAI,IAAI,yBAAyB;AAAA,EAC1C;AAEA,MAAI,OAAO,QAAQ,UAAU;AAC3B,UAAM,YAAiB,CAAC;AACxB,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,GAAG,GAAG;AAC9C,gBAAU,GAAG,IAAI,0BAA0B,KAAK;AAAA,IAClD;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;;;ACZA,eAAsB,8BACpB,YACwB;AACxB,MAAI,CAAC,YAAY;AACf,WAAO;AAAA,EACT;AAEA,QAAM,eAAe,0BAA0B,UAAU;AAEzD,QAAM,CAAC,UAAU,iBAAiB,IAAI,MAAM,QAAQ,IAAI;AAAA,IACtD,WAAW,SAAS,YAAY,EAAE,OAAO,UAAU,EAAE,KAAK,EAAE,KAAK;AAAA,IACjE,0BAA0B,SAAS,YAAY,EAC5C,OAAO,UAAU,EACjB,KAAK,EACL,KAAK;AAAA,EACV,CAAC;AAED,SAAO,YAAY;AACrB;","names":["mongoose","mongoose","mongoose","mongoose","mongoose"]}
@@ -2,4 +2,4 @@ import '@timardex/cluemart-shared';
2
2
  import '@timardex/cluemart-shared/types';
3
3
  export { default as express } from 'express';
4
4
  export { default as mongoose } from 'mongoose';
5
- export { A as AuthUser, E as EnumPubSubEvents, G as GraphQLContext, O as ObjectId, h as SubscriptionPayload } from '../Chat-G9w7Bdl9.mjs';
5
+ export { A as AuthUser, E as EnumPubSubEvents, G as GraphQLContext, O as ObjectId, h as SubscriptionPayload } from '../Chat-Ctmtatmi.mjs';
@@ -2,4 +2,4 @@ import '@timardex/cluemart-shared';
2
2
  import '@timardex/cluemart-shared/types';
3
3
  export { default as express } from 'express';
4
4
  export { default as mongoose } from 'mongoose';
5
- export { A as AuthUser, E as EnumPubSubEvents, G as GraphQLContext, O as ObjectId, h as SubscriptionPayload } from '../Chat-G9w7Bdl9.js';
5
+ export { A as AuthUser, E as EnumPubSubEvents, G as GraphQLContext, O as ObjectId, h as SubscriptionPayload } from '../Chat-Ctmtatmi.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timardex/cluemart-server-shared",
3
- "version": "1.0.149",
3
+ "version": "1.0.151",
4
4
  "description": "",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",
@@ -41,7 +41,7 @@
41
41
  "author": "Csaba Timár",
42
42
  "license": "ISC",
43
43
  "devDependencies": {
44
- "@timardex/cluemart-shared": "^1.5.552",
44
+ "@timardex/cluemart-shared": "^1.5.556",
45
45
  "@types/express": "^5.0.5",
46
46
  "@types/node": "^22.13.4",
47
47
  "@typescript-eslint/eslint-plugin": "^8.20.0",