elseware-nodejs 1.0.1 → 1.0.2

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.cjs CHANGED
@@ -1,2 +1,4 @@
1
- 'use strict';var m=require('mongoose'),w=require('dotenv');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var m__default=/*#__PURE__*/_interopDefault(m);var w__default=/*#__PURE__*/_interopDefault(w);var n=class extends Error{statusCode;status;isOperational;code;details;constructor(e,t=500,o){super(e),this.statusCode=t,this.status=`${t}`.startsWith("4")?"fail":"error",this.isOperational=true,this.code=o?.code,this.details=o?.details,Error.captureStackTrace(this,this.constructor);}};var a=class s{static send(e,t){let{statusCode:o=200,success:i=true,message:r="Success",data:u,meta:p}=t;if(o===204)return e.status(204).send();let d={success:i,message:r};return u!==void 0&&(d.data=u),p!==void 0&&(d.meta=p),e.status(o).json(d)}static ok(e,t,o,i){return s.send(e,{statusCode:200,data:t,message:o,meta:i})}static created(e,t,o){return s.send(e,{statusCode:201,data:t,message:o})}static noContent(e){return s.send(e,{statusCode:204})}static error(e,t,o=500){return s.send(e,{success:false,statusCode:o,message:t})}};var c=s=>(e,t,o)=>Promise.resolve(s(e,t,o)).catch(o);var l=class{query;queryString;page;limit;constructor(e,t){this.query=e,this.queryString=t,this.page=1,this.limit=100;}filter(){let e={...this.queryString};["page","sort","limit","fields"].forEach(o=>delete e[o]);let t=JSON.stringify(e);return t=t.replace(/\b(gte|gt|lte|lt|in)\b/g,o=>`$${o}`),this.query=this.query.find(JSON.parse(t)),this}sort(){if(this.queryString.sort){let e=String(this.queryString.sort).split(",").join(" ");this.query=this.query.sort(e);}else this.query=this.query.sort("-createdAt");return this}limitFields(){if(this.queryString.fields){let e=String(this.queryString.fields).split(",").join(" ");this.query=this.query.select(e);}else this.query=this.query.select("-__v");return this}paginate(){this.page=Number(this.queryString.page)||1,this.limit=Number(this.queryString.limit)||100;let e=(this.page-1)*this.limit;return this.query=this.query.skip(e).limit(this.limit),this}populate(e){return e&&(this.query=this.query.populate(e)),this}},g=l;var h=class{static getAll(e,t={}){return c(async(o,i)=>{let r=t.filter?t.filter(o):{},u=new g(e.find(r),o.query).filter().sort().limitFields().paginate().populate(t.populate),p=await u.query;a.ok(i,p,t.message??"Records fetched successfully",{count:p.length,page:u.page,limit:u.limit});})}static getOne(e,t={}){return c(async(o,i)=>{let r=e.findById(o.params.id);t.populate&&(r=r.populate(t.populate));let u=await r;if(!u)throw new n(t.notFoundMessage??"Resource not found",404,{code:"RESOURCE_NOT_FOUND"});a.ok(i,u);})}static createOne(e,t={}){return c(async(o,i)=>{let r=await e.create(o.body);a.created(i,r,t.message??"Resource created successfully");})}static updateOne(e,t={}){return c(async(o,i)=>{let r=await e.findByIdAndUpdate(o.params.id,o.body,{new:true,runValidators:true});if(!r)throw new n(t.notFoundMessage??"Resource not found",404);a.ok(i,r,t.message??"Resource updated successfully");})}static deleteOne(e,t={}){return c(async(o,i)=>{if(!await e.findByIdAndDelete(o.params.id))throw new n(t.notFoundMessage??"Resource not found",404);a.noContent(i);})}};async function _(s){try{m__default.default.set("strictQuery",!0),await m__default.default.connect(s.uri),console.log("\u2705 Database connected successfully"),process.on("SIGINT",async()=>{await m__default.default.connection.close(),console.log("\u2705 Database connection closed"),process.exit(0);});}catch(e){console.error("\u274C Database connection failed"),e instanceof Error&&console.error(e.message),process.exit(1);}}w__default.default.config();function j(s){let{value:e,error:t}=s.validate(process.env,{abortEarly:false,allowUnknown:true});if(t)throw new Error(`\u274C Env validation error: ${t.message}`);return e}var f=class{timestamp=true;enabled=true;configure(e={}){this.timestamp=e.timestamp??this.timestamp,this.enabled=e.enabled??this.enabled;}format(e){return this.timestamp?`[${new Date().toISOString()}] ${e}`:e}output(e){this.enabled&&console.log(this.format(e));}success(e){this.output(`\u2705 ${e}`);}info(e){this.output(`\u2139\uFE0F ${e}`);}warning(e){this.output(`\u26A0\uFE0F ${e}`);}danger(e){this.output(`\u274C ${e}`);}log(e){this.output(`\u2022 ${e}`);}},Q=new f;var X=(s,e,t)=>{t();};var x=s=>new n(`Invalid ${s.path}: ${s.value}`,400,{code:"INVALID_ID"}),E=s=>{let e=s.keyValue?JSON.stringify(s.keyValue):"duplicate value";return new n(`Duplicate field value: ${e}`,400,{code:"DUPLICATE_FIELD"})},T=s=>{let e=Object.values(s.errors).map(t=>t.message);return new n("Invalid input data",400,{code:"VALIDATION_ERROR",details:e})},R=()=>new n("Invalid token. Please log in again.",401),O=()=>new n("Your token has expired. Please log in again.",401),oe=(s=false)=>(e,t,o,i)=>{let r;return e instanceof n?r=e:e instanceof Error?(r=new n(e.message,500),r.isOperational=false):(r=new n("Internal Server Error",500),r.isOperational=false),e&&e.name==="CastError"&&(r=x(e)),e&&typeof e=="object"&&e.code===11e3&&(r=E(e)),e&&e.name==="ValidationError"&&(r=T(e)),e?.name==="JsonWebTokenError"&&(r=R()),e?.name==="TokenExpiredError"&&(r=O()),r.isOperational?console.log({message:r.message},"\u26A0\uFE0F Operational error"):console.error({err:e},"\u{1F4A3} Programming error"),s?a.send(o,{success:false,statusCode:r.statusCode,message:r.isOperational?r.message:"Something went wrong"}):a.send(o,{success:false,statusCode:r.statusCode,message:r.message,data:{stack:e?.stack,details:r.details}})};var se=s=>(e,t,o)=>{let{error:i}=s.validate(e.body);if(i)return o(i);o();};var y=class{model;resourceName;constructor(e,t){this.model=e,this.resourceName=t;}async create(e){return await this.model.create(e)}async findAll(){return this.model.find()}async findById(e){let t=await this.model.findById(e);if(!t)throw new n(`${this.resourceName} not found`,404,{code:`${this.resourceName.toUpperCase()}_NOT_FOUND`});return t}async updateById(e,t){let o=await this.model.findByIdAndUpdate(e,t,{new:true,runValidators:true});if(!o)throw new n(`${this.resourceName} not found`,404,{code:`${this.resourceName.toUpperCase()}_NOT_FOUND`});return o}async deleteById(e){let t=await this.model.findByIdAndDelete(e);if(!t)throw new n(`${this.resourceName} not found`,404,{code:`${this.resourceName.toUpperCase()}_NOT_FOUND`});return t}};exports.APIFactory=h;exports.ApiResponse=a;exports.AppError=n;exports.BaseService=y;exports.GlobalErrorHandler=oe;exports.asyncHandler=c;exports.authMiddleware=X;exports.connectMongoDB=_;exports.loadEnv=j;exports.logger=Q;exports.validate=se;//# sourceMappingURL=index.cjs.map
1
+ 'use strict';var g=require('mongoose'),E=require('dotenv');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var g__default=/*#__PURE__*/_interopDefault(g);var E__default=/*#__PURE__*/_interopDefault(E);var n=class extends Error{statusCode;status;isOperational;code;details;constructor(e,t=500,r){super(e),this.statusCode=t,this.status=`${t}`.startsWith("4")?"fail":"error",this.isOperational=true,this.code=r?.code,this.details=r?.details,Error.captureStackTrace(this,this.constructor);}};var a=class o{static send(e,t){let{statusCode:r=200,success:i=true,message:s="Success",data:u,meta:d}=t;if(r===204)return e.status(204).send();let l={success:i,message:s};return u!==void 0&&(l.data=u),d!==void 0&&(l.meta=d),e.status(r).json(l)}static ok(e,t,r,i){return o.send(e,{statusCode:200,data:t,message:r,meta:i})}static created(e,t,r){return o.send(e,{statusCode:201,data:t,message:r})}static noContent(e){return o.send(e,{statusCode:204})}static error(e,t,r=500){return o.send(e,{success:false,statusCode:r,message:t})}};var p=o=>(e,t,r)=>Promise.resolve(o(e,t,r)).catch(r);var m=class{query;queryString;page;limit;constructor(e,t){this.query=e,this.queryString=t,this.page=1,this.limit=100;}filter(){let e={...this.queryString};["page","sort","limit","fields"].forEach(r=>delete e[r]);let t=JSON.stringify(e);return t=t.replace(/\b(gte|gt|lte|lt|in)\b/g,r=>`$${r}`),this.query=this.query.find(JSON.parse(t)),this}sort(){if(this.queryString.sort){let e=String(this.queryString.sort).split(",").join(" ");this.query=this.query.sort(e);}else this.query=this.query.sort("-createdAt");return this}limitFields(){if(this.queryString.fields){let e=String(this.queryString.fields).split(",").join(" ");this.query=this.query.select(e);}else this.query=this.query.select("-__v");return this}paginate(){this.page=Number(this.queryString.page)||1,this.limit=Number(this.queryString.limit)||100;let e=(this.page-1)*this.limit;return this.query=this.query.skip(e).limit(this.limit),this}populate(e){return e&&(this.query=this.query.populate(e)),this}},h=m;var y=class{static getAll(e,t={}){return p(async(r,i)=>{let s=t.filter?t.filter(r):{},u=new h(e.find(s),r.query).filter().sort().limitFields().paginate().populate(t.populate),d=await u.query;a.ok(i,d,t.message??"Records fetched successfully",{count:d.length,page:u.page,limit:u.limit});})}static getOne(e,t={}){return p(async(r,i)=>{let s=e.findById(r.params.id);t.populate&&(s=s.populate(t.populate));let u=await s;if(!u)throw new n(t.notFoundMessage??"Resource not found",404,{code:"RESOURCE_NOT_FOUND"});a.ok(i,u);})}static createOne(e,t={}){return p(async(r,i)=>{let s=await e.create(r.body);a.created(i,s,t.message??"Resource created successfully");})}static updateOne(e,t={}){return p(async(r,i)=>{let s=await e.findByIdAndUpdate(r.params.id,r.body,{new:true,runValidators:true});if(!s)throw new n(t.notFoundMessage??"Resource not found",404);a.ok(i,s,t.message??"Resource updated successfully");})}static deleteOne(e,t={}){return p(async(r,i)=>{if(!await e.findByIdAndDelete(r.params.id))throw new n(t.notFoundMessage??"Resource not found",404);a.noContent(i);})}};function w(o){if(o instanceof Error)return `${o.name}: ${o.message}
2
+ ${o.stack??""}`;if(typeof o=="object")try{return JSON.stringify(o,null,2)}catch{return "[Unserializable object]"}return String(o)}var f=class{timestamp=true;enabled=true;configure(e={}){this.timestamp=e.timestamp??this.timestamp,this.enabled=e.enabled??this.enabled;}format(e){return this.timestamp?`[${new Date().toISOString()}] ${e}`:e}output(e){this.enabled&&console.log(this.format(e));}success(e){this.output(`\u2705 ${e}`);}info(e){this.output(`\u2139\uFE0F ${e}`);}warning(e){this.output(`\u26A0\uFE0F ${e}`);}danger(e,t){t?this.output(`\u274C ${e}
3
+ ${w(t)}`):this.output(`\u274C ${e}`);}log(e){this.output(`\u2022 ${e}`);}},c=new f;async function L(o){try{g__default.default.set("strictQuery",!0),await g__default.default.connect(o.uri),c.success("\u2705 Database connected successfully"),process.on("SIGINT",async()=>{await g__default.default.connection.close(),c.success("\u2705 Database connection closed"),process.exit(0);});}catch(e){c.danger("\u274C Database connection failed"),e instanceof Error&&c.danger(e.message),process.exit(1);}}E__default.default.config();function G(o){let{value:e,error:t}=o.validate(process.env,{abortEarly:false,allowUnknown:true});if(t)throw new Error(`\u274C Env validation error: ${t.message}`);return e}var re=(o,e,t)=>{t();};var T=o=>new n(`Invalid ${o.path}: ${o.value}`,400,{code:"INVALID_ID"}),b=o=>{let e=o.keyValue?JSON.stringify(o.keyValue):"duplicate value";return new n(`Duplicate field value: ${e}`,400,{code:"DUPLICATE_FIELD"})},O=o=>{let e=Object.values(o.errors).map(t=>t.message);return new n("Invalid input data",400,{code:"VALIDATION_ERROR",details:e})},R=()=>new n("Invalid token. Please log in again.",401),q=()=>new n("Your token has expired. Please log in again.",401),ae=(o=false)=>(e,t,r,i)=>{let s;return e instanceof n?s=e:e instanceof Error?(s=new n(e.message,500),s.isOperational=false):(s=new n("Internal Server Error",500),s.isOperational=false),e&&e.name==="CastError"&&(s=T(e)),e&&typeof e=="object"&&e.code===11e3&&(s=b(e)),e&&e.name==="ValidationError"&&(s=O(e)),e?.name==="JsonWebTokenError"&&(s=R()),e?.name==="TokenExpiredError"&&(s=q()),s.isOperational?c.danger("Operational error",{message:s.message}):c.danger("Programming error",e),o?a.send(r,{success:false,statusCode:s.statusCode,message:s.isOperational?s.message:"Something went wrong"}):a.send(r,{success:false,statusCode:s.statusCode,message:s.message,data:{stack:e?.stack,details:s.details}})};var ce=o=>(e,t,r)=>{let{error:i}=o.validate(e.body);if(i)return r(i);r();};var x=class{model;resourceName;constructor(e,t){this.model=e,this.resourceName=t;}async create(e){return await this.model.create(e)}async findAll(){return this.model.find()}async findById(e){let t=await this.model.findById(e);if(!t)throw new n(`${this.resourceName} not found`,404,{code:`${this.resourceName.toUpperCase()}_NOT_FOUND`});return t}async updateById(e,t){let r=await this.model.findByIdAndUpdate(e,t,{new:true,runValidators:true});if(!r)throw new n(`${this.resourceName} not found`,404,{code:`${this.resourceName.toUpperCase()}_NOT_FOUND`});return r}async deleteById(e){let t=await this.model.findByIdAndDelete(e);if(!t)throw new n(`${this.resourceName} not found`,404,{code:`${this.resourceName.toUpperCase()}_NOT_FOUND`});return t}};exports.APIFactory=y;exports.ApiResponse=a;exports.AppError=n;exports.BaseService=x;exports.GlobalErrorHandler=ae;exports.asyncHandler=p;exports.authMiddleware=re;exports.connectMongoDB=L;exports.loadEnv=G;exports.logger=c;exports.validate=ce;//# sourceMappingURL=index.cjs.map
2
4
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors/appError.ts","../src/api/apiResponse.ts","../src/api/asyncHandler.ts","../src/api/apiFeatures.ts","../src/api/apiFactory.ts","../src/configs/database.ts","../src/configs/env.ts","../src/configs/logger.ts","../src/middlewares/auth.middleware.ts","../src/middlewares/error.middleware.ts","../src/middlewares/validate.middleware.ts","../src/services/base.service.ts"],"names":["AppError","message","statusCode","options","ApiResponse","_ApiResponse","res","success","data","meta","body","asyncHandler","fn","req","next","APIFeatures","query","queryString","queryObj","el","queryStr","match","sortBy","fields","skip","populateOptions","apiFeatures_default","APIFactory","Model","filter","features","docs","doc","connectMongoDB","config","mongoose","error","dotenv","loadEnv","schema","value","Logger","msg","logger","authMiddleware","_req","_res","handleCastErrorDB","err","handleDuplicateFieldsDB","handleValidationErrorDB","errors","handleJWTError","handleJWTExpiredError","GlobalErrorHandler","isProd","_next","validate","BaseService","model","resourceName","id"],"mappings":"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","file":"index.cjs","sourcesContent":["export class AppError extends Error {\n statusCode: number;\n status: string;\n isOperational: boolean;\n code?: string;\n details?: unknown;\n\n constructor(\n message: string,\n statusCode: number = 500,\n options?: { code?: string; details?: unknown }\n ) {\n super(message);\n\n this.statusCode = statusCode;\n this.status = `${statusCode}`.startsWith(\"4\") ? \"fail\" : \"error\";\n this.isOperational = true;\n this.code = options?.code;\n this.details = options?.details;\n\n Error.captureStackTrace(this, this.constructor);\n }\n}\n","import { Response } from \"express\";\n\nexport class ApiResponse {\n static send(\n res: Response,\n options: {\n statusCode?: number;\n success?: boolean;\n message?: string;\n data?: unknown;\n meta?: unknown;\n }\n ) {\n const {\n statusCode = 200,\n success = true,\n message = \"Success\",\n data,\n meta,\n } = options;\n\n if (statusCode === 204) {\n return res.status(204).send();\n }\n\n const body: Record<string, unknown> = { success, message };\n if (data !== undefined) body.data = data;\n if (meta !== undefined) body.meta = meta;\n\n return res.status(statusCode).json(body);\n }\n\n static ok(res: Response, data: unknown, message?: string, meta?: unknown) {\n return ApiResponse.send(res, { statusCode: 200, data, message, meta });\n }\n\n static created(res: Response, data: unknown, message?: string) {\n return ApiResponse.send(res, { statusCode: 201, data, message });\n }\n\n static noContent(res: Response) {\n return ApiResponse.send(res, { statusCode: 204 });\n }\n\n // ❌ Error response (optional use in controllers)\n static error(res: Response, message?: string, statusCode: number = 500) {\n return ApiResponse.send(res, {\n success: false,\n statusCode,\n message,\n });\n }\n}\n","import { Request, Response, NextFunction } from \"express\";\n\nexport const asyncHandler =\n (fn: (req: Request, res: Response, next: NextFunction) => Promise<void>) =>\n (req: Request, res: Response, next: NextFunction) =>\n Promise.resolve(fn(req, res, next)).catch(next);\n","import type { Query, PopulateOptions } from \"mongoose\";\n\n/**\n * Generic API Features helper for Mongoose queries\n */\nclass APIFeatures<T> {\n public query: Query<T[], T>;\n public queryString: Record<string, unknown>;\n public page: number;\n public limit: number;\n\n constructor(query: Query<T[], T>, queryString: Record<string, unknown>) {\n this.query = query;\n this.queryString = queryString;\n this.page = 1;\n this.limit = 100;\n }\n\n filter(): this {\n const queryObj = { ...this.queryString };\n\n [\"page\", \"sort\", \"limit\", \"fields\"].forEach((el) => delete queryObj[el]);\n\n let queryStr = JSON.stringify(queryObj);\n queryStr = queryStr.replace(\n /\\b(gte|gt|lte|lt|in)\\b/g,\n (match) => `$${match}`\n );\n\n this.query = this.query.find(JSON.parse(queryStr));\n\n return this;\n }\n\n sort(): this {\n if (this.queryString.sort) {\n const sortBy = String(this.queryString.sort).split(\",\").join(\" \");\n this.query = this.query.sort(sortBy);\n } else {\n this.query = this.query.sort(\"-createdAt\");\n }\n\n return this;\n }\n\n limitFields(): this {\n if (this.queryString.fields) {\n const fields = String(this.queryString.fields).split(\",\").join(\" \");\n this.query = this.query.select(fields);\n } else {\n this.query = this.query.select(\"-__v\");\n }\n\n return this;\n }\n\n paginate(): this {\n this.page = Number(this.queryString.page) || 1;\n this.limit = Number(this.queryString.limit) || 100;\n\n const skip = (this.page - 1) * this.limit;\n\n this.query = this.query.skip(skip).limit(this.limit);\n\n return this;\n }\n\n populate(populateOptions?: PopulateOptions | PopulateOptions[]): this {\n if (populateOptions) {\n this.query = this.query.populate(populateOptions);\n }\n\n return this;\n }\n}\n\nexport default APIFeatures;\n","import type { Request } from \"express\";\nimport type { Model, PopulateOptions } from \"mongoose\";\n\nimport { AppError } from \"../errors/appError.js\";\nimport { ApiResponse } from \"./apiResponse.js\";\nimport { asyncHandler } from \"./asyncHandler.js\";\nimport APIFeatures from \"./apiFeatures.js\";\n\n/**\n * Options for API factory methods\n */\ninterface ApiFactoryOptions<T = unknown> {\n message?: string;\n notFoundMessage?: string;\n populate?: PopulateOptions | PopulateOptions[];\n filter?: (req: Request) => Record<string, unknown>;\n}\n\n/**\n * Generic API Factory (Hybrid Pattern)\n * For simple CRUD operations without business logic\n */\nexport class APIFactory {\n static getAll<T>(Model: Model<T>, options: ApiFactoryOptions<T> = {}) {\n return asyncHandler(async (req, res) => {\n const filter = options.filter ? options.filter(req) : {};\n\n const features = new APIFeatures(Model.find(filter), req.query)\n .filter()\n .sort()\n .limitFields()\n .paginate()\n .populate(options.populate);\n\n const docs = await features.query;\n\n ApiResponse.ok(\n res,\n docs,\n options.message ?? \"Records fetched successfully\",\n {\n count: docs.length,\n page: features.page,\n limit: features.limit,\n }\n );\n });\n }\n\n static getOne<T>(Model: Model<T>, options: ApiFactoryOptions<T> = {}) {\n return asyncHandler(async (req, res) => {\n let query = Model.findById(req.params.id);\n\n if (options.populate) {\n query = query.populate(\n options.populate as PopulateOptions | PopulateOptions[]\n );\n }\n\n const doc = await query;\n\n if (!doc) {\n throw new AppError(\n options.notFoundMessage ?? \"Resource not found\",\n 404,\n { code: \"RESOURCE_NOT_FOUND\" }\n );\n }\n\n ApiResponse.ok(res, doc);\n });\n }\n\n static createOne<T>(Model: Model<T>, options: ApiFactoryOptions<T> = {}) {\n return asyncHandler(async (req, res) => {\n const doc = await Model.create(req.body);\n\n ApiResponse.created(\n res,\n doc,\n options.message ?? \"Resource created successfully\"\n );\n });\n }\n\n static updateOne<T>(Model: Model<T>, options: ApiFactoryOptions<T> = {}) {\n return asyncHandler(async (req, res) => {\n const doc = await Model.findByIdAndUpdate(\n req.params.id,\n req.body as Partial<T>,\n {\n new: true,\n runValidators: true,\n }\n );\n\n if (!doc) {\n throw new AppError(\n options.notFoundMessage ?? \"Resource not found\",\n 404\n );\n }\n\n ApiResponse.ok(\n res,\n doc,\n options.message ?? \"Resource updated successfully\"\n );\n });\n }\n\n static deleteOne<T>(Model: Model<T>, options: ApiFactoryOptions<T> = {}) {\n return asyncHandler(async (req, res) => {\n const doc = await Model.findByIdAndDelete(req.params.id);\n\n if (!doc) {\n throw new AppError(\n options.notFoundMessage ?? \"Resource not found\",\n 404\n );\n }\n\n ApiResponse.noContent(res);\n });\n }\n}\n","import mongoose from \"mongoose\";\n\nexport interface DatabaseConfig {\n uri: string;\n}\n\nexport async function connectMongoDB(config: DatabaseConfig): Promise<void> {\n try {\n mongoose.set(\"strictQuery\", true);\n\n await mongoose.connect(config.uri);\n\n console.log(\"✅ Database connected successfully\");\n\n process.on(\"SIGINT\", async () => {\n await mongoose.connection.close();\n console.log(\"✅ Database connection closed\");\n process.exit(0);\n });\n } catch (error: unknown) {\n console.error(\"❌ Database connection failed\");\n\n if (error instanceof Error) {\n console.error(error.message);\n }\n\n process.exit(1);\n }\n}\n","import dotenv from \"dotenv\";\nimport type Joi from \"joi\";\n\ndotenv.config();\n\n/**\n * Generic env loader for Elseware apps\n * App provides its own Joi schema\n */\nexport function loadEnv<T>(schema: Joi.ObjectSchema<T>): T {\n const { value, error } = schema.validate(process.env, {\n abortEarly: false,\n allowUnknown: true,\n });\n\n if (error) {\n throw new Error(`❌ Env validation error: ${error.message}`);\n }\n\n return value;\n}\n","export interface LoggerOptions {\n timestamp?: boolean;\n enabled?: boolean;\n}\n\nclass Logger {\n private timestamp = true;\n private enabled = true;\n\n // Use to configure once at the root\n configure(options: LoggerOptions = {}) {\n this.timestamp = options.timestamp ?? this.timestamp;\n this.enabled = options.enabled ?? this.enabled;\n }\n\n private format(msg: string): string {\n if (!this.timestamp) return msg;\n return `[${new Date().toISOString()}] ${msg}`;\n }\n\n private output(msg: string): void {\n if (!this.enabled) return;\n console.log(this.format(msg));\n }\n\n // Log Levels\n success(msg: string): void {\n this.output(`✅ ${msg}`);\n }\n\n info(msg: string): void {\n this.output(`ℹ️ ${msg}`);\n }\n\n warning(msg: string): void {\n this.output(`⚠️ ${msg}`);\n }\n\n danger(msg: string): void {\n this.output(`❌ ${msg}`);\n }\n\n log(msg: string): void {\n this.output(`• ${msg}`);\n }\n}\n\nexport const logger = new Logger();\n","import type { Request, Response, NextFunction } from \"express\";\n\nexport const authMiddleware = (\n _req: Request,\n _res: Response,\n next: NextFunction\n): void => {\n next(); // placeholder\n};\n","import type { Request, Response, NextFunction } from \"express\";\nimport type { Error as MongooseError } from \"mongoose\";\n\nimport { AppError } from \"../errors/appError.js\";\nimport { ApiResponse } from \"../api/apiResponse.js\";\n\n/* ======================================================\n MongoDB Error Handlers\n====================================================== */\n\nconst handleCastErrorDB = (err: MongooseError.CastError): AppError =>\n new AppError(`Invalid ${err.path}: ${err.value}`, 400, {\n code: \"INVALID_ID\",\n });\n\nconst handleDuplicateFieldsDB = (err: {\n keyValue?: Record<string, unknown>;\n}): AppError => {\n const value = err.keyValue ? JSON.stringify(err.keyValue) : \"duplicate value\";\n\n return new AppError(`Duplicate field value: ${value}`, 400, {\n code: \"DUPLICATE_FIELD\",\n });\n};\n\nconst handleValidationErrorDB = (\n err: MongooseError.ValidationError\n): AppError => {\n const errors = Object.values(err.errors).map((el) => el.message);\n\n return new AppError(\"Invalid input data\", 400, {\n code: \"VALIDATION_ERROR\",\n details: errors,\n });\n};\n\n/* ======================================================\n JWT Error Handlers\n====================================================== */\n\nconst handleJWTError = (): AppError =>\n new AppError(\"Invalid token. Please log in again.\", 401);\n\nconst handleJWTExpiredError = (): AppError =>\n new AppError(\"Your token has expired. Please log in again.\", 401);\n\n/* ======================================================\n Global Error Middleware\n====================================================== */\n\nexport const GlobalErrorHandler =\n (isProd = false) =>\n (\n err: unknown,\n _req: Request,\n res: Response,\n _next: NextFunction\n ): Response => {\n let error: AppError;\n\n /* --------------------\n Normalize error\n -------------------- */\n\n if (err instanceof AppError) {\n error = err;\n } else if (err instanceof Error) {\n error = new AppError(err.message, 500);\n error.isOperational = false;\n } else {\n error = new AppError(\"Internal Server Error\", 500);\n error.isOperational = false;\n }\n\n /* --------------------\n MongoDB errors\n -------------------- */\n\n if (err && (err as MongooseError).name === \"CastError\") {\n error = handleCastErrorDB(err as MongooseError.CastError);\n }\n\n if (\n err &&\n typeof err === \"object\" &&\n (err as { code?: number }).code === 11000\n ) {\n error = handleDuplicateFieldsDB(\n err as { keyValue?: Record<string, unknown> }\n );\n }\n\n if (err && (err as MongooseError).name === \"ValidationError\") {\n error = handleValidationErrorDB(err as MongooseError.ValidationError);\n }\n\n /* --------------------\n JWT errors\n -------------------- */\n\n if ((err as Error)?.name === \"JsonWebTokenError\") {\n error = handleJWTError();\n }\n\n if ((err as Error)?.name === \"TokenExpiredError\") {\n error = handleJWTExpiredError();\n }\n\n /* --------------------\n Logging\n -------------------- */\n\n if (!error.isOperational) {\n console.error({ err }, \"💣 Programming error\");\n } else {\n console.log({ message: error.message }, \"⚠️ Operational error\");\n }\n\n /* --------------------\n Response\n -------------------- */\n\n if (!isProd) {\n // DEV: detailed error\n return ApiResponse.send(res, {\n success: false,\n statusCode: error.statusCode,\n message: error.message,\n data: {\n stack: (err as Error)?.stack,\n details: error.details,\n },\n });\n }\n\n // PROD: safe error\n return ApiResponse.send(res, {\n success: false,\n statusCode: error.statusCode,\n message: error.isOperational ? error.message : \"Something went wrong\",\n });\n };\n","import type { Request, Response, NextFunction } from \"express\";\nimport type { Schema } from \"joi\";\n\nexport const validate =\n (schema: Schema) =>\n (req: Request, _res: Response, next: NextFunction): void => {\n const { error } = schema.validate(req.body);\n\n if (error) {\n return next(error);\n }\n\n next();\n };\n","import type { Model, UpdateQuery } from \"mongoose\";\nimport { AppError } from \"../errors/appError.js\";\n\n/**\n * Generic Base Service\n * Provides default CRUD operations\n */\nexport abstract class BaseService<T> {\n protected model: Model<T>;\n protected resourceName: string;\n\n constructor(model: Model<T>, resourceName: string) {\n this.model = model;\n this.resourceName = resourceName;\n }\n\n async create(data: Partial<T>): Promise<T> {\n const doc = await this.model.create(data as any);\n return doc as T;\n }\n\n async findAll(): Promise<T[]> {\n return this.model.find();\n }\n\n async findById(id: string): Promise<T> {\n const doc = await this.model.findById(id);\n\n if (!doc) {\n throw new AppError(`${this.resourceName} not found`, 404, {\n code: `${this.resourceName.toUpperCase()}_NOT_FOUND`,\n });\n }\n\n return doc;\n }\n\n async updateById(id: string, data: UpdateQuery<T>): Promise<T> {\n const doc = await this.model.findByIdAndUpdate(id, data, {\n new: true,\n runValidators: true,\n });\n\n if (!doc) {\n throw new AppError(`${this.resourceName} not found`, 404, {\n code: `${this.resourceName.toUpperCase()}_NOT_FOUND`,\n });\n }\n\n return doc;\n }\n\n async deleteById(id: string): Promise<T> {\n const doc = await this.model.findByIdAndDelete(id);\n\n if (!doc) {\n throw new AppError(`${this.resourceName} not found`, 404, {\n code: `${this.resourceName.toUpperCase()}_NOT_FOUND`,\n });\n }\n\n return doc;\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/errors/appError.ts","../src/api/apiResponse.ts","../src/api/asyncHandler.ts","../src/api/apiFeatures.ts","../src/api/apiFactory.ts","../src/utils/errorToString.ts","../src/configs/logger.ts","../src/configs/database.ts","../src/configs/env.ts","../src/middlewares/auth.middleware.ts","../src/middlewares/error.middleware.ts","../src/middlewares/validate.middleware.ts","../src/services/base.service.ts"],"names":["AppError","message","statusCode","options","ApiResponse","_ApiResponse","res","success","data","meta","body","asyncHandler","fn","req","next","APIFeatures","query","queryString","queryObj","el","queryStr","match","sortBy","fields","skip","populateOptions","apiFeatures_default","APIFactory","Model","filter","features","docs","doc","errorToString","error","Logger","msg","logger","connectMongoDB","config","mongoose","dotenv","loadEnv","schema","value","authMiddleware","_req","_res","handleCastErrorDB","err","handleDuplicateFieldsDB","handleValidationErrorDB","errors","handleJWTError","handleJWTExpiredError","GlobalErrorHandler","isProd","_next","validate","BaseService","model","resourceName","id"],"mappings":"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","file":"index.cjs","sourcesContent":["export class AppError extends Error {\n statusCode: number;\n status: string;\n isOperational: boolean;\n code?: string;\n details?: unknown;\n\n constructor(\n message: string,\n statusCode: number = 500,\n options?: { code?: string; details?: unknown }\n ) {\n super(message);\n\n this.statusCode = statusCode;\n this.status = `${statusCode}`.startsWith(\"4\") ? \"fail\" : \"error\";\n this.isOperational = true;\n this.code = options?.code;\n this.details = options?.details;\n\n Error.captureStackTrace(this, this.constructor);\n }\n}\n","import { Response } from \"express\";\n\nexport class ApiResponse {\n static send(\n res: Response,\n options: {\n statusCode?: number;\n success?: boolean;\n message?: string;\n data?: unknown;\n meta?: unknown;\n }\n ) {\n const {\n statusCode = 200,\n success = true,\n message = \"Success\",\n data,\n meta,\n } = options;\n\n if (statusCode === 204) {\n return res.status(204).send();\n }\n\n const body: Record<string, unknown> = { success, message };\n if (data !== undefined) body.data = data;\n if (meta !== undefined) body.meta = meta;\n\n return res.status(statusCode).json(body);\n }\n\n static ok(res: Response, data: unknown, message?: string, meta?: unknown) {\n return ApiResponse.send(res, { statusCode: 200, data, message, meta });\n }\n\n static created(res: Response, data: unknown, message?: string) {\n return ApiResponse.send(res, { statusCode: 201, data, message });\n }\n\n static noContent(res: Response) {\n return ApiResponse.send(res, { statusCode: 204 });\n }\n\n // ❌ Error response (optional use in controllers)\n static error(res: Response, message?: string, statusCode: number = 500) {\n return ApiResponse.send(res, {\n success: false,\n statusCode,\n message,\n });\n }\n}\n","import { Request, Response, NextFunction } from \"express\";\n\nexport const asyncHandler =\n (fn: (req: Request, res: Response, next: NextFunction) => Promise<void>) =>\n (req: Request, res: Response, next: NextFunction) =>\n Promise.resolve(fn(req, res, next)).catch(next);\n","import type { Query, PopulateOptions } from \"mongoose\";\n\n/**\n * Generic API Features helper for Mongoose queries\n */\nclass APIFeatures<T> {\n public query: Query<T[], T>;\n public queryString: Record<string, unknown>;\n public page: number;\n public limit: number;\n\n constructor(query: Query<T[], T>, queryString: Record<string, unknown>) {\n this.query = query;\n this.queryString = queryString;\n this.page = 1;\n this.limit = 100;\n }\n\n filter(): this {\n const queryObj = { ...this.queryString };\n\n [\"page\", \"sort\", \"limit\", \"fields\"].forEach((el) => delete queryObj[el]);\n\n let queryStr = JSON.stringify(queryObj);\n queryStr = queryStr.replace(\n /\\b(gte|gt|lte|lt|in)\\b/g,\n (match) => `$${match}`\n );\n\n this.query = this.query.find(JSON.parse(queryStr));\n\n return this;\n }\n\n sort(): this {\n if (this.queryString.sort) {\n const sortBy = String(this.queryString.sort).split(\",\").join(\" \");\n this.query = this.query.sort(sortBy);\n } else {\n this.query = this.query.sort(\"-createdAt\");\n }\n\n return this;\n }\n\n limitFields(): this {\n if (this.queryString.fields) {\n const fields = String(this.queryString.fields).split(\",\").join(\" \");\n this.query = this.query.select(fields);\n } else {\n this.query = this.query.select(\"-__v\");\n }\n\n return this;\n }\n\n paginate(): this {\n this.page = Number(this.queryString.page) || 1;\n this.limit = Number(this.queryString.limit) || 100;\n\n const skip = (this.page - 1) * this.limit;\n\n this.query = this.query.skip(skip).limit(this.limit);\n\n return this;\n }\n\n populate(populateOptions?: PopulateOptions | PopulateOptions[]): this {\n if (populateOptions) {\n this.query = this.query.populate(populateOptions);\n }\n\n return this;\n }\n}\n\nexport default APIFeatures;\n","import type { Request } from \"express\";\nimport type { Model, PopulateOptions } from \"mongoose\";\n\nimport { AppError } from \"../errors/appError.js\";\nimport { ApiResponse } from \"./apiResponse.js\";\nimport { asyncHandler } from \"./asyncHandler.js\";\nimport APIFeatures from \"./apiFeatures.js\";\n\n/**\n * Options for API factory methods\n */\ninterface ApiFactoryOptions<T = unknown> {\n message?: string;\n notFoundMessage?: string;\n populate?: PopulateOptions | PopulateOptions[];\n filter?: (req: Request) => Record<string, unknown>;\n}\n\n/**\n * Generic API Factory (Hybrid Pattern)\n * For simple CRUD operations without business logic\n */\nexport class APIFactory {\n static getAll<T>(Model: Model<T>, options: ApiFactoryOptions<T> = {}) {\n return asyncHandler(async (req, res) => {\n const filter = options.filter ? options.filter(req) : {};\n\n const features = new APIFeatures(Model.find(filter), req.query)\n .filter()\n .sort()\n .limitFields()\n .paginate()\n .populate(options.populate);\n\n const docs = await features.query;\n\n ApiResponse.ok(\n res,\n docs,\n options.message ?? \"Records fetched successfully\",\n {\n count: docs.length,\n page: features.page,\n limit: features.limit,\n }\n );\n });\n }\n\n static getOne<T>(Model: Model<T>, options: ApiFactoryOptions<T> = {}) {\n return asyncHandler(async (req, res) => {\n let query = Model.findById(req.params.id);\n\n if (options.populate) {\n query = query.populate(\n options.populate as PopulateOptions | PopulateOptions[]\n );\n }\n\n const doc = await query;\n\n if (!doc) {\n throw new AppError(\n options.notFoundMessage ?? \"Resource not found\",\n 404,\n { code: \"RESOURCE_NOT_FOUND\" }\n );\n }\n\n ApiResponse.ok(res, doc);\n });\n }\n\n static createOne<T>(Model: Model<T>, options: ApiFactoryOptions<T> = {}) {\n return asyncHandler(async (req, res) => {\n const doc = await Model.create(req.body);\n\n ApiResponse.created(\n res,\n doc,\n options.message ?? \"Resource created successfully\"\n );\n });\n }\n\n static updateOne<T>(Model: Model<T>, options: ApiFactoryOptions<T> = {}) {\n return asyncHandler(async (req, res) => {\n const doc = await Model.findByIdAndUpdate(\n req.params.id,\n req.body as Partial<T>,\n {\n new: true,\n runValidators: true,\n }\n );\n\n if (!doc) {\n throw new AppError(\n options.notFoundMessage ?? \"Resource not found\",\n 404\n );\n }\n\n ApiResponse.ok(\n res,\n doc,\n options.message ?? \"Resource updated successfully\"\n );\n });\n }\n\n static deleteOne<T>(Model: Model<T>, options: ApiFactoryOptions<T> = {}) {\n return asyncHandler(async (req, res) => {\n const doc = await Model.findByIdAndDelete(req.params.id);\n\n if (!doc) {\n throw new AppError(\n options.notFoundMessage ?? \"Resource not found\",\n 404\n );\n }\n\n ApiResponse.noContent(res);\n });\n }\n}\n","export function errorToString(error: unknown): string {\n if (error instanceof Error) {\n return `${error.name}: ${error.message}\\n${error.stack ?? \"\"}`;\n }\n\n if (typeof error === \"object\") {\n try {\n return JSON.stringify(error, null, 2);\n } catch {\n return \"[Unserializable object]\";\n }\n }\n\n return String(error);\n}\n","import { errorToString } from \"../utils/errorToString.js\";\n\nexport interface LoggerOptions {\n timestamp?: boolean;\n enabled?: boolean;\n}\n\nclass Logger {\n private timestamp = true;\n private enabled = true;\n\n // Use to configure once at the root\n configure(options: LoggerOptions = {}) {\n this.timestamp = options.timestamp ?? this.timestamp;\n this.enabled = options.enabled ?? this.enabled;\n }\n\n private format(msg: string): string {\n if (!this.timestamp) return msg;\n return `[${new Date().toISOString()}] ${msg}`;\n }\n\n private output(msg: string): void {\n if (!this.enabled) return;\n console.log(this.format(msg));\n }\n\n // Log Levels\n success(msg: string): void {\n this.output(`✅ ${msg}`);\n }\n\n info(msg: string): void {\n this.output(`ℹ️ ${msg}`);\n }\n\n warning(msg: string): void {\n this.output(`⚠️ ${msg}`);\n }\n\n danger(message: string, error?: unknown): void {\n if (error) {\n this.output(`❌ ${message}\\n${errorToString(error)}`);\n } else {\n this.output(`❌ ${message}`);\n }\n }\n\n log(msg: string): void {\n this.output(`• ${msg}`);\n }\n}\n\nexport const logger = new Logger();\n","import mongoose from \"mongoose\";\nimport { logger } from \"./logger.js\";\n\nexport interface DatabaseConfig {\n uri: string;\n}\n\nexport async function connectMongoDB(config: DatabaseConfig): Promise<void> {\n try {\n mongoose.set(\"strictQuery\", true);\n\n await mongoose.connect(config.uri);\n\n logger.success(\"✅ Database connected successfully\");\n\n process.on(\"SIGINT\", async () => {\n await mongoose.connection.close();\n logger.success(\"✅ Database connection closed\");\n process.exit(0);\n });\n } catch (error: unknown) {\n logger.danger(\"❌ Database connection failed\");\n\n if (error instanceof Error) {\n logger.danger(error.message);\n }\n\n process.exit(1);\n }\n}\n","import dotenv from \"dotenv\";\nimport type Joi from \"joi\";\n\ndotenv.config();\n\n/**\n * Generic env loader for Elseware apps\n * App provides its own Joi schema\n */\nexport function loadEnv<T>(schema: Joi.ObjectSchema<T>): T {\n const { value, error } = schema.validate(process.env, {\n abortEarly: false,\n allowUnknown: true,\n });\n\n if (error) {\n throw new Error(`❌ Env validation error: ${error.message}`);\n }\n\n return value;\n}\n","import type { Request, Response, NextFunction } from \"express\";\n\nexport const authMiddleware = (\n _req: Request,\n _res: Response,\n next: NextFunction\n): void => {\n next(); // placeholder\n};\n","import type { Request, Response, NextFunction } from \"express\";\nimport type { Error as MongooseError } from \"mongoose\";\n\nimport { AppError } from \"../errors/appError.js\";\nimport { ApiResponse } from \"../api/apiResponse.js\";\nimport { logger } from \"../configs/logger.js\";\n\n/* ======================================================\n MongoDB Error Handlers\n====================================================== */\n\nconst handleCastErrorDB = (err: MongooseError.CastError): AppError =>\n new AppError(`Invalid ${err.path}: ${err.value}`, 400, {\n code: \"INVALID_ID\",\n });\n\nconst handleDuplicateFieldsDB = (err: {\n keyValue?: Record<string, unknown>;\n}): AppError => {\n const value = err.keyValue ? JSON.stringify(err.keyValue) : \"duplicate value\";\n\n return new AppError(`Duplicate field value: ${value}`, 400, {\n code: \"DUPLICATE_FIELD\",\n });\n};\n\nconst handleValidationErrorDB = (\n err: MongooseError.ValidationError\n): AppError => {\n const errors = Object.values(err.errors).map((el) => el.message);\n\n return new AppError(\"Invalid input data\", 400, {\n code: \"VALIDATION_ERROR\",\n details: errors,\n });\n};\n\n/* ======================================================\n JWT Error Handlers\n====================================================== */\n\nconst handleJWTError = (): AppError =>\n new AppError(\"Invalid token. Please log in again.\", 401);\n\nconst handleJWTExpiredError = (): AppError =>\n new AppError(\"Your token has expired. Please log in again.\", 401);\n\n/* ======================================================\n Global Error Middleware\n====================================================== */\n\nexport const GlobalErrorHandler =\n (isProd = false) =>\n (\n err: unknown,\n _req: Request,\n res: Response,\n _next: NextFunction\n ): Response => {\n let error: AppError;\n\n /* --------------------\n Normalize error\n -------------------- */\n\n if (err instanceof AppError) {\n error = err;\n } else if (err instanceof Error) {\n error = new AppError(err.message, 500);\n error.isOperational = false;\n } else {\n error = new AppError(\"Internal Server Error\", 500);\n error.isOperational = false;\n }\n\n /* --------------------\n MongoDB errors\n -------------------- */\n\n if (err && (err as MongooseError).name === \"CastError\") {\n error = handleCastErrorDB(err as MongooseError.CastError);\n }\n\n if (\n err &&\n typeof err === \"object\" &&\n (err as { code?: number }).code === 11000\n ) {\n error = handleDuplicateFieldsDB(\n err as { keyValue?: Record<string, unknown> }\n );\n }\n\n if (err && (err as MongooseError).name === \"ValidationError\") {\n error = handleValidationErrorDB(err as MongooseError.ValidationError);\n }\n\n /* --------------------\n JWT errors\n -------------------- */\n\n if ((err as Error)?.name === \"JsonWebTokenError\") {\n error = handleJWTError();\n }\n\n if ((err as Error)?.name === \"TokenExpiredError\") {\n error = handleJWTExpiredError();\n }\n\n /* --------------------\n Logging\n -------------------- */\n\n if (!error.isOperational) {\n logger.danger(\"Programming error\", err);\n } else {\n logger.danger(\"Operational error\", { message: error.message });\n }\n\n /* --------------------\n Response\n -------------------- */\n\n if (!isProd) {\n // DEV: detailed error\n return ApiResponse.send(res, {\n success: false,\n statusCode: error.statusCode,\n message: error.message,\n data: {\n stack: (err as Error)?.stack,\n details: error.details,\n },\n });\n }\n\n // PROD: safe error\n return ApiResponse.send(res, {\n success: false,\n statusCode: error.statusCode,\n message: error.isOperational ? error.message : \"Something went wrong\",\n });\n };\n","import type { Request, Response, NextFunction } from \"express\";\nimport type { Schema } from \"joi\";\n\nexport const validate =\n (schema: Schema) =>\n (req: Request, _res: Response, next: NextFunction): void => {\n const { error } = schema.validate(req.body);\n\n if (error) {\n return next(error);\n }\n\n next();\n };\n","import type { Model, UpdateQuery } from \"mongoose\";\nimport { AppError } from \"../errors/appError.js\";\n\n/**\n * Generic Base Service\n * Provides default CRUD operations\n */\nexport abstract class BaseService<T> {\n protected model: Model<T>;\n protected resourceName: string;\n\n constructor(model: Model<T>, resourceName: string) {\n this.model = model;\n this.resourceName = resourceName;\n }\n\n async create(data: Partial<T>): Promise<T> {\n const doc = await this.model.create(data as any);\n return doc as T;\n }\n\n async findAll(): Promise<T[]> {\n return this.model.find();\n }\n\n async findById(id: string): Promise<T> {\n const doc = await this.model.findById(id);\n\n if (!doc) {\n throw new AppError(`${this.resourceName} not found`, 404, {\n code: `${this.resourceName.toUpperCase()}_NOT_FOUND`,\n });\n }\n\n return doc;\n }\n\n async updateById(id: string, data: UpdateQuery<T>): Promise<T> {\n const doc = await this.model.findByIdAndUpdate(id, data, {\n new: true,\n runValidators: true,\n });\n\n if (!doc) {\n throw new AppError(`${this.resourceName} not found`, 404, {\n code: `${this.resourceName.toUpperCase()}_NOT_FOUND`,\n });\n }\n\n return doc;\n }\n\n async deleteById(id: string): Promise<T> {\n const doc = await this.model.findByIdAndDelete(id);\n\n if (!doc) {\n throw new AppError(`${this.resourceName} not found`, 404, {\n code: `${this.resourceName.toUpperCase()}_NOT_FOUND`,\n });\n }\n\n return doc;\n }\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -64,7 +64,7 @@ declare class Logger {
64
64
  success(msg: string): void;
65
65
  info(msg: string): void;
66
66
  warning(msg: string): void;
67
- danger(msg: string): void;
67
+ danger(message: string, error?: unknown): void;
68
68
  log(msg: string): void;
69
69
  }
70
70
  declare const logger: Logger;
package/dist/index.d.ts CHANGED
@@ -64,7 +64,7 @@ declare class Logger {
64
64
  success(msg: string): void;
65
65
  info(msg: string): void;
66
66
  warning(msg: string): void;
67
- danger(msg: string): void;
67
+ danger(message: string, error?: unknown): void;
68
68
  log(msg: string): void;
69
69
  }
70
70
  declare const logger: Logger;
package/dist/index.js CHANGED
@@ -1,2 +1,4 @@
1
- import m from'mongoose';import w from'dotenv';var n=class extends Error{statusCode;status;isOperational;code;details;constructor(e,t=500,o){super(e),this.statusCode=t,this.status=`${t}`.startsWith("4")?"fail":"error",this.isOperational=true,this.code=o?.code,this.details=o?.details,Error.captureStackTrace(this,this.constructor);}};var a=class s{static send(e,t){let{statusCode:o=200,success:i=true,message:r="Success",data:u,meta:p}=t;if(o===204)return e.status(204).send();let d={success:i,message:r};return u!==void 0&&(d.data=u),p!==void 0&&(d.meta=p),e.status(o).json(d)}static ok(e,t,o,i){return s.send(e,{statusCode:200,data:t,message:o,meta:i})}static created(e,t,o){return s.send(e,{statusCode:201,data:t,message:o})}static noContent(e){return s.send(e,{statusCode:204})}static error(e,t,o=500){return s.send(e,{success:false,statusCode:o,message:t})}};var c=s=>(e,t,o)=>Promise.resolve(s(e,t,o)).catch(o);var l=class{query;queryString;page;limit;constructor(e,t){this.query=e,this.queryString=t,this.page=1,this.limit=100;}filter(){let e={...this.queryString};["page","sort","limit","fields"].forEach(o=>delete e[o]);let t=JSON.stringify(e);return t=t.replace(/\b(gte|gt|lte|lt|in)\b/g,o=>`$${o}`),this.query=this.query.find(JSON.parse(t)),this}sort(){if(this.queryString.sort){let e=String(this.queryString.sort).split(",").join(" ");this.query=this.query.sort(e);}else this.query=this.query.sort("-createdAt");return this}limitFields(){if(this.queryString.fields){let e=String(this.queryString.fields).split(",").join(" ");this.query=this.query.select(e);}else this.query=this.query.select("-__v");return this}paginate(){this.page=Number(this.queryString.page)||1,this.limit=Number(this.queryString.limit)||100;let e=(this.page-1)*this.limit;return this.query=this.query.skip(e).limit(this.limit),this}populate(e){return e&&(this.query=this.query.populate(e)),this}},g=l;var h=class{static getAll(e,t={}){return c(async(o,i)=>{let r=t.filter?t.filter(o):{},u=new g(e.find(r),o.query).filter().sort().limitFields().paginate().populate(t.populate),p=await u.query;a.ok(i,p,t.message??"Records fetched successfully",{count:p.length,page:u.page,limit:u.limit});})}static getOne(e,t={}){return c(async(o,i)=>{let r=e.findById(o.params.id);t.populate&&(r=r.populate(t.populate));let u=await r;if(!u)throw new n(t.notFoundMessage??"Resource not found",404,{code:"RESOURCE_NOT_FOUND"});a.ok(i,u);})}static createOne(e,t={}){return c(async(o,i)=>{let r=await e.create(o.body);a.created(i,r,t.message??"Resource created successfully");})}static updateOne(e,t={}){return c(async(o,i)=>{let r=await e.findByIdAndUpdate(o.params.id,o.body,{new:true,runValidators:true});if(!r)throw new n(t.notFoundMessage??"Resource not found",404);a.ok(i,r,t.message??"Resource updated successfully");})}static deleteOne(e,t={}){return c(async(o,i)=>{if(!await e.findByIdAndDelete(o.params.id))throw new n(t.notFoundMessage??"Resource not found",404);a.noContent(i);})}};async function _(s){try{m.set("strictQuery",!0),await m.connect(s.uri),console.log("\u2705 Database connected successfully"),process.on("SIGINT",async()=>{await m.connection.close(),console.log("\u2705 Database connection closed"),process.exit(0);});}catch(e){console.error("\u274C Database connection failed"),e instanceof Error&&console.error(e.message),process.exit(1);}}w.config();function j(s){let{value:e,error:t}=s.validate(process.env,{abortEarly:false,allowUnknown:true});if(t)throw new Error(`\u274C Env validation error: ${t.message}`);return e}var f=class{timestamp=true;enabled=true;configure(e={}){this.timestamp=e.timestamp??this.timestamp,this.enabled=e.enabled??this.enabled;}format(e){return this.timestamp?`[${new Date().toISOString()}] ${e}`:e}output(e){this.enabled&&console.log(this.format(e));}success(e){this.output(`\u2705 ${e}`);}info(e){this.output(`\u2139\uFE0F ${e}`);}warning(e){this.output(`\u26A0\uFE0F ${e}`);}danger(e){this.output(`\u274C ${e}`);}log(e){this.output(`\u2022 ${e}`);}},Q=new f;var X=(s,e,t)=>{t();};var x=s=>new n(`Invalid ${s.path}: ${s.value}`,400,{code:"INVALID_ID"}),E=s=>{let e=s.keyValue?JSON.stringify(s.keyValue):"duplicate value";return new n(`Duplicate field value: ${e}`,400,{code:"DUPLICATE_FIELD"})},T=s=>{let e=Object.values(s.errors).map(t=>t.message);return new n("Invalid input data",400,{code:"VALIDATION_ERROR",details:e})},R=()=>new n("Invalid token. Please log in again.",401),O=()=>new n("Your token has expired. Please log in again.",401),oe=(s=false)=>(e,t,o,i)=>{let r;return e instanceof n?r=e:e instanceof Error?(r=new n(e.message,500),r.isOperational=false):(r=new n("Internal Server Error",500),r.isOperational=false),e&&e.name==="CastError"&&(r=x(e)),e&&typeof e=="object"&&e.code===11e3&&(r=E(e)),e&&e.name==="ValidationError"&&(r=T(e)),e?.name==="JsonWebTokenError"&&(r=R()),e?.name==="TokenExpiredError"&&(r=O()),r.isOperational?console.log({message:r.message},"\u26A0\uFE0F Operational error"):console.error({err:e},"\u{1F4A3} Programming error"),s?a.send(o,{success:false,statusCode:r.statusCode,message:r.isOperational?r.message:"Something went wrong"}):a.send(o,{success:false,statusCode:r.statusCode,message:r.message,data:{stack:e?.stack,details:r.details}})};var se=s=>(e,t,o)=>{let{error:i}=s.validate(e.body);if(i)return o(i);o();};var y=class{model;resourceName;constructor(e,t){this.model=e,this.resourceName=t;}async create(e){return await this.model.create(e)}async findAll(){return this.model.find()}async findById(e){let t=await this.model.findById(e);if(!t)throw new n(`${this.resourceName} not found`,404,{code:`${this.resourceName.toUpperCase()}_NOT_FOUND`});return t}async updateById(e,t){let o=await this.model.findByIdAndUpdate(e,t,{new:true,runValidators:true});if(!o)throw new n(`${this.resourceName} not found`,404,{code:`${this.resourceName.toUpperCase()}_NOT_FOUND`});return o}async deleteById(e){let t=await this.model.findByIdAndDelete(e);if(!t)throw new n(`${this.resourceName} not found`,404,{code:`${this.resourceName.toUpperCase()}_NOT_FOUND`});return t}};export{h as APIFactory,a as ApiResponse,n as AppError,y as BaseService,oe as GlobalErrorHandler,c as asyncHandler,X as authMiddleware,_ as connectMongoDB,j as loadEnv,Q as logger,se as validate};//# sourceMappingURL=index.js.map
1
+ import g from'mongoose';import E from'dotenv';var n=class extends Error{statusCode;status;isOperational;code;details;constructor(e,t=500,r){super(e),this.statusCode=t,this.status=`${t}`.startsWith("4")?"fail":"error",this.isOperational=true,this.code=r?.code,this.details=r?.details,Error.captureStackTrace(this,this.constructor);}};var a=class o{static send(e,t){let{statusCode:r=200,success:i=true,message:s="Success",data:u,meta:d}=t;if(r===204)return e.status(204).send();let l={success:i,message:s};return u!==void 0&&(l.data=u),d!==void 0&&(l.meta=d),e.status(r).json(l)}static ok(e,t,r,i){return o.send(e,{statusCode:200,data:t,message:r,meta:i})}static created(e,t,r){return o.send(e,{statusCode:201,data:t,message:r})}static noContent(e){return o.send(e,{statusCode:204})}static error(e,t,r=500){return o.send(e,{success:false,statusCode:r,message:t})}};var p=o=>(e,t,r)=>Promise.resolve(o(e,t,r)).catch(r);var m=class{query;queryString;page;limit;constructor(e,t){this.query=e,this.queryString=t,this.page=1,this.limit=100;}filter(){let e={...this.queryString};["page","sort","limit","fields"].forEach(r=>delete e[r]);let t=JSON.stringify(e);return t=t.replace(/\b(gte|gt|lte|lt|in)\b/g,r=>`$${r}`),this.query=this.query.find(JSON.parse(t)),this}sort(){if(this.queryString.sort){let e=String(this.queryString.sort).split(",").join(" ");this.query=this.query.sort(e);}else this.query=this.query.sort("-createdAt");return this}limitFields(){if(this.queryString.fields){let e=String(this.queryString.fields).split(",").join(" ");this.query=this.query.select(e);}else this.query=this.query.select("-__v");return this}paginate(){this.page=Number(this.queryString.page)||1,this.limit=Number(this.queryString.limit)||100;let e=(this.page-1)*this.limit;return this.query=this.query.skip(e).limit(this.limit),this}populate(e){return e&&(this.query=this.query.populate(e)),this}},h=m;var y=class{static getAll(e,t={}){return p(async(r,i)=>{let s=t.filter?t.filter(r):{},u=new h(e.find(s),r.query).filter().sort().limitFields().paginate().populate(t.populate),d=await u.query;a.ok(i,d,t.message??"Records fetched successfully",{count:d.length,page:u.page,limit:u.limit});})}static getOne(e,t={}){return p(async(r,i)=>{let s=e.findById(r.params.id);t.populate&&(s=s.populate(t.populate));let u=await s;if(!u)throw new n(t.notFoundMessage??"Resource not found",404,{code:"RESOURCE_NOT_FOUND"});a.ok(i,u);})}static createOne(e,t={}){return p(async(r,i)=>{let s=await e.create(r.body);a.created(i,s,t.message??"Resource created successfully");})}static updateOne(e,t={}){return p(async(r,i)=>{let s=await e.findByIdAndUpdate(r.params.id,r.body,{new:true,runValidators:true});if(!s)throw new n(t.notFoundMessage??"Resource not found",404);a.ok(i,s,t.message??"Resource updated successfully");})}static deleteOne(e,t={}){return p(async(r,i)=>{if(!await e.findByIdAndDelete(r.params.id))throw new n(t.notFoundMessage??"Resource not found",404);a.noContent(i);})}};function w(o){if(o instanceof Error)return `${o.name}: ${o.message}
2
+ ${o.stack??""}`;if(typeof o=="object")try{return JSON.stringify(o,null,2)}catch{return "[Unserializable object]"}return String(o)}var f=class{timestamp=true;enabled=true;configure(e={}){this.timestamp=e.timestamp??this.timestamp,this.enabled=e.enabled??this.enabled;}format(e){return this.timestamp?`[${new Date().toISOString()}] ${e}`:e}output(e){this.enabled&&console.log(this.format(e));}success(e){this.output(`\u2705 ${e}`);}info(e){this.output(`\u2139\uFE0F ${e}`);}warning(e){this.output(`\u26A0\uFE0F ${e}`);}danger(e,t){t?this.output(`\u274C ${e}
3
+ ${w(t)}`):this.output(`\u274C ${e}`);}log(e){this.output(`\u2022 ${e}`);}},c=new f;async function L(o){try{g.set("strictQuery",!0),await g.connect(o.uri),c.success("\u2705 Database connected successfully"),process.on("SIGINT",async()=>{await g.connection.close(),c.success("\u2705 Database connection closed"),process.exit(0);});}catch(e){c.danger("\u274C Database connection failed"),e instanceof Error&&c.danger(e.message),process.exit(1);}}E.config();function G(o){let{value:e,error:t}=o.validate(process.env,{abortEarly:false,allowUnknown:true});if(t)throw new Error(`\u274C Env validation error: ${t.message}`);return e}var re=(o,e,t)=>{t();};var T=o=>new n(`Invalid ${o.path}: ${o.value}`,400,{code:"INVALID_ID"}),b=o=>{let e=o.keyValue?JSON.stringify(o.keyValue):"duplicate value";return new n(`Duplicate field value: ${e}`,400,{code:"DUPLICATE_FIELD"})},O=o=>{let e=Object.values(o.errors).map(t=>t.message);return new n("Invalid input data",400,{code:"VALIDATION_ERROR",details:e})},R=()=>new n("Invalid token. Please log in again.",401),q=()=>new n("Your token has expired. Please log in again.",401),ae=(o=false)=>(e,t,r,i)=>{let s;return e instanceof n?s=e:e instanceof Error?(s=new n(e.message,500),s.isOperational=false):(s=new n("Internal Server Error",500),s.isOperational=false),e&&e.name==="CastError"&&(s=T(e)),e&&typeof e=="object"&&e.code===11e3&&(s=b(e)),e&&e.name==="ValidationError"&&(s=O(e)),e?.name==="JsonWebTokenError"&&(s=R()),e?.name==="TokenExpiredError"&&(s=q()),s.isOperational?c.danger("Operational error",{message:s.message}):c.danger("Programming error",e),o?a.send(r,{success:false,statusCode:s.statusCode,message:s.isOperational?s.message:"Something went wrong"}):a.send(r,{success:false,statusCode:s.statusCode,message:s.message,data:{stack:e?.stack,details:s.details}})};var ce=o=>(e,t,r)=>{let{error:i}=o.validate(e.body);if(i)return r(i);r();};var x=class{model;resourceName;constructor(e,t){this.model=e,this.resourceName=t;}async create(e){return await this.model.create(e)}async findAll(){return this.model.find()}async findById(e){let t=await this.model.findById(e);if(!t)throw new n(`${this.resourceName} not found`,404,{code:`${this.resourceName.toUpperCase()}_NOT_FOUND`});return t}async updateById(e,t){let r=await this.model.findByIdAndUpdate(e,t,{new:true,runValidators:true});if(!r)throw new n(`${this.resourceName} not found`,404,{code:`${this.resourceName.toUpperCase()}_NOT_FOUND`});return r}async deleteById(e){let t=await this.model.findByIdAndDelete(e);if(!t)throw new n(`${this.resourceName} not found`,404,{code:`${this.resourceName.toUpperCase()}_NOT_FOUND`});return t}};export{y as APIFactory,a as ApiResponse,n as AppError,x as BaseService,ae as GlobalErrorHandler,p as asyncHandler,re as authMiddleware,L as connectMongoDB,G as loadEnv,c as logger,ce as validate};//# sourceMappingURL=index.js.map
2
4
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors/appError.ts","../src/api/apiResponse.ts","../src/api/asyncHandler.ts","../src/api/apiFeatures.ts","../src/api/apiFactory.ts","../src/configs/database.ts","../src/configs/env.ts","../src/configs/logger.ts","../src/middlewares/auth.middleware.ts","../src/middlewares/error.middleware.ts","../src/middlewares/validate.middleware.ts","../src/services/base.service.ts"],"names":["AppError","message","statusCode","options","ApiResponse","_ApiResponse","res","success","data","meta","body","asyncHandler","fn","req","next","APIFeatures","query","queryString","queryObj","el","queryStr","match","sortBy","fields","skip","populateOptions","apiFeatures_default","APIFactory","Model","filter","features","docs","doc","connectMongoDB","config","mongoose","error","dotenv","loadEnv","schema","value","Logger","msg","logger","authMiddleware","_req","_res","handleCastErrorDB","err","handleDuplicateFieldsDB","handleValidationErrorDB","errors","handleJWTError","handleJWTExpiredError","GlobalErrorHandler","isProd","_next","validate","BaseService","model","resourceName","id"],"mappings":"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","file":"index.js","sourcesContent":["export class AppError extends Error {\n statusCode: number;\n status: string;\n isOperational: boolean;\n code?: string;\n details?: unknown;\n\n constructor(\n message: string,\n statusCode: number = 500,\n options?: { code?: string; details?: unknown }\n ) {\n super(message);\n\n this.statusCode = statusCode;\n this.status = `${statusCode}`.startsWith(\"4\") ? \"fail\" : \"error\";\n this.isOperational = true;\n this.code = options?.code;\n this.details = options?.details;\n\n Error.captureStackTrace(this, this.constructor);\n }\n}\n","import { Response } from \"express\";\n\nexport class ApiResponse {\n static send(\n res: Response,\n options: {\n statusCode?: number;\n success?: boolean;\n message?: string;\n data?: unknown;\n meta?: unknown;\n }\n ) {\n const {\n statusCode = 200,\n success = true,\n message = \"Success\",\n data,\n meta,\n } = options;\n\n if (statusCode === 204) {\n return res.status(204).send();\n }\n\n const body: Record<string, unknown> = { success, message };\n if (data !== undefined) body.data = data;\n if (meta !== undefined) body.meta = meta;\n\n return res.status(statusCode).json(body);\n }\n\n static ok(res: Response, data: unknown, message?: string, meta?: unknown) {\n return ApiResponse.send(res, { statusCode: 200, data, message, meta });\n }\n\n static created(res: Response, data: unknown, message?: string) {\n return ApiResponse.send(res, { statusCode: 201, data, message });\n }\n\n static noContent(res: Response) {\n return ApiResponse.send(res, { statusCode: 204 });\n }\n\n // ❌ Error response (optional use in controllers)\n static error(res: Response, message?: string, statusCode: number = 500) {\n return ApiResponse.send(res, {\n success: false,\n statusCode,\n message,\n });\n }\n}\n","import { Request, Response, NextFunction } from \"express\";\n\nexport const asyncHandler =\n (fn: (req: Request, res: Response, next: NextFunction) => Promise<void>) =>\n (req: Request, res: Response, next: NextFunction) =>\n Promise.resolve(fn(req, res, next)).catch(next);\n","import type { Query, PopulateOptions } from \"mongoose\";\n\n/**\n * Generic API Features helper for Mongoose queries\n */\nclass APIFeatures<T> {\n public query: Query<T[], T>;\n public queryString: Record<string, unknown>;\n public page: number;\n public limit: number;\n\n constructor(query: Query<T[], T>, queryString: Record<string, unknown>) {\n this.query = query;\n this.queryString = queryString;\n this.page = 1;\n this.limit = 100;\n }\n\n filter(): this {\n const queryObj = { ...this.queryString };\n\n [\"page\", \"sort\", \"limit\", \"fields\"].forEach((el) => delete queryObj[el]);\n\n let queryStr = JSON.stringify(queryObj);\n queryStr = queryStr.replace(\n /\\b(gte|gt|lte|lt|in)\\b/g,\n (match) => `$${match}`\n );\n\n this.query = this.query.find(JSON.parse(queryStr));\n\n return this;\n }\n\n sort(): this {\n if (this.queryString.sort) {\n const sortBy = String(this.queryString.sort).split(\",\").join(\" \");\n this.query = this.query.sort(sortBy);\n } else {\n this.query = this.query.sort(\"-createdAt\");\n }\n\n return this;\n }\n\n limitFields(): this {\n if (this.queryString.fields) {\n const fields = String(this.queryString.fields).split(\",\").join(\" \");\n this.query = this.query.select(fields);\n } else {\n this.query = this.query.select(\"-__v\");\n }\n\n return this;\n }\n\n paginate(): this {\n this.page = Number(this.queryString.page) || 1;\n this.limit = Number(this.queryString.limit) || 100;\n\n const skip = (this.page - 1) * this.limit;\n\n this.query = this.query.skip(skip).limit(this.limit);\n\n return this;\n }\n\n populate(populateOptions?: PopulateOptions | PopulateOptions[]): this {\n if (populateOptions) {\n this.query = this.query.populate(populateOptions);\n }\n\n return this;\n }\n}\n\nexport default APIFeatures;\n","import type { Request } from \"express\";\nimport type { Model, PopulateOptions } from \"mongoose\";\n\nimport { AppError } from \"../errors/appError.js\";\nimport { ApiResponse } from \"./apiResponse.js\";\nimport { asyncHandler } from \"./asyncHandler.js\";\nimport APIFeatures from \"./apiFeatures.js\";\n\n/**\n * Options for API factory methods\n */\ninterface ApiFactoryOptions<T = unknown> {\n message?: string;\n notFoundMessage?: string;\n populate?: PopulateOptions | PopulateOptions[];\n filter?: (req: Request) => Record<string, unknown>;\n}\n\n/**\n * Generic API Factory (Hybrid Pattern)\n * For simple CRUD operations without business logic\n */\nexport class APIFactory {\n static getAll<T>(Model: Model<T>, options: ApiFactoryOptions<T> = {}) {\n return asyncHandler(async (req, res) => {\n const filter = options.filter ? options.filter(req) : {};\n\n const features = new APIFeatures(Model.find(filter), req.query)\n .filter()\n .sort()\n .limitFields()\n .paginate()\n .populate(options.populate);\n\n const docs = await features.query;\n\n ApiResponse.ok(\n res,\n docs,\n options.message ?? \"Records fetched successfully\",\n {\n count: docs.length,\n page: features.page,\n limit: features.limit,\n }\n );\n });\n }\n\n static getOne<T>(Model: Model<T>, options: ApiFactoryOptions<T> = {}) {\n return asyncHandler(async (req, res) => {\n let query = Model.findById(req.params.id);\n\n if (options.populate) {\n query = query.populate(\n options.populate as PopulateOptions | PopulateOptions[]\n );\n }\n\n const doc = await query;\n\n if (!doc) {\n throw new AppError(\n options.notFoundMessage ?? \"Resource not found\",\n 404,\n { code: \"RESOURCE_NOT_FOUND\" }\n );\n }\n\n ApiResponse.ok(res, doc);\n });\n }\n\n static createOne<T>(Model: Model<T>, options: ApiFactoryOptions<T> = {}) {\n return asyncHandler(async (req, res) => {\n const doc = await Model.create(req.body);\n\n ApiResponse.created(\n res,\n doc,\n options.message ?? \"Resource created successfully\"\n );\n });\n }\n\n static updateOne<T>(Model: Model<T>, options: ApiFactoryOptions<T> = {}) {\n return asyncHandler(async (req, res) => {\n const doc = await Model.findByIdAndUpdate(\n req.params.id,\n req.body as Partial<T>,\n {\n new: true,\n runValidators: true,\n }\n );\n\n if (!doc) {\n throw new AppError(\n options.notFoundMessage ?? \"Resource not found\",\n 404\n );\n }\n\n ApiResponse.ok(\n res,\n doc,\n options.message ?? \"Resource updated successfully\"\n );\n });\n }\n\n static deleteOne<T>(Model: Model<T>, options: ApiFactoryOptions<T> = {}) {\n return asyncHandler(async (req, res) => {\n const doc = await Model.findByIdAndDelete(req.params.id);\n\n if (!doc) {\n throw new AppError(\n options.notFoundMessage ?? \"Resource not found\",\n 404\n );\n }\n\n ApiResponse.noContent(res);\n });\n }\n}\n","import mongoose from \"mongoose\";\n\nexport interface DatabaseConfig {\n uri: string;\n}\n\nexport async function connectMongoDB(config: DatabaseConfig): Promise<void> {\n try {\n mongoose.set(\"strictQuery\", true);\n\n await mongoose.connect(config.uri);\n\n console.log(\"✅ Database connected successfully\");\n\n process.on(\"SIGINT\", async () => {\n await mongoose.connection.close();\n console.log(\"✅ Database connection closed\");\n process.exit(0);\n });\n } catch (error: unknown) {\n console.error(\"❌ Database connection failed\");\n\n if (error instanceof Error) {\n console.error(error.message);\n }\n\n process.exit(1);\n }\n}\n","import dotenv from \"dotenv\";\nimport type Joi from \"joi\";\n\ndotenv.config();\n\n/**\n * Generic env loader for Elseware apps\n * App provides its own Joi schema\n */\nexport function loadEnv<T>(schema: Joi.ObjectSchema<T>): T {\n const { value, error } = schema.validate(process.env, {\n abortEarly: false,\n allowUnknown: true,\n });\n\n if (error) {\n throw new Error(`❌ Env validation error: ${error.message}`);\n }\n\n return value;\n}\n","export interface LoggerOptions {\n timestamp?: boolean;\n enabled?: boolean;\n}\n\nclass Logger {\n private timestamp = true;\n private enabled = true;\n\n // Use to configure once at the root\n configure(options: LoggerOptions = {}) {\n this.timestamp = options.timestamp ?? this.timestamp;\n this.enabled = options.enabled ?? this.enabled;\n }\n\n private format(msg: string): string {\n if (!this.timestamp) return msg;\n return `[${new Date().toISOString()}] ${msg}`;\n }\n\n private output(msg: string): void {\n if (!this.enabled) return;\n console.log(this.format(msg));\n }\n\n // Log Levels\n success(msg: string): void {\n this.output(`✅ ${msg}`);\n }\n\n info(msg: string): void {\n this.output(`ℹ️ ${msg}`);\n }\n\n warning(msg: string): void {\n this.output(`⚠️ ${msg}`);\n }\n\n danger(msg: string): void {\n this.output(`❌ ${msg}`);\n }\n\n log(msg: string): void {\n this.output(`• ${msg}`);\n }\n}\n\nexport const logger = new Logger();\n","import type { Request, Response, NextFunction } from \"express\";\n\nexport const authMiddleware = (\n _req: Request,\n _res: Response,\n next: NextFunction\n): void => {\n next(); // placeholder\n};\n","import type { Request, Response, NextFunction } from \"express\";\nimport type { Error as MongooseError } from \"mongoose\";\n\nimport { AppError } from \"../errors/appError.js\";\nimport { ApiResponse } from \"../api/apiResponse.js\";\n\n/* ======================================================\n MongoDB Error Handlers\n====================================================== */\n\nconst handleCastErrorDB = (err: MongooseError.CastError): AppError =>\n new AppError(`Invalid ${err.path}: ${err.value}`, 400, {\n code: \"INVALID_ID\",\n });\n\nconst handleDuplicateFieldsDB = (err: {\n keyValue?: Record<string, unknown>;\n}): AppError => {\n const value = err.keyValue ? JSON.stringify(err.keyValue) : \"duplicate value\";\n\n return new AppError(`Duplicate field value: ${value}`, 400, {\n code: \"DUPLICATE_FIELD\",\n });\n};\n\nconst handleValidationErrorDB = (\n err: MongooseError.ValidationError\n): AppError => {\n const errors = Object.values(err.errors).map((el) => el.message);\n\n return new AppError(\"Invalid input data\", 400, {\n code: \"VALIDATION_ERROR\",\n details: errors,\n });\n};\n\n/* ======================================================\n JWT Error Handlers\n====================================================== */\n\nconst handleJWTError = (): AppError =>\n new AppError(\"Invalid token. Please log in again.\", 401);\n\nconst handleJWTExpiredError = (): AppError =>\n new AppError(\"Your token has expired. Please log in again.\", 401);\n\n/* ======================================================\n Global Error Middleware\n====================================================== */\n\nexport const GlobalErrorHandler =\n (isProd = false) =>\n (\n err: unknown,\n _req: Request,\n res: Response,\n _next: NextFunction\n ): Response => {\n let error: AppError;\n\n /* --------------------\n Normalize error\n -------------------- */\n\n if (err instanceof AppError) {\n error = err;\n } else if (err instanceof Error) {\n error = new AppError(err.message, 500);\n error.isOperational = false;\n } else {\n error = new AppError(\"Internal Server Error\", 500);\n error.isOperational = false;\n }\n\n /* --------------------\n MongoDB errors\n -------------------- */\n\n if (err && (err as MongooseError).name === \"CastError\") {\n error = handleCastErrorDB(err as MongooseError.CastError);\n }\n\n if (\n err &&\n typeof err === \"object\" &&\n (err as { code?: number }).code === 11000\n ) {\n error = handleDuplicateFieldsDB(\n err as { keyValue?: Record<string, unknown> }\n );\n }\n\n if (err && (err as MongooseError).name === \"ValidationError\") {\n error = handleValidationErrorDB(err as MongooseError.ValidationError);\n }\n\n /* --------------------\n JWT errors\n -------------------- */\n\n if ((err as Error)?.name === \"JsonWebTokenError\") {\n error = handleJWTError();\n }\n\n if ((err as Error)?.name === \"TokenExpiredError\") {\n error = handleJWTExpiredError();\n }\n\n /* --------------------\n Logging\n -------------------- */\n\n if (!error.isOperational) {\n console.error({ err }, \"💣 Programming error\");\n } else {\n console.log({ message: error.message }, \"⚠️ Operational error\");\n }\n\n /* --------------------\n Response\n -------------------- */\n\n if (!isProd) {\n // DEV: detailed error\n return ApiResponse.send(res, {\n success: false,\n statusCode: error.statusCode,\n message: error.message,\n data: {\n stack: (err as Error)?.stack,\n details: error.details,\n },\n });\n }\n\n // PROD: safe error\n return ApiResponse.send(res, {\n success: false,\n statusCode: error.statusCode,\n message: error.isOperational ? error.message : \"Something went wrong\",\n });\n };\n","import type { Request, Response, NextFunction } from \"express\";\nimport type { Schema } from \"joi\";\n\nexport const validate =\n (schema: Schema) =>\n (req: Request, _res: Response, next: NextFunction): void => {\n const { error } = schema.validate(req.body);\n\n if (error) {\n return next(error);\n }\n\n next();\n };\n","import type { Model, UpdateQuery } from \"mongoose\";\nimport { AppError } from \"../errors/appError.js\";\n\n/**\n * Generic Base Service\n * Provides default CRUD operations\n */\nexport abstract class BaseService<T> {\n protected model: Model<T>;\n protected resourceName: string;\n\n constructor(model: Model<T>, resourceName: string) {\n this.model = model;\n this.resourceName = resourceName;\n }\n\n async create(data: Partial<T>): Promise<T> {\n const doc = await this.model.create(data as any);\n return doc as T;\n }\n\n async findAll(): Promise<T[]> {\n return this.model.find();\n }\n\n async findById(id: string): Promise<T> {\n const doc = await this.model.findById(id);\n\n if (!doc) {\n throw new AppError(`${this.resourceName} not found`, 404, {\n code: `${this.resourceName.toUpperCase()}_NOT_FOUND`,\n });\n }\n\n return doc;\n }\n\n async updateById(id: string, data: UpdateQuery<T>): Promise<T> {\n const doc = await this.model.findByIdAndUpdate(id, data, {\n new: true,\n runValidators: true,\n });\n\n if (!doc) {\n throw new AppError(`${this.resourceName} not found`, 404, {\n code: `${this.resourceName.toUpperCase()}_NOT_FOUND`,\n });\n }\n\n return doc;\n }\n\n async deleteById(id: string): Promise<T> {\n const doc = await this.model.findByIdAndDelete(id);\n\n if (!doc) {\n throw new AppError(`${this.resourceName} not found`, 404, {\n code: `${this.resourceName.toUpperCase()}_NOT_FOUND`,\n });\n }\n\n return doc;\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/errors/appError.ts","../src/api/apiResponse.ts","../src/api/asyncHandler.ts","../src/api/apiFeatures.ts","../src/api/apiFactory.ts","../src/utils/errorToString.ts","../src/configs/logger.ts","../src/configs/database.ts","../src/configs/env.ts","../src/middlewares/auth.middleware.ts","../src/middlewares/error.middleware.ts","../src/middlewares/validate.middleware.ts","../src/services/base.service.ts"],"names":["AppError","message","statusCode","options","ApiResponse","_ApiResponse","res","success","data","meta","body","asyncHandler","fn","req","next","APIFeatures","query","queryString","queryObj","el","queryStr","match","sortBy","fields","skip","populateOptions","apiFeatures_default","APIFactory","Model","filter","features","docs","doc","errorToString","error","Logger","msg","logger","connectMongoDB","config","mongoose","dotenv","loadEnv","schema","value","authMiddleware","_req","_res","handleCastErrorDB","err","handleDuplicateFieldsDB","handleValidationErrorDB","errors","handleJWTError","handleJWTExpiredError","GlobalErrorHandler","isProd","_next","validate","BaseService","model","resourceName","id"],"mappings":"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","file":"index.js","sourcesContent":["export class AppError extends Error {\n statusCode: number;\n status: string;\n isOperational: boolean;\n code?: string;\n details?: unknown;\n\n constructor(\n message: string,\n statusCode: number = 500,\n options?: { code?: string; details?: unknown }\n ) {\n super(message);\n\n this.statusCode = statusCode;\n this.status = `${statusCode}`.startsWith(\"4\") ? \"fail\" : \"error\";\n this.isOperational = true;\n this.code = options?.code;\n this.details = options?.details;\n\n Error.captureStackTrace(this, this.constructor);\n }\n}\n","import { Response } from \"express\";\n\nexport class ApiResponse {\n static send(\n res: Response,\n options: {\n statusCode?: number;\n success?: boolean;\n message?: string;\n data?: unknown;\n meta?: unknown;\n }\n ) {\n const {\n statusCode = 200,\n success = true,\n message = \"Success\",\n data,\n meta,\n } = options;\n\n if (statusCode === 204) {\n return res.status(204).send();\n }\n\n const body: Record<string, unknown> = { success, message };\n if (data !== undefined) body.data = data;\n if (meta !== undefined) body.meta = meta;\n\n return res.status(statusCode).json(body);\n }\n\n static ok(res: Response, data: unknown, message?: string, meta?: unknown) {\n return ApiResponse.send(res, { statusCode: 200, data, message, meta });\n }\n\n static created(res: Response, data: unknown, message?: string) {\n return ApiResponse.send(res, { statusCode: 201, data, message });\n }\n\n static noContent(res: Response) {\n return ApiResponse.send(res, { statusCode: 204 });\n }\n\n // ❌ Error response (optional use in controllers)\n static error(res: Response, message?: string, statusCode: number = 500) {\n return ApiResponse.send(res, {\n success: false,\n statusCode,\n message,\n });\n }\n}\n","import { Request, Response, NextFunction } from \"express\";\n\nexport const asyncHandler =\n (fn: (req: Request, res: Response, next: NextFunction) => Promise<void>) =>\n (req: Request, res: Response, next: NextFunction) =>\n Promise.resolve(fn(req, res, next)).catch(next);\n","import type { Query, PopulateOptions } from \"mongoose\";\n\n/**\n * Generic API Features helper for Mongoose queries\n */\nclass APIFeatures<T> {\n public query: Query<T[], T>;\n public queryString: Record<string, unknown>;\n public page: number;\n public limit: number;\n\n constructor(query: Query<T[], T>, queryString: Record<string, unknown>) {\n this.query = query;\n this.queryString = queryString;\n this.page = 1;\n this.limit = 100;\n }\n\n filter(): this {\n const queryObj = { ...this.queryString };\n\n [\"page\", \"sort\", \"limit\", \"fields\"].forEach((el) => delete queryObj[el]);\n\n let queryStr = JSON.stringify(queryObj);\n queryStr = queryStr.replace(\n /\\b(gte|gt|lte|lt|in)\\b/g,\n (match) => `$${match}`\n );\n\n this.query = this.query.find(JSON.parse(queryStr));\n\n return this;\n }\n\n sort(): this {\n if (this.queryString.sort) {\n const sortBy = String(this.queryString.sort).split(\",\").join(\" \");\n this.query = this.query.sort(sortBy);\n } else {\n this.query = this.query.sort(\"-createdAt\");\n }\n\n return this;\n }\n\n limitFields(): this {\n if (this.queryString.fields) {\n const fields = String(this.queryString.fields).split(\",\").join(\" \");\n this.query = this.query.select(fields);\n } else {\n this.query = this.query.select(\"-__v\");\n }\n\n return this;\n }\n\n paginate(): this {\n this.page = Number(this.queryString.page) || 1;\n this.limit = Number(this.queryString.limit) || 100;\n\n const skip = (this.page - 1) * this.limit;\n\n this.query = this.query.skip(skip).limit(this.limit);\n\n return this;\n }\n\n populate(populateOptions?: PopulateOptions | PopulateOptions[]): this {\n if (populateOptions) {\n this.query = this.query.populate(populateOptions);\n }\n\n return this;\n }\n}\n\nexport default APIFeatures;\n","import type { Request } from \"express\";\nimport type { Model, PopulateOptions } from \"mongoose\";\n\nimport { AppError } from \"../errors/appError.js\";\nimport { ApiResponse } from \"./apiResponse.js\";\nimport { asyncHandler } from \"./asyncHandler.js\";\nimport APIFeatures from \"./apiFeatures.js\";\n\n/**\n * Options for API factory methods\n */\ninterface ApiFactoryOptions<T = unknown> {\n message?: string;\n notFoundMessage?: string;\n populate?: PopulateOptions | PopulateOptions[];\n filter?: (req: Request) => Record<string, unknown>;\n}\n\n/**\n * Generic API Factory (Hybrid Pattern)\n * For simple CRUD operations without business logic\n */\nexport class APIFactory {\n static getAll<T>(Model: Model<T>, options: ApiFactoryOptions<T> = {}) {\n return asyncHandler(async (req, res) => {\n const filter = options.filter ? options.filter(req) : {};\n\n const features = new APIFeatures(Model.find(filter), req.query)\n .filter()\n .sort()\n .limitFields()\n .paginate()\n .populate(options.populate);\n\n const docs = await features.query;\n\n ApiResponse.ok(\n res,\n docs,\n options.message ?? \"Records fetched successfully\",\n {\n count: docs.length,\n page: features.page,\n limit: features.limit,\n }\n );\n });\n }\n\n static getOne<T>(Model: Model<T>, options: ApiFactoryOptions<T> = {}) {\n return asyncHandler(async (req, res) => {\n let query = Model.findById(req.params.id);\n\n if (options.populate) {\n query = query.populate(\n options.populate as PopulateOptions | PopulateOptions[]\n );\n }\n\n const doc = await query;\n\n if (!doc) {\n throw new AppError(\n options.notFoundMessage ?? \"Resource not found\",\n 404,\n { code: \"RESOURCE_NOT_FOUND\" }\n );\n }\n\n ApiResponse.ok(res, doc);\n });\n }\n\n static createOne<T>(Model: Model<T>, options: ApiFactoryOptions<T> = {}) {\n return asyncHandler(async (req, res) => {\n const doc = await Model.create(req.body);\n\n ApiResponse.created(\n res,\n doc,\n options.message ?? \"Resource created successfully\"\n );\n });\n }\n\n static updateOne<T>(Model: Model<T>, options: ApiFactoryOptions<T> = {}) {\n return asyncHandler(async (req, res) => {\n const doc = await Model.findByIdAndUpdate(\n req.params.id,\n req.body as Partial<T>,\n {\n new: true,\n runValidators: true,\n }\n );\n\n if (!doc) {\n throw new AppError(\n options.notFoundMessage ?? \"Resource not found\",\n 404\n );\n }\n\n ApiResponse.ok(\n res,\n doc,\n options.message ?? \"Resource updated successfully\"\n );\n });\n }\n\n static deleteOne<T>(Model: Model<T>, options: ApiFactoryOptions<T> = {}) {\n return asyncHandler(async (req, res) => {\n const doc = await Model.findByIdAndDelete(req.params.id);\n\n if (!doc) {\n throw new AppError(\n options.notFoundMessage ?? \"Resource not found\",\n 404\n );\n }\n\n ApiResponse.noContent(res);\n });\n }\n}\n","export function errorToString(error: unknown): string {\n if (error instanceof Error) {\n return `${error.name}: ${error.message}\\n${error.stack ?? \"\"}`;\n }\n\n if (typeof error === \"object\") {\n try {\n return JSON.stringify(error, null, 2);\n } catch {\n return \"[Unserializable object]\";\n }\n }\n\n return String(error);\n}\n","import { errorToString } from \"../utils/errorToString.js\";\n\nexport interface LoggerOptions {\n timestamp?: boolean;\n enabled?: boolean;\n}\n\nclass Logger {\n private timestamp = true;\n private enabled = true;\n\n // Use to configure once at the root\n configure(options: LoggerOptions = {}) {\n this.timestamp = options.timestamp ?? this.timestamp;\n this.enabled = options.enabled ?? this.enabled;\n }\n\n private format(msg: string): string {\n if (!this.timestamp) return msg;\n return `[${new Date().toISOString()}] ${msg}`;\n }\n\n private output(msg: string): void {\n if (!this.enabled) return;\n console.log(this.format(msg));\n }\n\n // Log Levels\n success(msg: string): void {\n this.output(`✅ ${msg}`);\n }\n\n info(msg: string): void {\n this.output(`ℹ️ ${msg}`);\n }\n\n warning(msg: string): void {\n this.output(`⚠️ ${msg}`);\n }\n\n danger(message: string, error?: unknown): void {\n if (error) {\n this.output(`❌ ${message}\\n${errorToString(error)}`);\n } else {\n this.output(`❌ ${message}`);\n }\n }\n\n log(msg: string): void {\n this.output(`• ${msg}`);\n }\n}\n\nexport const logger = new Logger();\n","import mongoose from \"mongoose\";\nimport { logger } from \"./logger.js\";\n\nexport interface DatabaseConfig {\n uri: string;\n}\n\nexport async function connectMongoDB(config: DatabaseConfig): Promise<void> {\n try {\n mongoose.set(\"strictQuery\", true);\n\n await mongoose.connect(config.uri);\n\n logger.success(\"✅ Database connected successfully\");\n\n process.on(\"SIGINT\", async () => {\n await mongoose.connection.close();\n logger.success(\"✅ Database connection closed\");\n process.exit(0);\n });\n } catch (error: unknown) {\n logger.danger(\"❌ Database connection failed\");\n\n if (error instanceof Error) {\n logger.danger(error.message);\n }\n\n process.exit(1);\n }\n}\n","import dotenv from \"dotenv\";\nimport type Joi from \"joi\";\n\ndotenv.config();\n\n/**\n * Generic env loader for Elseware apps\n * App provides its own Joi schema\n */\nexport function loadEnv<T>(schema: Joi.ObjectSchema<T>): T {\n const { value, error } = schema.validate(process.env, {\n abortEarly: false,\n allowUnknown: true,\n });\n\n if (error) {\n throw new Error(`❌ Env validation error: ${error.message}`);\n }\n\n return value;\n}\n","import type { Request, Response, NextFunction } from \"express\";\n\nexport const authMiddleware = (\n _req: Request,\n _res: Response,\n next: NextFunction\n): void => {\n next(); // placeholder\n};\n","import type { Request, Response, NextFunction } from \"express\";\nimport type { Error as MongooseError } from \"mongoose\";\n\nimport { AppError } from \"../errors/appError.js\";\nimport { ApiResponse } from \"../api/apiResponse.js\";\nimport { logger } from \"../configs/logger.js\";\n\n/* ======================================================\n MongoDB Error Handlers\n====================================================== */\n\nconst handleCastErrorDB = (err: MongooseError.CastError): AppError =>\n new AppError(`Invalid ${err.path}: ${err.value}`, 400, {\n code: \"INVALID_ID\",\n });\n\nconst handleDuplicateFieldsDB = (err: {\n keyValue?: Record<string, unknown>;\n}): AppError => {\n const value = err.keyValue ? JSON.stringify(err.keyValue) : \"duplicate value\";\n\n return new AppError(`Duplicate field value: ${value}`, 400, {\n code: \"DUPLICATE_FIELD\",\n });\n};\n\nconst handleValidationErrorDB = (\n err: MongooseError.ValidationError\n): AppError => {\n const errors = Object.values(err.errors).map((el) => el.message);\n\n return new AppError(\"Invalid input data\", 400, {\n code: \"VALIDATION_ERROR\",\n details: errors,\n });\n};\n\n/* ======================================================\n JWT Error Handlers\n====================================================== */\n\nconst handleJWTError = (): AppError =>\n new AppError(\"Invalid token. Please log in again.\", 401);\n\nconst handleJWTExpiredError = (): AppError =>\n new AppError(\"Your token has expired. Please log in again.\", 401);\n\n/* ======================================================\n Global Error Middleware\n====================================================== */\n\nexport const GlobalErrorHandler =\n (isProd = false) =>\n (\n err: unknown,\n _req: Request,\n res: Response,\n _next: NextFunction\n ): Response => {\n let error: AppError;\n\n /* --------------------\n Normalize error\n -------------------- */\n\n if (err instanceof AppError) {\n error = err;\n } else if (err instanceof Error) {\n error = new AppError(err.message, 500);\n error.isOperational = false;\n } else {\n error = new AppError(\"Internal Server Error\", 500);\n error.isOperational = false;\n }\n\n /* --------------------\n MongoDB errors\n -------------------- */\n\n if (err && (err as MongooseError).name === \"CastError\") {\n error = handleCastErrorDB(err as MongooseError.CastError);\n }\n\n if (\n err &&\n typeof err === \"object\" &&\n (err as { code?: number }).code === 11000\n ) {\n error = handleDuplicateFieldsDB(\n err as { keyValue?: Record<string, unknown> }\n );\n }\n\n if (err && (err as MongooseError).name === \"ValidationError\") {\n error = handleValidationErrorDB(err as MongooseError.ValidationError);\n }\n\n /* --------------------\n JWT errors\n -------------------- */\n\n if ((err as Error)?.name === \"JsonWebTokenError\") {\n error = handleJWTError();\n }\n\n if ((err as Error)?.name === \"TokenExpiredError\") {\n error = handleJWTExpiredError();\n }\n\n /* --------------------\n Logging\n -------------------- */\n\n if (!error.isOperational) {\n logger.danger(\"Programming error\", err);\n } else {\n logger.danger(\"Operational error\", { message: error.message });\n }\n\n /* --------------------\n Response\n -------------------- */\n\n if (!isProd) {\n // DEV: detailed error\n return ApiResponse.send(res, {\n success: false,\n statusCode: error.statusCode,\n message: error.message,\n data: {\n stack: (err as Error)?.stack,\n details: error.details,\n },\n });\n }\n\n // PROD: safe error\n return ApiResponse.send(res, {\n success: false,\n statusCode: error.statusCode,\n message: error.isOperational ? error.message : \"Something went wrong\",\n });\n };\n","import type { Request, Response, NextFunction } from \"express\";\nimport type { Schema } from \"joi\";\n\nexport const validate =\n (schema: Schema) =>\n (req: Request, _res: Response, next: NextFunction): void => {\n const { error } = schema.validate(req.body);\n\n if (error) {\n return next(error);\n }\n\n next();\n };\n","import type { Model, UpdateQuery } from \"mongoose\";\nimport { AppError } from \"../errors/appError.js\";\n\n/**\n * Generic Base Service\n * Provides default CRUD operations\n */\nexport abstract class BaseService<T> {\n protected model: Model<T>;\n protected resourceName: string;\n\n constructor(model: Model<T>, resourceName: string) {\n this.model = model;\n this.resourceName = resourceName;\n }\n\n async create(data: Partial<T>): Promise<T> {\n const doc = await this.model.create(data as any);\n return doc as T;\n }\n\n async findAll(): Promise<T[]> {\n return this.model.find();\n }\n\n async findById(id: string): Promise<T> {\n const doc = await this.model.findById(id);\n\n if (!doc) {\n throw new AppError(`${this.resourceName} not found`, 404, {\n code: `${this.resourceName.toUpperCase()}_NOT_FOUND`,\n });\n }\n\n return doc;\n }\n\n async updateById(id: string, data: UpdateQuery<T>): Promise<T> {\n const doc = await this.model.findByIdAndUpdate(id, data, {\n new: true,\n runValidators: true,\n });\n\n if (!doc) {\n throw new AppError(`${this.resourceName} not found`, 404, {\n code: `${this.resourceName.toUpperCase()}_NOT_FOUND`,\n });\n }\n\n return doc;\n }\n\n async deleteById(id: string): Promise<T> {\n const doc = await this.model.findByIdAndDelete(id);\n\n if (!doc) {\n throw new AppError(`${this.resourceName} not found`, 404, {\n code: `${this.resourceName.toUpperCase()}_NOT_FOUND`,\n });\n }\n\n return doc;\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "elseware-nodejs",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "private": false,
5
5
  "description": "Core framework utilities for Elseware NodeJS applications",
6
6
  "type": "module",