nexus-backend 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Request, Response, NextFunction } from 'express';
2
2
  import NodeCache from 'node-cache';
3
+ import { Resend } from 'resend';
3
4
 
4
5
  interface SuccessResponse<T = any> {
5
6
  success: true;
@@ -82,4 +83,30 @@ declare class Cache extends NodeCache {
82
83
 
83
84
  declare const cacheMemory: Cache;
84
85
 
85
- export { ApiError, BadRequestError, Cache, type ErrorResponse, ForbiddenError, type MongoConfig, NotFoundError, type SuccessResponse, UnauthorizedError, ValidationError, cacheMemory, connectMongoDb, errorHandler as errorMiddleware, errorResponse, requiredEnv, successResponse };
86
+ /**
87
+ * Generates a random numeric string of given length.
88
+ * Uses crypto for better randomness (production safe).
89
+ *
90
+ * @param digit - Length of the random number
91
+ * @returns string
92
+ */
93
+ declare function random(digit: number): string;
94
+
95
+ interface MailerConfig {
96
+ brandName: string;
97
+ smtpProvider: Resend;
98
+ fromEmail: string;
99
+ }
100
+ interface EmailOptions {
101
+ to: string;
102
+ subject: string;
103
+ body: string;
104
+ }
105
+
106
+ declare class Mailer {
107
+ private config;
108
+ constructor(mailerConfig: MailerConfig);
109
+ sendMail(options: EmailOptions): Promise<boolean>;
110
+ }
111
+
112
+ export { ApiError, BadRequestError, Cache, type EmailOptions, type ErrorResponse, ForbiddenError, Mailer, type MailerConfig, type MongoConfig, NotFoundError, type SuccessResponse, UnauthorizedError, ValidationError, cacheMemory, connectMongoDb, errorHandler as errorMiddleware, errorResponse, random, requiredEnv, successResponse };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Request, Response, NextFunction } from 'express';
2
2
  import NodeCache from 'node-cache';
3
+ import { Resend } from 'resend';
3
4
 
4
5
  interface SuccessResponse<T = any> {
5
6
  success: true;
@@ -82,4 +83,30 @@ declare class Cache extends NodeCache {
82
83
 
83
84
  declare const cacheMemory: Cache;
84
85
 
85
- export { ApiError, BadRequestError, Cache, type ErrorResponse, ForbiddenError, type MongoConfig, NotFoundError, type SuccessResponse, UnauthorizedError, ValidationError, cacheMemory, connectMongoDb, errorHandler as errorMiddleware, errorResponse, requiredEnv, successResponse };
86
+ /**
87
+ * Generates a random numeric string of given length.
88
+ * Uses crypto for better randomness (production safe).
89
+ *
90
+ * @param digit - Length of the random number
91
+ * @returns string
92
+ */
93
+ declare function random(digit: number): string;
94
+
95
+ interface MailerConfig {
96
+ brandName: string;
97
+ smtpProvider: Resend;
98
+ fromEmail: string;
99
+ }
100
+ interface EmailOptions {
101
+ to: string;
102
+ subject: string;
103
+ body: string;
104
+ }
105
+
106
+ declare class Mailer {
107
+ private config;
108
+ constructor(mailerConfig: MailerConfig);
109
+ sendMail(options: EmailOptions): Promise<boolean>;
110
+ }
111
+
112
+ export { ApiError, BadRequestError, Cache, type EmailOptions, type ErrorResponse, ForbiddenError, Mailer, type MailerConfig, type MongoConfig, NotFoundError, type SuccessResponse, UnauthorizedError, ValidationError, cacheMemory, connectMongoDb, errorHandler as errorMiddleware, errorResponse, random, requiredEnv, successResponse };
package/dist/index.js CHANGED
@@ -34,6 +34,7 @@ __export(index_exports, {
34
34
  BadRequestError: () => BadRequestError,
35
35
  Cache: () => Cache,
36
36
  ForbiddenError: () => ForbiddenError,
37
+ Mailer: () => mailer_default,
37
38
  NotFoundError: () => NotFoundError,
38
39
  UnauthorizedError: () => UnauthorizedError,
39
40
  ValidationError: () => ValidationError,
@@ -41,6 +42,7 @@ __export(index_exports, {
41
42
  connectMongoDb: () => connectMongoDb,
42
43
  errorMiddleware: () => errorHandler_default,
43
44
  errorResponse: () => errorResponse,
45
+ random: () => random,
44
46
  requiredEnv: () => requiredEnv,
45
47
  successResponse: () => successResponse
46
48
  });
@@ -260,12 +262,65 @@ var Cache = class extends import_node_cache.default {
260
262
  // src/utils/cacheMemory.ts
261
263
  var cacheMemory = new Cache();
262
264
  var cacheMemory_default = cacheMemory;
265
+
266
+ // src/utils/randomNum.ts
267
+ var import_crypto = __toESM(require("crypto"));
268
+ function random(digit) {
269
+ if (!Number.isInteger(digit) || digit <= 0) {
270
+ throw new Error("Digit must be a positive integer");
271
+ }
272
+ const min = 10 ** (digit - 1);
273
+ const max = 10 ** digit - 1;
274
+ const randomNumber = import_crypto.default.randomInt(min, max + 1);
275
+ return randomNumber.toString();
276
+ }
277
+
278
+ // src/utils/mailer.ts
279
+ var Mailer = class {
280
+ // 2. Use 'private' or 'readonly' to encapsulate the config data
281
+ constructor(mailerConfig) {
282
+ this.config = mailerConfig;
283
+ }
284
+ // 3. Defined the sendMail method with parameters and proper return type
285
+ async sendMail(options) {
286
+ const { to, subject, body } = options;
287
+ console.log(`Sending email via ${this.config.smtpProvider}...`);
288
+ console.log(`From: "${this.config.brandName}" <${this.config.fromEmail}>`);
289
+ console.log(`To: ${to}`);
290
+ console.log(`Subject: ${subject}`);
291
+ console.log(`Body: ${body}`);
292
+ try {
293
+ const response = await this.config.smtpProvider.emails.send({
294
+ from: `${this.config.brandName} <${this.config.fromEmail}>`,
295
+ to: [to],
296
+ subject,
297
+ html: body
298
+ // or 'text: body' depending on your needs
299
+ });
300
+ console.log("SMTP Response:", response);
301
+ if (response.error) {
302
+ console.error("SMTP API error:", response.error);
303
+ return false;
304
+ }
305
+ return true;
306
+ } catch (error) {
307
+ if (error instanceof Error) {
308
+ console.error("SMTP error:", error.message);
309
+ } else {
310
+ console.error("SMTP error:", error);
311
+ }
312
+ return false;
313
+ }
314
+ }
315
+ };
316
+ var mailer_default = Mailer;
263
317
  // Annotate the CommonJS export names for ESM import in node:
264
318
  0 && (module.exports = {
265
319
  ApiError,
266
320
  BadRequestError,
267
321
  Cache,
268
322
  ForbiddenError,
323
+ Mailer,
269
324
  NotFoundError,
270
325
  UnauthorizedError,
271
326
  ValidationError,
@@ -273,6 +328,7 @@ var cacheMemory_default = cacheMemory;
273
328
  connectMongoDb,
274
329
  errorMiddleware,
275
330
  errorResponse,
331
+ random,
276
332
  requiredEnv,
277
333
  successResponse
278
334
  });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/responses/successResponse.ts","../src/responses/errorResponse.ts","../src/utils/envManager.ts","../src/config/mongoConfig.ts","../src/errors/ApiError.ts","../src/errors/BadRequestError.ts","../src/errors/UnauthorisedError.ts","../src/errors/ForbiddenError.ts","../src/errors/NotFoundError.ts","../src/errors/ValidationError.ts","../src/handlers/errorHandler.ts","../src/utils/cache.ts","../src/utils/cacheMemory.ts"],"sourcesContent":["export { successResponse } from \"./responses/successResponse\";\nexport { errorResponse } from \"./responses/errorResponse\";\nexport { requiredEnv } from \"./utils/envManager\";\nexport { connectMongoDb } from \"./config/mongoConfig\";\nexport { default as ApiError } from \"./errors/ApiError\";\nexport { default as BadRequestError } from \"./errors/BadRequestError\";\nexport { default as UnauthorizedError } from \"./errors/UnauthorisedError\";\nexport { default as ForbiddenError } from \"./errors/ForbiddenError\";\nexport { default as NotFoundError } from \"./errors/NotFoundError\";\nexport { default as ValidationError } from \"./errors/ValidationError\";\nexport { default as errorMiddleware } from \"./handlers/errorHandler\";\nexport { Cache } from \"./utils/cache\";\nexport { default as cacheMemory } from \"./utils/cacheMemory\";\nexport type { SuccessResponse, ErrorResponse } from \"./types/response.types\";\nexport { type MongoConfig } from \"./types/mongoConfig.types\";\n","import { SuccessResponse } from \"../types/response.types\";\n\nexport const successResponse = <T>(\n data: T,\n message?: string\n): SuccessResponse<T> => ({\n success: true,\n data,\n message,\n});","import { ErrorResponse } from \"../types/response.types\";\n\nexport const errorResponse = (\n message: string,\n errors?: any,\n stack?: string\n): ErrorResponse => ({\n success: false,\n message,\n errors,\n stack,\n});","export const requiredEnv = (value: string | undefined, key: string): string => {\n if (!value) {\n throw new Error(`Missing required environment variable: ${key}`);\n }\n return value;\n};","import mongoose from \"mongoose\";\n\nimport type { MongoConfig } from \"../types/mongoConfig.types\";\n\nexport const connectMongoDb = async (config: MongoConfig) => {\n const { subDomain, userName, password, cluster, dbName } = config;\n // Primary +srv URL\n const srvURL = `mongodb+srv://${userName}:${password}@${cluster.toLowerCase()}.${subDomain}.mongodb.net/${dbName}?retryWrites=true&w=majority&appName=${cluster.toLowerCase()}`;\n // Fallback standard mongodb:// URL (you need to adjust hostnames from Atlas)\n const fallbackURL = `mongodb://${userName}:${password}@ac-hkntio7-shard-00-00.${subDomain}.mongodb.net:27017,ac-hkntio7-shard-00-01.${subDomain}.mongodb.net:27017,ac-hkntio7-shard-00-02.${subDomain}.mongodb.net:27017/${dbName}?ssl=true&replicaSet=atlas-cji6jk-shard-0&authSource=admin&appName=${cluster}`;\n try {\n console.log(\"Trying primary +srv connection...\");\n await mongoose.connect(srvURL);\n console.log(\"Connected to MongoDB Atlas via +srv URL\");\n return true;\n } catch (err: any) {\n console.warn(\"+srv connection failed:\", err.message);\n console.log(\"Trying fallback standard connection...\");\n try {\n await mongoose.connect(fallbackURL);\n console.log(\"Connected to MongoDB Atlas via fallback URL\");\n return true;\n } catch (fallbackErr: any) {\n console.error(\"Fallback connection failed:\", fallbackErr.message);\n return false;\n }\n }\n};","export default class ApiError extends Error {\n statusCode: number;\n isOperational: boolean;\n errors?: any;\n\n constructor(\n message: string,\n statusCode = 500,\n errors?: any,\n isOperational = true\n ) {\n super(message);\n\n this.statusCode = statusCode;\n this.errors = errors;\n this.isOperational = isOperational;\n\n Error.captureStackTrace(this, this.constructor);\n }\n}","import ApiError from \"./ApiError\";\n\nexport default class BadRequestError extends ApiError {\n constructor(message = \"Bad Request\", errors?: any) {\n super(message, 400, errors);\n }\n}","import ApiError from \"./ApiError\";\n\nexport default class UnauthorizedError extends ApiError {\n constructor(message = \"Unauthorized\", errors?: any) {\n super(message, 401, errors);\n }\n}","import ApiError from \"./ApiError\";\n\nexport default class ForbiddenError extends ApiError {\n constructor(message = \"Forbidden\", errors?: any) {\n super(message, 403, errors);\n }\n}","import ApiError from \"./ApiError\";\n\nexport default class NotFoundError extends ApiError {\n constructor(message = \"Resource Not Found\", errors?: any) {\n super(message, 404, errors);\n }\n}","import ApiError from \"./ApiError\";\n\nexport default class ValidationError extends ApiError {\n constructor(message = \"Validation Failed\", errors?: any) {\n super(message, 422, errors);\n }\n}","// errorHandler.ts\nimport { Request, Response, NextFunction } from \"express\";\nimport ApiError from \"../errors/ApiError\";\nimport { errorResponse } from \"../responses/errorResponse\";\n\nconst routeNotFoundHandler = (req: Request, _: Response, next: NextFunction) => {\n next(new ApiError(`Route ${req.originalUrl} not found`, 404));\n};\n\nconst globalErrorHandler = (err: any, req: Request, res: Response, next: NextFunction) => {\n console.error(\"Error:\", err);\n let statusCode = 500;\n let message = \"Internal Server Error\";\n let errors = undefined;\n let stack = undefined;\n\n if (err instanceof ApiError) {\n statusCode = err.statusCode;\n message = err.message;\n errors = err.errors;\n }\n\n if (process.env.NODE_ENV === \"development\") {\n stack = err.stack;\n }\n\n return res.status(statusCode).json(errorResponse(message, errors, stack));\n};\n\n// Export as a tuple with explicit types so spread works correctly\nconst errorHandler: [\n (req: Request, res: Response, next: NextFunction) => void,\n (err: any, req: Request, res: Response, next: NextFunction) => void\n] = [routeNotFoundHandler, globalErrorHandler];\n\nexport default errorHandler;","import NodeCache from \"node-cache\";\n\ntype CacheValue = unknown;\ntype ComputeFn<T> = () => Promise<T> | T;\n\nexport class Cache extends NodeCache {\n private namespaces: Set<string>;\n private tagMap: Map<string, Set<string>>;\n\n constructor(options: NodeCache.Options = {}) {\n const defaults: NodeCache.Options = {\n stdTTL: 60,\n checkperiod: 120,\n };\n\n super({ ...defaults, ...options });\n\n this.namespaces = new Set<string>();\n this.tagMap = new Map<string, Set<string>>();\n\n this.on(\"expired\", (key: string, value: unknown) => {\n console.log(`[Cache] Key expired: ${key} ->`, value);\n this.removeKeyFromTags(key);\n });\n }\n\n private getKey(key: string, namespace?: string): string {\n return namespace ? `${namespace}:${key}` : key;\n }\n\n private removeKeyFromTags(key: string): void {\n for (const [tag, keys] of this.tagMap.entries()) {\n if (keys.has(key)) {\n keys.delete(key);\n if (keys.size === 0) {\n this.tagMap.delete(tag);\n }\n }\n }\n }\n\n setItem(\n key: string,\n value: CacheValue,\n ttl: number =60,\n namespace?: string,\n tags: string[] = []\n ): void {\n const fullKey = this.getKey(key, namespace);\n\n if (namespace) {\n this.namespaces.add(namespace);\n }\n\n const storedValue =\n typeof value === \"string\" ? value : JSON.stringify(value);\n\n this.set(fullKey, storedValue, ttl);\n\n for (const tag of tags) {\n if (!this.tagMap.has(tag)) {\n this.tagMap.set(tag, new Set<string>());\n }\n this.tagMap.get(tag)!.add(fullKey);\n }\n }\n\n getItem<T = unknown>(key: string, namespace?: string): T | undefined {\n const fullKey = this.getKey(key, namespace);\n const value = this.get(fullKey);\n\n if (value === undefined) return undefined;\n\n try {\n return JSON.parse(value as string) as T;\n } catch {\n return value as T;\n }\n }\n\n async getOrSetItem<T>(\n key: string,\n computeFn: ComputeFn<T>,\n ttl?: number,\n namespace?: string,\n tags: string[] = []\n ): Promise<T> {\n let value = this.getItem<T>(key, namespace);\n\n if (value === undefined) {\n value = await computeFn();\n this.setItem(key, value, ttl, namespace, tags);\n }\n\n return value;\n }\n\n deleteItem(key: string, namespace?: string): number {\n const fullKey = this.getKey(key, namespace);\n this.removeKeyFromTags(fullKey);\n return this.del(fullKey);\n }\n\n clearNamespace(namespace?: string): void {\n if (!namespace) {\n this.flushAll();\n this.tagMap.clear();\n console.log(\"[Cache] All keys cleared\");\n return;\n }\n\n for (const key of this.keys()) {\n if (key.startsWith(`${namespace}:`)) {\n this.deleteItem(key);\n }\n }\n\n console.log(`[Cache] Namespace \"${namespace}\" cleared`);\n }\n\n /**\n * Delete all keys associated with a tag\n */\n clearTag(tag: string): void {\n const keys = this.tagMap.get(tag);\n if (!keys) return;\n\n for (const key of keys) {\n this.del(key);\n }\n\n this.tagMap.delete(tag);\n console.log(`[Cache] All keys with tag \"${tag}\" cleared`);\n }\n}\n","import { Cache } from \"./cache\";\n\nconst cacheMemory = new Cache();\n\nexport default cacheMemory;\n"],"mappings":"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","names":["mongoose","NodeCache"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/responses/successResponse.ts","../src/responses/errorResponse.ts","../src/utils/envManager.ts","../src/config/mongoConfig.ts","../src/errors/ApiError.ts","../src/errors/BadRequestError.ts","../src/errors/UnauthorisedError.ts","../src/errors/ForbiddenError.ts","../src/errors/NotFoundError.ts","../src/errors/ValidationError.ts","../src/handlers/errorHandler.ts","../src/utils/cache.ts","../src/utils/cacheMemory.ts","../src/utils/randomNum.ts","../src/utils/mailer.ts"],"sourcesContent":["export { successResponse } from \"./responses/successResponse\";\nexport { errorResponse } from \"./responses/errorResponse\";\nexport { requiredEnv } from \"./utils/envManager\";\nexport { connectMongoDb } from \"./config/mongoConfig\";\nexport { default as ApiError } from \"./errors/ApiError\";\nexport { default as BadRequestError } from \"./errors/BadRequestError\";\nexport { default as UnauthorizedError } from \"./errors/UnauthorisedError\";\nexport { default as ForbiddenError } from \"./errors/ForbiddenError\";\nexport { default as NotFoundError } from \"./errors/NotFoundError\";\nexport { default as ValidationError } from \"./errors/ValidationError\";\nexport { default as errorMiddleware } from \"./handlers/errorHandler\";\nexport { Cache } from \"./utils/cache\";\nexport { default as cacheMemory } from \"./utils/cacheMemory\";\nexport {default as random} from \"./utils/randomNum\";\nexport type { SuccessResponse, ErrorResponse } from \"./types/response.types\";\nexport { type MongoConfig } from \"./types/mongoConfig.types\";\nexport {type MailerConfig, EmailOptions} from \"./types/mailerConfig.types\";\nexport {default as Mailer} from \"./utils/mailer\";\n","import { SuccessResponse } from \"../types/response.types\";\n\nexport const successResponse = <T>(\n data: T,\n message?: string\n): SuccessResponse<T> => ({\n success: true,\n data,\n message,\n});","import { ErrorResponse } from \"../types/response.types\";\n\nexport const errorResponse = (\n message: string,\n errors?: any,\n stack?: string\n): ErrorResponse => ({\n success: false,\n message,\n errors,\n stack,\n});","export const requiredEnv = (value: string | undefined, key: string): string => {\n if (!value) {\n throw new Error(`Missing required environment variable: ${key}`);\n }\n return value;\n};","import mongoose from \"mongoose\";\n\nimport type { MongoConfig } from \"../types/mongoConfig.types\";\n\nexport const connectMongoDb = async (config: MongoConfig) => {\n const { subDomain, userName, password, cluster, dbName } = config;\n // Primary +srv URL\n const srvURL = `mongodb+srv://${userName}:${password}@${cluster.toLowerCase()}.${subDomain}.mongodb.net/${dbName}?retryWrites=true&w=majority&appName=${cluster.toLowerCase()}`;\n // Fallback standard mongodb:// URL (you need to adjust hostnames from Atlas)\n const fallbackURL = `mongodb://${userName}:${password}@ac-hkntio7-shard-00-00.${subDomain}.mongodb.net:27017,ac-hkntio7-shard-00-01.${subDomain}.mongodb.net:27017,ac-hkntio7-shard-00-02.${subDomain}.mongodb.net:27017/${dbName}?ssl=true&replicaSet=atlas-cji6jk-shard-0&authSource=admin&appName=${cluster}`;\n try {\n console.log(\"Trying primary +srv connection...\");\n await mongoose.connect(srvURL);\n console.log(\"Connected to MongoDB Atlas via +srv URL\");\n return true;\n } catch (err: any) {\n console.warn(\"+srv connection failed:\", err.message);\n console.log(\"Trying fallback standard connection...\");\n try {\n await mongoose.connect(fallbackURL);\n console.log(\"Connected to MongoDB Atlas via fallback URL\");\n return true;\n } catch (fallbackErr: any) {\n console.error(\"Fallback connection failed:\", fallbackErr.message);\n return false;\n }\n }\n};","export default class ApiError extends Error {\n statusCode: number;\n isOperational: boolean;\n errors?: any;\n\n constructor(\n message: string,\n statusCode = 500,\n errors?: any,\n isOperational = true\n ) {\n super(message);\n\n this.statusCode = statusCode;\n this.errors = errors;\n this.isOperational = isOperational;\n\n Error.captureStackTrace(this, this.constructor);\n }\n}","import ApiError from \"./ApiError\";\n\nexport default class BadRequestError extends ApiError {\n constructor(message = \"Bad Request\", errors?: any) {\n super(message, 400, errors);\n }\n}","import ApiError from \"./ApiError\";\n\nexport default class UnauthorizedError extends ApiError {\n constructor(message = \"Unauthorized\", errors?: any) {\n super(message, 401, errors);\n }\n}","import ApiError from \"./ApiError\";\n\nexport default class ForbiddenError extends ApiError {\n constructor(message = \"Forbidden\", errors?: any) {\n super(message, 403, errors);\n }\n}","import ApiError from \"./ApiError\";\n\nexport default class NotFoundError extends ApiError {\n constructor(message = \"Resource Not Found\", errors?: any) {\n super(message, 404, errors);\n }\n}","import ApiError from \"./ApiError\";\n\nexport default class ValidationError extends ApiError {\n constructor(message = \"Validation Failed\", errors?: any) {\n super(message, 422, errors);\n }\n}","// errorHandler.ts\nimport { Request, Response, NextFunction } from \"express\";\nimport ApiError from \"../errors/ApiError\";\nimport { errorResponse } from \"../responses/errorResponse\";\n\nconst routeNotFoundHandler = (req: Request, _: Response, next: NextFunction) => {\n next(new ApiError(`Route ${req.originalUrl} not found`, 404));\n};\n\nconst globalErrorHandler = (err: any, req: Request, res: Response, next: NextFunction) => {\n console.error(\"Error:\", err);\n let statusCode = 500;\n let message = \"Internal Server Error\";\n let errors = undefined;\n let stack = undefined;\n\n if (err instanceof ApiError) {\n statusCode = err.statusCode;\n message = err.message;\n errors = err.errors;\n }\n\n if (process.env.NODE_ENV === \"development\") {\n stack = err.stack;\n }\n\n return res.status(statusCode).json(errorResponse(message, errors, stack));\n};\n\n// Export as a tuple with explicit types so spread works correctly\nconst errorHandler: [\n (req: Request, res: Response, next: NextFunction) => void,\n (err: any, req: Request, res: Response, next: NextFunction) => void\n] = [routeNotFoundHandler, globalErrorHandler];\n\nexport default errorHandler;","import NodeCache from \"node-cache\";\n\ntype CacheValue = unknown;\ntype ComputeFn<T> = () => Promise<T> | T;\n\nexport class Cache extends NodeCache {\n private namespaces: Set<string>;\n private tagMap: Map<string, Set<string>>;\n\n constructor(options: NodeCache.Options = {}) {\n const defaults: NodeCache.Options = {\n stdTTL: 60,\n checkperiod: 120,\n };\n\n super({ ...defaults, ...options });\n\n this.namespaces = new Set<string>();\n this.tagMap = new Map<string, Set<string>>();\n\n this.on(\"expired\", (key: string, value: unknown) => {\n console.log(`[Cache] Key expired: ${key} ->`, value);\n this.removeKeyFromTags(key);\n });\n }\n\n private getKey(key: string, namespace?: string): string {\n return namespace ? `${namespace}:${key}` : key;\n }\n\n private removeKeyFromTags(key: string): void {\n for (const [tag, keys] of this.tagMap.entries()) {\n if (keys.has(key)) {\n keys.delete(key);\n if (keys.size === 0) {\n this.tagMap.delete(tag);\n }\n }\n }\n }\n\n setItem(\n key: string,\n value: CacheValue,\n ttl: number =60,\n namespace?: string,\n tags: string[] = []\n ): void {\n const fullKey = this.getKey(key, namespace);\n\n if (namespace) {\n this.namespaces.add(namespace);\n }\n\n const storedValue =\n typeof value === \"string\" ? value : JSON.stringify(value);\n\n this.set(fullKey, storedValue, ttl);\n\n for (const tag of tags) {\n if (!this.tagMap.has(tag)) {\n this.tagMap.set(tag, new Set<string>());\n }\n this.tagMap.get(tag)!.add(fullKey);\n }\n }\n\n getItem<T = unknown>(key: string, namespace?: string): T | undefined {\n const fullKey = this.getKey(key, namespace);\n const value = this.get(fullKey);\n\n if (value === undefined) return undefined;\n\n try {\n return JSON.parse(value as string) as T;\n } catch {\n return value as T;\n }\n }\n\n async getOrSetItem<T>(\n key: string,\n computeFn: ComputeFn<T>,\n ttl?: number,\n namespace?: string,\n tags: string[] = []\n ): Promise<T> {\n let value = this.getItem<T>(key, namespace);\n\n if (value === undefined) {\n value = await computeFn();\n this.setItem(key, value, ttl, namespace, tags);\n }\n\n return value;\n }\n\n deleteItem(key: string, namespace?: string): number {\n const fullKey = this.getKey(key, namespace);\n this.removeKeyFromTags(fullKey);\n return this.del(fullKey);\n }\n\n clearNamespace(namespace?: string): void {\n if (!namespace) {\n this.flushAll();\n this.tagMap.clear();\n console.log(\"[Cache] All keys cleared\");\n return;\n }\n\n for (const key of this.keys()) {\n if (key.startsWith(`${namespace}:`)) {\n this.deleteItem(key);\n }\n }\n\n console.log(`[Cache] Namespace \"${namespace}\" cleared`);\n }\n\n /**\n * Delete all keys associated with a tag\n */\n clearTag(tag: string): void {\n const keys = this.tagMap.get(tag);\n if (!keys) return;\n\n for (const key of keys) {\n this.del(key);\n }\n\n this.tagMap.delete(tag);\n console.log(`[Cache] All keys with tag \"${tag}\" cleared`);\n }\n}\n","import { Cache } from \"./cache\";\n\nconst cacheMemory = new Cache();\n\nexport default cacheMemory;\n","// src/utils/randomNum.ts\n\nimport crypto from \"crypto\";\n\n/**\n * Generates a random numeric string of given length.\n * Uses crypto for better randomness (production safe).\n *\n * @param digit - Length of the random number\n * @returns string\n */\nexport default function random(digit: number): string {\n if (!Number.isInteger(digit) || digit <= 0) {\n throw new Error(\"Digit must be a positive integer\");\n }\n\n const min = 10 ** (digit - 1);\n const max = 10 ** digit - 1;\n\n const randomNumber = crypto.randomInt(min, max + 1);\n\n return randomNumber.toString();\n}\n","import type { MailerConfig, EmailOptions } from \"../types/mailerConfig.types\";\n\nclass Mailer {\n // 1. In TS, you must explicitly declare class properties\n private config: MailerConfig;\n\n // 2. Use 'private' or 'readonly' to encapsulate the config data\n public constructor(mailerConfig: MailerConfig) {\n this.config = mailerConfig;\n }\n\n // 3. Defined the sendMail method with parameters and proper return type\n async sendMail(options: EmailOptions): Promise<boolean> {\n const { to, subject, body } = options;\n\n console.log(`Sending email via ${this.config.smtpProvider}...`);\n console.log(`From: \"${this.config.brandName}\" <${this.config.fromEmail}>`);\n console.log(`To: ${to}`);\n console.log(`Subject: ${subject}`);\n console.log(`Body: ${body}`);\n\n try {\n const response = await this.config.smtpProvider.emails.send({\n from: `${this.config.brandName} <${this.config.fromEmail}>`,\n to: [to],\n subject: subject,\n html: body, // or 'text: body' depending on your needs\n });\n\n console.log(\"SMTP Response:\", response);\n if (response.error) {\n console.error(\"SMTP API error:\", response.error);\n return false;\n }\n return true;\n } catch (error) {\n if (error instanceof Error) {\n console.error(\"SMTP error:\", error.message);\n } else {\n console.error(\"SMTP error:\", error);\n }\n return false;\n }\n }\n}\n\n\nexport default Mailer"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEO,IAAM,kBAAkB,CAC7B,MACA,aACwB;AAAA,EACxB,SAAS;AAAA,EACT;AAAA,EACA;AACF;;;ACPO,IAAM,gBAAgB,CAC3B,SACA,QACA,WACmB;AAAA,EACnB,SAAS;AAAA,EACT;AAAA,EACA;AAAA,EACA;AACF;;;ACXO,IAAM,cAAc,CAAC,OAA2B,QAAwB;AAC7E,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,0CAA0C,GAAG,EAAE;AAAA,EACjE;AACA,SAAO;AACT;;;ACLA,sBAAqB;AAId,IAAM,iBAAiB,OAAO,WAAwB;AAC3D,QAAM,EAAE,WAAW,UAAU,UAAU,SAAS,OAAO,IAAI;AAE3D,QAAM,SAAS,iBAAiB,QAAQ,IAAI,QAAQ,IAAI,QAAQ,YAAY,CAAC,IAAI,SAAS,gBAAgB,MAAM,wCAAwC,QAAQ,YAAY,CAAC;AAE7K,QAAM,cAAc,aAAa,QAAQ,IAAI,QAAQ,2BAA2B,SAAS,6CAA6C,SAAS,6CAA6C,SAAS,sBAAsB,MAAM,sEAAsE,OAAO;AAC9S,MAAI;AACF,YAAQ,IAAI,mCAAmC;AAC/C,UAAM,gBAAAA,QAAS,QAAQ,MAAM;AAC7B,YAAQ,IAAI,yCAAyC;AACrD,WAAO;AAAA,EACT,SAAS,KAAU;AACjB,YAAQ,KAAK,2BAA2B,IAAI,OAAO;AACnD,YAAQ,IAAI,wCAAwC;AACpD,QAAI;AACF,YAAM,gBAAAA,QAAS,QAAQ,WAAW;AAClC,cAAQ,IAAI,6CAA6C;AACzD,aAAO;AAAA,IACT,SAAS,aAAkB;AACzB,cAAQ,MAAM,+BAA+B,YAAY,OAAO;AAChE,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;AC3BA,IAAqB,WAArB,cAAsC,MAAM;AAAA,EAK1C,YACE,SACA,aAAa,KACb,QACA,gBAAgB,MAChB;AACA,UAAM,OAAO;AAEb,SAAK,aAAa;AAClB,SAAK,SAAS;AACd,SAAK,gBAAgB;AAErB,UAAM,kBAAkB,MAAM,KAAK,WAAW;AAAA,EAChD;AACF;;;ACjBA,IAAqB,kBAArB,cAA6C,SAAS;AAAA,EACpD,YAAY,UAAU,eAAe,QAAc;AACjD,UAAM,SAAS,KAAK,MAAM;AAAA,EAC5B;AACF;;;ACJA,IAAqB,oBAArB,cAA+C,SAAS;AAAA,EACtD,YAAY,UAAU,gBAAgB,QAAc;AAClD,UAAM,SAAS,KAAK,MAAM;AAAA,EAC5B;AACF;;;ACJA,IAAqB,iBAArB,cAA4C,SAAS;AAAA,EACnD,YAAY,UAAU,aAAa,QAAc;AAC/C,UAAM,SAAS,KAAK,MAAM;AAAA,EAC5B;AACF;;;ACJA,IAAqB,gBAArB,cAA2C,SAAS;AAAA,EAClD,YAAY,UAAU,sBAAsB,QAAc;AACxD,UAAM,SAAS,KAAK,MAAM;AAAA,EAC5B;AACF;;;ACJA,IAAqB,kBAArB,cAA6C,SAAS;AAAA,EACpD,YAAY,UAAU,qBAAqB,QAAc;AACvD,UAAM,SAAS,KAAK,MAAM;AAAA,EAC5B;AACF;;;ACDA,IAAM,uBAAuB,CAAC,KAAc,GAAa,SAAuB;AAC9E,OAAK,IAAI,SAAS,SAAS,IAAI,WAAW,cAAc,GAAG,CAAC;AAC9D;AAEA,IAAM,qBAAqB,CAAC,KAAU,KAAc,KAAe,SAAuB;AACxF,UAAQ,MAAM,UAAU,GAAG;AAC3B,MAAI,aAAa;AACjB,MAAI,UAAU;AACd,MAAI,SAAS;AACb,MAAI,QAAQ;AAEZ,MAAI,eAAe,UAAU;AAC3B,iBAAa,IAAI;AACjB,cAAU,IAAI;AACd,aAAS,IAAI;AAAA,EACf;AAEA,MAAI,QAAQ,IAAI,aAAa,eAAe;AAC1C,YAAQ,IAAI;AAAA,EACd;AAEA,SAAO,IAAI,OAAO,UAAU,EAAE,KAAK,cAAc,SAAS,QAAQ,KAAK,CAAC;AAC1E;AAGA,IAAM,eAGF,CAAC,sBAAsB,kBAAkB;AAE7C,IAAO,uBAAQ;;;ACnCf,wBAAsB;AAKf,IAAM,QAAN,cAAoB,kBAAAC,QAAU;AAAA,EAInC,YAAY,UAA6B,CAAC,GAAG;AAC3C,UAAM,WAA8B;AAAA,MAClC,QAAQ;AAAA,MACR,aAAa;AAAA,IACf;AAEA,UAAM,EAAE,GAAG,UAAU,GAAG,QAAQ,CAAC;AAEjC,SAAK,aAAa,oBAAI,IAAY;AAClC,SAAK,SAAS,oBAAI,IAAyB;AAE3C,SAAK,GAAG,WAAW,CAAC,KAAa,UAAmB;AAClD,cAAQ,IAAI,wBAAwB,GAAG,OAAO,KAAK;AACnD,WAAK,kBAAkB,GAAG;AAAA,IAC5B,CAAC;AAAA,EACH;AAAA,EAEQ,OAAO,KAAa,WAA4B;AACtD,WAAO,YAAY,GAAG,SAAS,IAAI,GAAG,KAAK;AAAA,EAC7C;AAAA,EAEQ,kBAAkB,KAAmB;AAC3C,eAAW,CAAC,KAAK,IAAI,KAAK,KAAK,OAAO,QAAQ,GAAG;AAC/C,UAAI,KAAK,IAAI,GAAG,GAAG;AACjB,aAAK,OAAO,GAAG;AACf,YAAI,KAAK,SAAS,GAAG;AACnB,eAAK,OAAO,OAAO,GAAG;AAAA,QACxB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,QACE,KACA,OACA,MAAa,IACb,WACA,OAAiB,CAAC,GACZ;AACN,UAAM,UAAU,KAAK,OAAO,KAAK,SAAS;AAE1C,QAAI,WAAW;AACb,WAAK,WAAW,IAAI,SAAS;AAAA,IAC/B;AAEA,UAAM,cACJ,OAAO,UAAU,WAAW,QAAQ,KAAK,UAAU,KAAK;AAE1D,SAAK,IAAI,SAAS,aAAa,GAAG;AAElC,eAAW,OAAO,MAAM;AACtB,UAAI,CAAC,KAAK,OAAO,IAAI,GAAG,GAAG;AACzB,aAAK,OAAO,IAAI,KAAK,oBAAI,IAAY,CAAC;AAAA,MACxC;AACA,WAAK,OAAO,IAAI,GAAG,EAAG,IAAI,OAAO;AAAA,IACnC;AAAA,EACF;AAAA,EAEA,QAAqB,KAAa,WAAmC;AACnE,UAAM,UAAU,KAAK,OAAO,KAAK,SAAS;AAC1C,UAAM,QAAQ,KAAK,IAAI,OAAO;AAE9B,QAAI,UAAU,OAAW,QAAO;AAEhC,QAAI;AACF,aAAO,KAAK,MAAM,KAAe;AAAA,IACnC,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,aACJ,KACA,WACA,KACA,WACA,OAAiB,CAAC,GACN;AACZ,QAAI,QAAQ,KAAK,QAAW,KAAK,SAAS;AAE1C,QAAI,UAAU,QAAW;AACvB,cAAQ,MAAM,UAAU;AACxB,WAAK,QAAQ,KAAK,OAAO,KAAK,WAAW,IAAI;AAAA,IAC/C;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,WAAW,KAAa,WAA4B;AAClD,UAAM,UAAU,KAAK,OAAO,KAAK,SAAS;AAC1C,SAAK,kBAAkB,OAAO;AAC9B,WAAO,KAAK,IAAI,OAAO;AAAA,EACzB;AAAA,EAEA,eAAe,WAA0B;AACvC,QAAI,CAAC,WAAW;AACd,WAAK,SAAS;AACd,WAAK,OAAO,MAAM;AAClB,cAAQ,IAAI,0BAA0B;AACtC;AAAA,IACF;AAEA,eAAW,OAAO,KAAK,KAAK,GAAG;AAC7B,UAAI,IAAI,WAAW,GAAG,SAAS,GAAG,GAAG;AACnC,aAAK,WAAW,GAAG;AAAA,MACrB;AAAA,IACF;AAEA,YAAQ,IAAI,sBAAsB,SAAS,WAAW;AAAA,EACxD;AAAA;AAAA;AAAA;AAAA,EAKA,SAAS,KAAmB;AAC1B,UAAM,OAAO,KAAK,OAAO,IAAI,GAAG;AAChC,QAAI,CAAC,KAAM;AAEX,eAAW,OAAO,MAAM;AACtB,WAAK,IAAI,GAAG;AAAA,IACd;AAEA,SAAK,OAAO,OAAO,GAAG;AACtB,YAAQ,IAAI,8BAA8B,GAAG,WAAW;AAAA,EAC1D;AACF;;;ACpIA,IAAM,cAAc,IAAI,MAAM;AAE9B,IAAO,sBAAQ;;;ACFf,oBAAmB;AASH,SAAR,OAAwB,OAAuB;AACrD,MAAI,CAAC,OAAO,UAAU,KAAK,KAAK,SAAS,GAAG;AAC1C,UAAM,IAAI,MAAM,kCAAkC;AAAA,EACpD;AAEA,QAAM,MAAM,OAAO,QAAQ;AAC3B,QAAM,MAAM,MAAM,QAAQ;AAE1B,QAAM,eAAe,cAAAC,QAAO,UAAU,KAAK,MAAM,CAAC;AAElD,SAAO,aAAa,SAAS;AAC/B;;;ACpBA,IAAM,SAAN,MAAa;AAAA;AAAA,EAKJ,YAAY,cAA4B;AAC7C,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA,EAGA,MAAM,SAAS,SAAyC;AACtD,UAAM,EAAE,IAAI,SAAS,KAAK,IAAI;AAE9B,YAAQ,IAAI,qBAAqB,KAAK,OAAO,YAAY,KAAK;AAC9D,YAAQ,IAAI,UAAU,KAAK,OAAO,SAAS,MAAM,KAAK,OAAO,SAAS,GAAG;AACzE,YAAQ,IAAI,OAAO,EAAE,EAAE;AACvB,YAAQ,IAAI,YAAY,OAAO,EAAE;AACjC,YAAQ,IAAI,SAAS,IAAI,EAAE;AAE3B,QAAI;AACF,YAAM,WAAW,MAAM,KAAK,OAAO,aAAa,OAAO,KAAK;AAAA,QAC1D,MAAM,GAAG,KAAK,OAAO,SAAS,KAAK,KAAK,OAAO,SAAS;AAAA,QACxD,IAAI,CAAC,EAAE;AAAA,QACP;AAAA,QACA,MAAM;AAAA;AAAA,MACR,CAAC;AAED,cAAQ,IAAI,kBAAkB,QAAQ;AACtC,UAAI,SAAS,OAAO;AAClB,gBAAQ,MAAM,mBAAmB,SAAS,KAAK;AAC/C,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT,SAAS,OAAO;AACd,UAAI,iBAAiB,OAAO;AAC1B,gBAAQ,MAAM,eAAe,MAAM,OAAO;AAAA,MAC5C,OAAO;AACL,gBAAQ,MAAM,eAAe,KAAK;AAAA,MACpC;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAGA,IAAO,iBAAQ;","names":["mongoose","NodeCache","crypto"]}
package/dist/index.mjs CHANGED
@@ -212,11 +212,64 @@ var Cache = class extends NodeCache {
212
212
  // src/utils/cacheMemory.ts
213
213
  var cacheMemory = new Cache();
214
214
  var cacheMemory_default = cacheMemory;
215
+
216
+ // src/utils/randomNum.ts
217
+ import crypto from "crypto";
218
+ function random(digit) {
219
+ if (!Number.isInteger(digit) || digit <= 0) {
220
+ throw new Error("Digit must be a positive integer");
221
+ }
222
+ const min = 10 ** (digit - 1);
223
+ const max = 10 ** digit - 1;
224
+ const randomNumber = crypto.randomInt(min, max + 1);
225
+ return randomNumber.toString();
226
+ }
227
+
228
+ // src/utils/mailer.ts
229
+ var Mailer = class {
230
+ // 2. Use 'private' or 'readonly' to encapsulate the config data
231
+ constructor(mailerConfig) {
232
+ this.config = mailerConfig;
233
+ }
234
+ // 3. Defined the sendMail method with parameters and proper return type
235
+ async sendMail(options) {
236
+ const { to, subject, body } = options;
237
+ console.log(`Sending email via ${this.config.smtpProvider}...`);
238
+ console.log(`From: "${this.config.brandName}" <${this.config.fromEmail}>`);
239
+ console.log(`To: ${to}`);
240
+ console.log(`Subject: ${subject}`);
241
+ console.log(`Body: ${body}`);
242
+ try {
243
+ const response = await this.config.smtpProvider.emails.send({
244
+ from: `${this.config.brandName} <${this.config.fromEmail}>`,
245
+ to: [to],
246
+ subject,
247
+ html: body
248
+ // or 'text: body' depending on your needs
249
+ });
250
+ console.log("SMTP Response:", response);
251
+ if (response.error) {
252
+ console.error("SMTP API error:", response.error);
253
+ return false;
254
+ }
255
+ return true;
256
+ } catch (error) {
257
+ if (error instanceof Error) {
258
+ console.error("SMTP error:", error.message);
259
+ } else {
260
+ console.error("SMTP error:", error);
261
+ }
262
+ return false;
263
+ }
264
+ }
265
+ };
266
+ var mailer_default = Mailer;
215
267
  export {
216
268
  ApiError,
217
269
  BadRequestError,
218
270
  Cache,
219
271
  ForbiddenError,
272
+ mailer_default as Mailer,
220
273
  NotFoundError,
221
274
  UnauthorizedError,
222
275
  ValidationError,
@@ -224,6 +277,7 @@ export {
224
277
  connectMongoDb,
225
278
  errorHandler_default as errorMiddleware,
226
279
  errorResponse,
280
+ random,
227
281
  requiredEnv,
228
282
  successResponse
229
283
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/responses/successResponse.ts","../src/responses/errorResponse.ts","../src/utils/envManager.ts","../src/config/mongoConfig.ts","../src/errors/ApiError.ts","../src/errors/BadRequestError.ts","../src/errors/UnauthorisedError.ts","../src/errors/ForbiddenError.ts","../src/errors/NotFoundError.ts","../src/errors/ValidationError.ts","../src/handlers/errorHandler.ts","../src/utils/cache.ts","../src/utils/cacheMemory.ts"],"sourcesContent":["import { SuccessResponse } from \"../types/response.types\";\n\nexport const successResponse = <T>(\n data: T,\n message?: string\n): SuccessResponse<T> => ({\n success: true,\n data,\n message,\n});","import { ErrorResponse } from \"../types/response.types\";\n\nexport const errorResponse = (\n message: string,\n errors?: any,\n stack?: string\n): ErrorResponse => ({\n success: false,\n message,\n errors,\n stack,\n});","export const requiredEnv = (value: string | undefined, key: string): string => {\n if (!value) {\n throw new Error(`Missing required environment variable: ${key}`);\n }\n return value;\n};","import mongoose from \"mongoose\";\n\nimport type { MongoConfig } from \"../types/mongoConfig.types\";\n\nexport const connectMongoDb = async (config: MongoConfig) => {\n const { subDomain, userName, password, cluster, dbName } = config;\n // Primary +srv URL\n const srvURL = `mongodb+srv://${userName}:${password}@${cluster.toLowerCase()}.${subDomain}.mongodb.net/${dbName}?retryWrites=true&w=majority&appName=${cluster.toLowerCase()}`;\n // Fallback standard mongodb:// URL (you need to adjust hostnames from Atlas)\n const fallbackURL = `mongodb://${userName}:${password}@ac-hkntio7-shard-00-00.${subDomain}.mongodb.net:27017,ac-hkntio7-shard-00-01.${subDomain}.mongodb.net:27017,ac-hkntio7-shard-00-02.${subDomain}.mongodb.net:27017/${dbName}?ssl=true&replicaSet=atlas-cji6jk-shard-0&authSource=admin&appName=${cluster}`;\n try {\n console.log(\"Trying primary +srv connection...\");\n await mongoose.connect(srvURL);\n console.log(\"Connected to MongoDB Atlas via +srv URL\");\n return true;\n } catch (err: any) {\n console.warn(\"+srv connection failed:\", err.message);\n console.log(\"Trying fallback standard connection...\");\n try {\n await mongoose.connect(fallbackURL);\n console.log(\"Connected to MongoDB Atlas via fallback URL\");\n return true;\n } catch (fallbackErr: any) {\n console.error(\"Fallback connection failed:\", fallbackErr.message);\n return false;\n }\n }\n};","export default class ApiError extends Error {\n statusCode: number;\n isOperational: boolean;\n errors?: any;\n\n constructor(\n message: string,\n statusCode = 500,\n errors?: any,\n isOperational = true\n ) {\n super(message);\n\n this.statusCode = statusCode;\n this.errors = errors;\n this.isOperational = isOperational;\n\n Error.captureStackTrace(this, this.constructor);\n }\n}","import ApiError from \"./ApiError\";\n\nexport default class BadRequestError extends ApiError {\n constructor(message = \"Bad Request\", errors?: any) {\n super(message, 400, errors);\n }\n}","import ApiError from \"./ApiError\";\n\nexport default class UnauthorizedError extends ApiError {\n constructor(message = \"Unauthorized\", errors?: any) {\n super(message, 401, errors);\n }\n}","import ApiError from \"./ApiError\";\n\nexport default class ForbiddenError extends ApiError {\n constructor(message = \"Forbidden\", errors?: any) {\n super(message, 403, errors);\n }\n}","import ApiError from \"./ApiError\";\n\nexport default class NotFoundError extends ApiError {\n constructor(message = \"Resource Not Found\", errors?: any) {\n super(message, 404, errors);\n }\n}","import ApiError from \"./ApiError\";\n\nexport default class ValidationError extends ApiError {\n constructor(message = \"Validation Failed\", errors?: any) {\n super(message, 422, errors);\n }\n}","// errorHandler.ts\nimport { Request, Response, NextFunction } from \"express\";\nimport ApiError from \"../errors/ApiError\";\nimport { errorResponse } from \"../responses/errorResponse\";\n\nconst routeNotFoundHandler = (req: Request, _: Response, next: NextFunction) => {\n next(new ApiError(`Route ${req.originalUrl} not found`, 404));\n};\n\nconst globalErrorHandler = (err: any, req: Request, res: Response, next: NextFunction) => {\n console.error(\"Error:\", err);\n let statusCode = 500;\n let message = \"Internal Server Error\";\n let errors = undefined;\n let stack = undefined;\n\n if (err instanceof ApiError) {\n statusCode = err.statusCode;\n message = err.message;\n errors = err.errors;\n }\n\n if (process.env.NODE_ENV === \"development\") {\n stack = err.stack;\n }\n\n return res.status(statusCode).json(errorResponse(message, errors, stack));\n};\n\n// Export as a tuple with explicit types so spread works correctly\nconst errorHandler: [\n (req: Request, res: Response, next: NextFunction) => void,\n (err: any, req: Request, res: Response, next: NextFunction) => void\n] = [routeNotFoundHandler, globalErrorHandler];\n\nexport default errorHandler;","import NodeCache from \"node-cache\";\n\ntype CacheValue = unknown;\ntype ComputeFn<T> = () => Promise<T> | T;\n\nexport class Cache extends NodeCache {\n private namespaces: Set<string>;\n private tagMap: Map<string, Set<string>>;\n\n constructor(options: NodeCache.Options = {}) {\n const defaults: NodeCache.Options = {\n stdTTL: 60,\n checkperiod: 120,\n };\n\n super({ ...defaults, ...options });\n\n this.namespaces = new Set<string>();\n this.tagMap = new Map<string, Set<string>>();\n\n this.on(\"expired\", (key: string, value: unknown) => {\n console.log(`[Cache] Key expired: ${key} ->`, value);\n this.removeKeyFromTags(key);\n });\n }\n\n private getKey(key: string, namespace?: string): string {\n return namespace ? `${namespace}:${key}` : key;\n }\n\n private removeKeyFromTags(key: string): void {\n for (const [tag, keys] of this.tagMap.entries()) {\n if (keys.has(key)) {\n keys.delete(key);\n if (keys.size === 0) {\n this.tagMap.delete(tag);\n }\n }\n }\n }\n\n setItem(\n key: string,\n value: CacheValue,\n ttl: number =60,\n namespace?: string,\n tags: string[] = []\n ): void {\n const fullKey = this.getKey(key, namespace);\n\n if (namespace) {\n this.namespaces.add(namespace);\n }\n\n const storedValue =\n typeof value === \"string\" ? value : JSON.stringify(value);\n\n this.set(fullKey, storedValue, ttl);\n\n for (const tag of tags) {\n if (!this.tagMap.has(tag)) {\n this.tagMap.set(tag, new Set<string>());\n }\n this.tagMap.get(tag)!.add(fullKey);\n }\n }\n\n getItem<T = unknown>(key: string, namespace?: string): T | undefined {\n const fullKey = this.getKey(key, namespace);\n const value = this.get(fullKey);\n\n if (value === undefined) return undefined;\n\n try {\n return JSON.parse(value as string) as T;\n } catch {\n return value as T;\n }\n }\n\n async getOrSetItem<T>(\n key: string,\n computeFn: ComputeFn<T>,\n ttl?: number,\n namespace?: string,\n tags: string[] = []\n ): Promise<T> {\n let value = this.getItem<T>(key, namespace);\n\n if (value === undefined) {\n value = await computeFn();\n this.setItem(key, value, ttl, namespace, tags);\n }\n\n return value;\n }\n\n deleteItem(key: string, namespace?: string): number {\n const fullKey = this.getKey(key, namespace);\n this.removeKeyFromTags(fullKey);\n return this.del(fullKey);\n }\n\n clearNamespace(namespace?: string): void {\n if (!namespace) {\n this.flushAll();\n this.tagMap.clear();\n console.log(\"[Cache] All keys cleared\");\n return;\n }\n\n for (const key of this.keys()) {\n if (key.startsWith(`${namespace}:`)) {\n this.deleteItem(key);\n }\n }\n\n console.log(`[Cache] Namespace \"${namespace}\" cleared`);\n }\n\n /**\n * Delete all keys associated with a tag\n */\n clearTag(tag: string): void {\n const keys = this.tagMap.get(tag);\n if (!keys) return;\n\n for (const key of keys) {\n this.del(key);\n }\n\n this.tagMap.delete(tag);\n console.log(`[Cache] All keys with tag \"${tag}\" cleared`);\n }\n}\n","import { Cache } from \"./cache\";\n\nconst cacheMemory = new Cache();\n\nexport default cacheMemory;\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/responses/successResponse.ts","../src/responses/errorResponse.ts","../src/utils/envManager.ts","../src/config/mongoConfig.ts","../src/errors/ApiError.ts","../src/errors/BadRequestError.ts","../src/errors/UnauthorisedError.ts","../src/errors/ForbiddenError.ts","../src/errors/NotFoundError.ts","../src/errors/ValidationError.ts","../src/handlers/errorHandler.ts","../src/utils/cache.ts","../src/utils/cacheMemory.ts","../src/utils/randomNum.ts","../src/utils/mailer.ts"],"sourcesContent":["import { SuccessResponse } from \"../types/response.types\";\n\nexport const successResponse = <T>(\n data: T,\n message?: string\n): SuccessResponse<T> => ({\n success: true,\n data,\n message,\n});","import { ErrorResponse } from \"../types/response.types\";\n\nexport const errorResponse = (\n message: string,\n errors?: any,\n stack?: string\n): ErrorResponse => ({\n success: false,\n message,\n errors,\n stack,\n});","export const requiredEnv = (value: string | undefined, key: string): string => {\n if (!value) {\n throw new Error(`Missing required environment variable: ${key}`);\n }\n return value;\n};","import mongoose from \"mongoose\";\n\nimport type { MongoConfig } from \"../types/mongoConfig.types\";\n\nexport const connectMongoDb = async (config: MongoConfig) => {\n const { subDomain, userName, password, cluster, dbName } = config;\n // Primary +srv URL\n const srvURL = `mongodb+srv://${userName}:${password}@${cluster.toLowerCase()}.${subDomain}.mongodb.net/${dbName}?retryWrites=true&w=majority&appName=${cluster.toLowerCase()}`;\n // Fallback standard mongodb:// URL (you need to adjust hostnames from Atlas)\n const fallbackURL = `mongodb://${userName}:${password}@ac-hkntio7-shard-00-00.${subDomain}.mongodb.net:27017,ac-hkntio7-shard-00-01.${subDomain}.mongodb.net:27017,ac-hkntio7-shard-00-02.${subDomain}.mongodb.net:27017/${dbName}?ssl=true&replicaSet=atlas-cji6jk-shard-0&authSource=admin&appName=${cluster}`;\n try {\n console.log(\"Trying primary +srv connection...\");\n await mongoose.connect(srvURL);\n console.log(\"Connected to MongoDB Atlas via +srv URL\");\n return true;\n } catch (err: any) {\n console.warn(\"+srv connection failed:\", err.message);\n console.log(\"Trying fallback standard connection...\");\n try {\n await mongoose.connect(fallbackURL);\n console.log(\"Connected to MongoDB Atlas via fallback URL\");\n return true;\n } catch (fallbackErr: any) {\n console.error(\"Fallback connection failed:\", fallbackErr.message);\n return false;\n }\n }\n};","export default class ApiError extends Error {\n statusCode: number;\n isOperational: boolean;\n errors?: any;\n\n constructor(\n message: string,\n statusCode = 500,\n errors?: any,\n isOperational = true\n ) {\n super(message);\n\n this.statusCode = statusCode;\n this.errors = errors;\n this.isOperational = isOperational;\n\n Error.captureStackTrace(this, this.constructor);\n }\n}","import ApiError from \"./ApiError\";\n\nexport default class BadRequestError extends ApiError {\n constructor(message = \"Bad Request\", errors?: any) {\n super(message, 400, errors);\n }\n}","import ApiError from \"./ApiError\";\n\nexport default class UnauthorizedError extends ApiError {\n constructor(message = \"Unauthorized\", errors?: any) {\n super(message, 401, errors);\n }\n}","import ApiError from \"./ApiError\";\n\nexport default class ForbiddenError extends ApiError {\n constructor(message = \"Forbidden\", errors?: any) {\n super(message, 403, errors);\n }\n}","import ApiError from \"./ApiError\";\n\nexport default class NotFoundError extends ApiError {\n constructor(message = \"Resource Not Found\", errors?: any) {\n super(message, 404, errors);\n }\n}","import ApiError from \"./ApiError\";\n\nexport default class ValidationError extends ApiError {\n constructor(message = \"Validation Failed\", errors?: any) {\n super(message, 422, errors);\n }\n}","// errorHandler.ts\nimport { Request, Response, NextFunction } from \"express\";\nimport ApiError from \"../errors/ApiError\";\nimport { errorResponse } from \"../responses/errorResponse\";\n\nconst routeNotFoundHandler = (req: Request, _: Response, next: NextFunction) => {\n next(new ApiError(`Route ${req.originalUrl} not found`, 404));\n};\n\nconst globalErrorHandler = (err: any, req: Request, res: Response, next: NextFunction) => {\n console.error(\"Error:\", err);\n let statusCode = 500;\n let message = \"Internal Server Error\";\n let errors = undefined;\n let stack = undefined;\n\n if (err instanceof ApiError) {\n statusCode = err.statusCode;\n message = err.message;\n errors = err.errors;\n }\n\n if (process.env.NODE_ENV === \"development\") {\n stack = err.stack;\n }\n\n return res.status(statusCode).json(errorResponse(message, errors, stack));\n};\n\n// Export as a tuple with explicit types so spread works correctly\nconst errorHandler: [\n (req: Request, res: Response, next: NextFunction) => void,\n (err: any, req: Request, res: Response, next: NextFunction) => void\n] = [routeNotFoundHandler, globalErrorHandler];\n\nexport default errorHandler;","import NodeCache from \"node-cache\";\n\ntype CacheValue = unknown;\ntype ComputeFn<T> = () => Promise<T> | T;\n\nexport class Cache extends NodeCache {\n private namespaces: Set<string>;\n private tagMap: Map<string, Set<string>>;\n\n constructor(options: NodeCache.Options = {}) {\n const defaults: NodeCache.Options = {\n stdTTL: 60,\n checkperiod: 120,\n };\n\n super({ ...defaults, ...options });\n\n this.namespaces = new Set<string>();\n this.tagMap = new Map<string, Set<string>>();\n\n this.on(\"expired\", (key: string, value: unknown) => {\n console.log(`[Cache] Key expired: ${key} ->`, value);\n this.removeKeyFromTags(key);\n });\n }\n\n private getKey(key: string, namespace?: string): string {\n return namespace ? `${namespace}:${key}` : key;\n }\n\n private removeKeyFromTags(key: string): void {\n for (const [tag, keys] of this.tagMap.entries()) {\n if (keys.has(key)) {\n keys.delete(key);\n if (keys.size === 0) {\n this.tagMap.delete(tag);\n }\n }\n }\n }\n\n setItem(\n key: string,\n value: CacheValue,\n ttl: number =60,\n namespace?: string,\n tags: string[] = []\n ): void {\n const fullKey = this.getKey(key, namespace);\n\n if (namespace) {\n this.namespaces.add(namespace);\n }\n\n const storedValue =\n typeof value === \"string\" ? value : JSON.stringify(value);\n\n this.set(fullKey, storedValue, ttl);\n\n for (const tag of tags) {\n if (!this.tagMap.has(tag)) {\n this.tagMap.set(tag, new Set<string>());\n }\n this.tagMap.get(tag)!.add(fullKey);\n }\n }\n\n getItem<T = unknown>(key: string, namespace?: string): T | undefined {\n const fullKey = this.getKey(key, namespace);\n const value = this.get(fullKey);\n\n if (value === undefined) return undefined;\n\n try {\n return JSON.parse(value as string) as T;\n } catch {\n return value as T;\n }\n }\n\n async getOrSetItem<T>(\n key: string,\n computeFn: ComputeFn<T>,\n ttl?: number,\n namespace?: string,\n tags: string[] = []\n ): Promise<T> {\n let value = this.getItem<T>(key, namespace);\n\n if (value === undefined) {\n value = await computeFn();\n this.setItem(key, value, ttl, namespace, tags);\n }\n\n return value;\n }\n\n deleteItem(key: string, namespace?: string): number {\n const fullKey = this.getKey(key, namespace);\n this.removeKeyFromTags(fullKey);\n return this.del(fullKey);\n }\n\n clearNamespace(namespace?: string): void {\n if (!namespace) {\n this.flushAll();\n this.tagMap.clear();\n console.log(\"[Cache] All keys cleared\");\n return;\n }\n\n for (const key of this.keys()) {\n if (key.startsWith(`${namespace}:`)) {\n this.deleteItem(key);\n }\n }\n\n console.log(`[Cache] Namespace \"${namespace}\" cleared`);\n }\n\n /**\n * Delete all keys associated with a tag\n */\n clearTag(tag: string): void {\n const keys = this.tagMap.get(tag);\n if (!keys) return;\n\n for (const key of keys) {\n this.del(key);\n }\n\n this.tagMap.delete(tag);\n console.log(`[Cache] All keys with tag \"${tag}\" cleared`);\n }\n}\n","import { Cache } from \"./cache\";\n\nconst cacheMemory = new Cache();\n\nexport default cacheMemory;\n","// src/utils/randomNum.ts\n\nimport crypto from \"crypto\";\n\n/**\n * Generates a random numeric string of given length.\n * Uses crypto for better randomness (production safe).\n *\n * @param digit - Length of the random number\n * @returns string\n */\nexport default function random(digit: number): string {\n if (!Number.isInteger(digit) || digit <= 0) {\n throw new Error(\"Digit must be a positive integer\");\n }\n\n const min = 10 ** (digit - 1);\n const max = 10 ** digit - 1;\n\n const randomNumber = crypto.randomInt(min, max + 1);\n\n return randomNumber.toString();\n}\n","import type { MailerConfig, EmailOptions } from \"../types/mailerConfig.types\";\n\nclass Mailer {\n // 1. In TS, you must explicitly declare class properties\n private config: MailerConfig;\n\n // 2. Use 'private' or 'readonly' to encapsulate the config data\n public constructor(mailerConfig: MailerConfig) {\n this.config = mailerConfig;\n }\n\n // 3. Defined the sendMail method with parameters and proper return type\n async sendMail(options: EmailOptions): Promise<boolean> {\n const { to, subject, body } = options;\n\n console.log(`Sending email via ${this.config.smtpProvider}...`);\n console.log(`From: \"${this.config.brandName}\" <${this.config.fromEmail}>`);\n console.log(`To: ${to}`);\n console.log(`Subject: ${subject}`);\n console.log(`Body: ${body}`);\n\n try {\n const response = await this.config.smtpProvider.emails.send({\n from: `${this.config.brandName} <${this.config.fromEmail}>`,\n to: [to],\n subject: subject,\n html: body, // or 'text: body' depending on your needs\n });\n\n console.log(\"SMTP Response:\", response);\n if (response.error) {\n console.error(\"SMTP API error:\", response.error);\n return false;\n }\n return true;\n } catch (error) {\n if (error instanceof Error) {\n console.error(\"SMTP error:\", error.message);\n } else {\n console.error(\"SMTP error:\", error);\n }\n return false;\n }\n }\n}\n\n\nexport default Mailer"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexus-backend",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "Backend utility library for Express.js",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -20,6 +20,7 @@
20
20
  "mongoose": "^9.6.2",
21
21
  "morgan": "^1.10.1",
22
22
  "socket.io": "^4.8.3",
23
+ "resend": "^6.12.3",
23
24
  "node-cache": "5.1.2"
24
25
  },
25
26
  "devDependencies": {