elseware-nodejs 1.8.6 → 1.9.0

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.js CHANGED
@@ -1,5 +1,5 @@
1
- import mongoose from 'mongoose';
2
1
  import dotenv from 'dotenv';
2
+ import mongoose from 'mongoose';
3
3
  import { BlobServiceClient, StorageSharedKeyCredential, generateBlobSASQueryParameters, BlobSASPermissions } from '@azure/storage-blob';
4
4
  import fs3 from 'fs';
5
5
  import cloudinaryModule from 'cloudinary';
@@ -10,252 +10,7 @@ import juice from 'juice';
10
10
  import jwt from 'jsonwebtoken';
11
11
  import multer from 'multer';
12
12
 
13
- // src/errors/appError.ts
14
- var AppError = class extends Error {
15
- statusCode;
16
- status;
17
- isOperational;
18
- code;
19
- details;
20
- constructor(message, statusCode = 500, options) {
21
- super(message);
22
- this.statusCode = statusCode;
23
- this.status = `${statusCode}`.startsWith("4") ? "fail" : "error";
24
- this.isOperational = true;
25
- this.code = options?.code;
26
- this.details = options?.details;
27
- Error.captureStackTrace(this, this.constructor);
28
- }
29
- };
30
-
31
- // src/api/apiResponse.ts
32
- var APIResponse = class _APIResponse {
33
- static send(res, options) {
34
- const {
35
- statusCode = 200,
36
- success = true,
37
- message = "Success",
38
- data,
39
- meta
40
- } = options;
41
- if (statusCode === 204) {
42
- return res.status(204).send();
43
- }
44
- const body = {
45
- success,
46
- message,
47
- ...data !== void 0 && { data },
48
- ...meta !== void 0 && { meta }
49
- };
50
- return res.status(statusCode).json(body);
51
- }
52
- static ok(res, data, message = "Success", meta) {
53
- return _APIResponse.send(res, { statusCode: 200, data, message, meta });
54
- }
55
- static created(res, data, message = "Resource created successfully") {
56
- return _APIResponse.send(res, { statusCode: 201, data, message });
57
- }
58
- static noContent(res) {
59
- return _APIResponse.send(res, { statusCode: 204 });
60
- }
61
- static error(res, message = "Internal Server Error", statusCode = 500, meta) {
62
- return _APIResponse.send(res, {
63
- success: false,
64
- statusCode,
65
- message,
66
- meta
67
- });
68
- }
69
- static paginated(res, data, options, message = "Data fetched successfully") {
70
- return _APIResponse.ok(res, data, message, {
71
- total: options.total,
72
- page: options.page,
73
- limit: options.limit,
74
- totalPages: Math.ceil(options.total / options.limit)
75
- });
76
- }
77
- };
78
-
79
- // src/api/asyncHandler.ts
80
- var asyncHandler = (fn) => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next);
81
-
82
- // src/api/apiFeatures.ts
83
- var APIFeatures = class {
84
- query;
85
- queryString;
86
- page;
87
- limit;
88
- constructor(query, queryString) {
89
- this.query = query;
90
- this.queryString = queryString;
91
- this.page = 1;
92
- this.limit = 100;
93
- }
94
- filter() {
95
- const queryObj = { ...this.queryString };
96
- ["page", "sort", "limit", "fields"].forEach((el) => delete queryObj[el]);
97
- let queryStr = JSON.stringify(queryObj);
98
- queryStr = queryStr.replace(
99
- /\b(gte|gt|lte|lt|in)\b/g,
100
- (match) => `$${match}`
101
- );
102
- this.query = this.query.find(JSON.parse(queryStr));
103
- return this;
104
- }
105
- sort() {
106
- if (this.queryString.sort) {
107
- const sortBy = String(this.queryString.sort).split(",").join(" ");
108
- this.query = this.query.sort(sortBy);
109
- } else {
110
- this.query = this.query.sort("-createdAt");
111
- }
112
- return this;
113
- }
114
- limitFields() {
115
- if (this.queryString.fields) {
116
- const fields = String(this.queryString.fields).split(",").join(" ");
117
- this.query = this.query.select(fields);
118
- } else {
119
- this.query = this.query.select("-__v");
120
- }
121
- return this;
122
- }
123
- paginate() {
124
- this.page = Number(this.queryString.page) || 1;
125
- this.limit = Number(this.queryString.limit) || 100;
126
- const skip = (this.page - 1) * this.limit;
127
- this.query = this.query.skip(skip).limit(this.limit);
128
- return this;
129
- }
130
- populate(populateOptions) {
131
- if (populateOptions) {
132
- this.query = this.query.populate(populateOptions);
133
- }
134
- return this;
135
- }
136
- };
137
- var apiFeatures_default = APIFeatures;
138
-
139
- // src/api/apiFactory.ts
140
- var APIFactory = class {
141
- static getAll(Model, options = {}) {
142
- return asyncHandler(async (req, res) => {
143
- const filter = options.filter ? options.filter(req) : {};
144
- const features = new apiFeatures_default(Model.find(filter), req.query).filter().sort().limitFields().paginate().populate(options.populate);
145
- const docs = await features.query;
146
- APIResponse.ok(
147
- res,
148
- docs,
149
- options.message ?? "Records fetched successfully",
150
- {
151
- count: docs.length,
152
- page: features.page,
153
- limit: features.limit
154
- }
155
- );
156
- });
157
- }
158
- static getOne(Model, options = {}) {
159
- return asyncHandler(async (req, res) => {
160
- let query = Model.findById(req.params.id);
161
- if (options.populate) {
162
- query = query.populate(
163
- options.populate
164
- );
165
- }
166
- const doc = await query;
167
- if (!doc) {
168
- throw new AppError(
169
- options.notFoundMessage ?? "Resource not found",
170
- 404,
171
- { code: "RESOURCE_NOT_FOUND" }
172
- );
173
- }
174
- APIResponse.ok(res, doc);
175
- });
176
- }
177
- static createOne(Model, options = {}) {
178
- return asyncHandler(async (req, res) => {
179
- const doc = await Model.create(req.body);
180
- APIResponse.created(
181
- res,
182
- doc,
183
- options.message ?? "Resource created successfully"
184
- );
185
- });
186
- }
187
- static updateOne(Model, options = {}) {
188
- return asyncHandler(async (req, res) => {
189
- const doc = await Model.findByIdAndUpdate(
190
- req.params.id,
191
- req.body,
192
- {
193
- new: true,
194
- runValidators: true
195
- }
196
- );
197
- if (!doc) {
198
- throw new AppError(
199
- options.notFoundMessage ?? "Resource not found",
200
- 404
201
- );
202
- }
203
- APIResponse.ok(
204
- res,
205
- doc,
206
- options.message ?? "Resource updated successfully"
207
- );
208
- });
209
- }
210
- static deleteOne(Model, options = {}) {
211
- return asyncHandler(async (req, res) => {
212
- const doc = await Model.findByIdAndDelete(req.params.id);
213
- if (!doc) {
214
- throw new AppError(
215
- options.notFoundMessage ?? "Resource not found",
216
- 404
217
- );
218
- }
219
- APIResponse.noContent(res);
220
- });
221
- }
222
- };
223
-
224
- // src/api/pickFields.ts
225
- function pickFields(obj, fields, options = {}) {
226
- const removeUndefined = options.removeUndefined ?? false;
227
- const removeNull = options.removeNull ?? false;
228
- const removeEmptyString = options.removeEmptyString ?? false;
229
- const strict = options.strict ?? false;
230
- const defaults = options.defaults ?? {};
231
- const rename = options.rename ?? {};
232
- const transform = options.transform ?? {};
233
- const result = {};
234
- if (strict) {
235
- for (const key of Object.keys(obj)) {
236
- if (!fields.includes(key)) {
237
- throw new Error(`Unknown field: ${key}`);
238
- }
239
- }
240
- }
241
- for (const field of fields) {
242
- let value = obj[field];
243
- if (value === void 0 && field in defaults) {
244
- value = defaults[field];
245
- }
246
- if (removeUndefined && value === void 0) continue;
247
- if (removeNull && value === null) continue;
248
- if (removeEmptyString && value === "") continue;
249
- if (transform[field]) {
250
- value = transform[field](value);
251
- }
252
- const outputKey = rename[field] ?? field;
253
- if (Object.prototype.hasOwnProperty.call(obj, field) || field in defaults) {
254
- result[outputKey] = value;
255
- }
256
- }
257
- return result;
258
- }
13
+ // src/configs/env.ts
259
14
 
260
15
  // src/utils/errorToString.ts
261
16
  function errorToString(error) {
@@ -314,25 +69,7 @@ ${errorToString(error)}`);
314
69
  };
315
70
  var logger = new Logger();
316
71
 
317
- // src/configs/database.ts
318
- async function connectMongoDB(config) {
319
- try {
320
- mongoose.set("strictQuery", true);
321
- await mongoose.connect(config.uri);
322
- logger.success("Database connected successfully");
323
- process.on("SIGINT", async () => {
324
- await mongoose.connection.close();
325
- logger.success("Database connection closed");
326
- process.exit(0);
327
- });
328
- } catch (error) {
329
- logger.danger("Database connection failed");
330
- if (error instanceof Error) {
331
- logger.danger(error.message);
332
- }
333
- process.exit(1);
334
- }
335
- }
72
+ // src/configs/env.ts
336
73
  dotenv.config({ quiet: true });
337
74
  function loadEnv(options) {
338
75
  const result = options.schema.safeParse(process.env);
@@ -2167,6 +1904,7 @@ var CircularQueue = class {
2167
1904
  }
2168
1905
  this.data = new Array(capacity);
2169
1906
  }
1907
+ capacity;
2170
1908
  data;
2171
1909
  head = 0;
2172
1910
  tail = 0;
@@ -5087,31 +4825,418 @@ var SegmentTree = class {
5087
4825
  }
5088
4826
  };
5089
4827
 
5090
- // src/middlewares/auth.middleware.ts
4828
+ // src/database/DatabaseManager.ts
4829
+ var DatabaseManager = class {
4830
+ constructor(provider, options = {
4831
+ enableShutdownHooks: true
4832
+ }) {
4833
+ this.provider = provider;
4834
+ this.options = options;
4835
+ }
4836
+ provider;
4837
+ options;
4838
+ async connect() {
4839
+ try {
4840
+ await this.provider.connect();
4841
+ logger.success("Database connected successfully");
4842
+ if (this.options.enableShutdownHooks) {
4843
+ this.registerShutdownHooks();
4844
+ }
4845
+ } catch (error) {
4846
+ logger.danger("Database connection failed", error);
4847
+ throw error;
4848
+ }
4849
+ }
4850
+ async disconnect() {
4851
+ try {
4852
+ await this.provider.disconnect();
4853
+ logger.success("Database connection closed");
4854
+ } catch (error) {
4855
+ logger.danger("Database disconnect failed", error);
4856
+ throw error;
4857
+ }
4858
+ }
4859
+ isConnected() {
4860
+ return this.provider.isConnected();
4861
+ }
4862
+ getConnectionState() {
4863
+ return this.provider.getConnectionState();
4864
+ }
4865
+ registerShutdownHooks() {
4866
+ process.once("SIGINT", async () => {
4867
+ await this.disconnect();
4868
+ process.exit(0);
4869
+ });
4870
+ process.once("SIGTERM", async () => {
4871
+ await this.disconnect();
4872
+ process.exit(0);
4873
+ });
4874
+ }
4875
+ };
4876
+ var MongoDatabaseProvider = class {
4877
+ constructor(config) {
4878
+ this.config = config;
4879
+ }
4880
+ config;
4881
+ async connect() {
4882
+ mongoose.set("strictQuery", this.config.strictQuery ?? true);
4883
+ await mongoose.connect(this.config.uri);
4884
+ }
4885
+ async disconnect() {
4886
+ await mongoose.disconnect();
4887
+ }
4888
+ isConnected() {
4889
+ return mongoose.connection.readyState === 1;
4890
+ }
4891
+ getConnectionState() {
4892
+ switch (mongoose.connection.readyState) {
4893
+ case 0:
4894
+ return "disconnected";
4895
+ case 1:
4896
+ return "connected";
4897
+ case 2:
4898
+ return "connecting";
4899
+ case 3:
4900
+ return "disconnecting";
4901
+ default:
4902
+ return "unknown";
4903
+ }
4904
+ }
4905
+ };
4906
+
4907
+ // src/errors/appError.ts
4908
+ var AppError = class extends Error {
4909
+ statusCode;
4910
+ status;
4911
+ isOperational;
4912
+ code;
4913
+ details;
4914
+ constructor(message, statusCode = 500, options) {
4915
+ super(message);
4916
+ this.statusCode = statusCode;
4917
+ this.status = `${statusCode}`.startsWith("4") ? "fail" : "error";
4918
+ this.isOperational = true;
4919
+ this.code = options?.code;
4920
+ this.details = options?.details;
4921
+ Error.captureStackTrace(this, this.constructor);
4922
+ }
4923
+ };
4924
+
4925
+ // src/http/handlers/AsyncHandler.ts
4926
+ var AsyncHandler = (fn) => (req, res, next) => {
4927
+ Promise.resolve(fn(req, res, next)).catch(next);
4928
+ };
4929
+
4930
+ // src/http/query/QueryConstants.ts
4931
+ var QUERY_RESERVED_FIELDS = [
4932
+ "page",
4933
+ "limit",
4934
+ "sort",
4935
+ "fields",
4936
+ "populate"
4937
+ ];
4938
+ var DEFAULT_PAGE = 1;
4939
+ var DEFAULT_LIMIT = 100;
4940
+ var DEFAULT_SORT_DIRECTION = "asc";
4941
+ var SUPPORTED_OPERATORS = [
4942
+ "eq",
4943
+ "ne",
4944
+ "gt",
4945
+ "gte",
4946
+ "lt",
4947
+ "lte",
4948
+ "in",
4949
+ "nin",
4950
+ "contains",
4951
+ "startsWith",
4952
+ "endsWith"
4953
+ ];
4954
+
4955
+ // src/http/query/ApiFeatures.ts
4956
+ var ApiFeatures = class {
4957
+ static parse(query) {
4958
+ return {
4959
+ filters: this.parseFilters(query),
4960
+ sort: this.parseSort(query),
4961
+ fields: this.parseFields(query),
4962
+ pagination: this.parsePagination(query),
4963
+ populate: this.parsePopulate(query)
4964
+ };
4965
+ }
4966
+ static parseFilters(query) {
4967
+ const filters = [];
4968
+ Object.entries(query).forEach(([key, value]) => {
4969
+ if (QUERY_RESERVED_FIELDS.includes(key)) {
4970
+ return;
4971
+ }
4972
+ const match = key.match(
4973
+ /^(.+)\[(eq|ne|gt|gte|lt|lte|in|nin|contains|startsWith|endsWith)\]$/
4974
+ );
4975
+ if (match) {
4976
+ filters.push({
4977
+ field: match[1],
4978
+ operator: match[2],
4979
+ value
4980
+ });
4981
+ return;
4982
+ }
4983
+ filters.push({
4984
+ field: key,
4985
+ operator: "eq",
4986
+ value
4987
+ });
4988
+ });
4989
+ return filters;
4990
+ }
4991
+ static parseSort(query) {
4992
+ const sort = query.sort?.toString();
4993
+ if (!sort) {
4994
+ return [];
4995
+ }
4996
+ return sort.split(",").filter(Boolean).map((item) => {
4997
+ if (item.startsWith("-")) {
4998
+ return {
4999
+ field: item.substring(1),
5000
+ direction: "desc"
5001
+ };
5002
+ }
5003
+ return {
5004
+ field: item,
5005
+ direction: "asc"
5006
+ };
5007
+ });
5008
+ }
5009
+ static parseFields(query) {
5010
+ const fields = query.fields?.toString();
5011
+ if (!fields) {
5012
+ return [];
5013
+ }
5014
+ return fields.split(",").map((field) => field.trim()).filter(Boolean);
5015
+ }
5016
+ static parsePopulate(query) {
5017
+ const populate = query.populate?.toString();
5018
+ if (!populate) {
5019
+ return [];
5020
+ }
5021
+ return populate.split(",").map((field) => field.trim()).filter(Boolean);
5022
+ }
5023
+ static parsePagination(query) {
5024
+ const page = Number(query.page) || DEFAULT_PAGE;
5025
+ const limit = Number(query.limit) || DEFAULT_LIMIT;
5026
+ return {
5027
+ page,
5028
+ limit
5029
+ };
5030
+ }
5031
+ };
5032
+
5033
+ // src/http/responses/ApiResponse.ts
5034
+ var ApiResponse = class _ApiResponse {
5035
+ static send(res, options) {
5036
+ const {
5037
+ statusCode = 200,
5038
+ success = true,
5039
+ message = "Success",
5040
+ data,
5041
+ meta
5042
+ } = options;
5043
+ if (statusCode === 204) {
5044
+ return res.status(204).send();
5045
+ }
5046
+ const body = {
5047
+ success,
5048
+ message
5049
+ };
5050
+ if (data !== void 0) {
5051
+ body.data = data;
5052
+ }
5053
+ if (meta !== void 0) {
5054
+ body.meta = meta;
5055
+ }
5056
+ return res.status(statusCode).json(body);
5057
+ }
5058
+ static ok(res, data, message = "Success", meta) {
5059
+ return _ApiResponse.send(res, {
5060
+ statusCode: 200,
5061
+ success: true,
5062
+ message,
5063
+ data,
5064
+ meta
5065
+ });
5066
+ }
5067
+ static created(res, data, message = "Resource created successfully") {
5068
+ return _ApiResponse.send(res, {
5069
+ statusCode: 201,
5070
+ success: true,
5071
+ message,
5072
+ data
5073
+ });
5074
+ }
5075
+ static accepted(res, data, message = "Request accepted") {
5076
+ return _ApiResponse.send(res, {
5077
+ statusCode: 202,
5078
+ success: true,
5079
+ message,
5080
+ data
5081
+ });
5082
+ }
5083
+ static noContent(res) {
5084
+ return _ApiResponse.send(res, {
5085
+ statusCode: 204
5086
+ });
5087
+ }
5088
+ static error(res, message = "Internal Server Error", statusCode = 500, meta) {
5089
+ return _ApiResponse.send(res, {
5090
+ success: false,
5091
+ statusCode,
5092
+ message,
5093
+ meta
5094
+ });
5095
+ }
5096
+ static paginated(res, data, options, message = "Data fetched successfully") {
5097
+ const meta = {
5098
+ total: options.total,
5099
+ page: options.page,
5100
+ limit: options.limit,
5101
+ totalPages: Math.ceil(options.total / options.limit)
5102
+ };
5103
+ return _ApiResponse.ok(res, data, message, meta);
5104
+ }
5105
+ };
5106
+
5107
+ // src/http/controllers/CrudControllerFactory.ts
5108
+ var CrudControllerFactory = class {
5109
+ static create(repository, options = {}) {
5110
+ const resourceName = options.resourceName ?? "Resource";
5111
+ return {
5112
+ /**
5113
+ * GET /
5114
+ */
5115
+ getAll: AsyncHandler(async (req, res) => {
5116
+ const parsed = ApiFeatures.parse(req.query);
5117
+ const baseFilter = options.filter?.(req) ?? {};
5118
+ const filter = {
5119
+ ...baseFilter
5120
+ };
5121
+ parsed.filters.forEach((item) => {
5122
+ if (item.operator === "eq") {
5123
+ filter[item.field] = item.value;
5124
+ }
5125
+ });
5126
+ const queryOptions = {
5127
+ limit: parsed.pagination.limit,
5128
+ skip: (parsed.pagination.page - 1) * parsed.pagination.limit,
5129
+ populate: parsed.populate
5130
+ };
5131
+ if (parsed.sort.length > 0) {
5132
+ queryOptions.sort = Object.fromEntries(
5133
+ parsed.sort.map((item) => [
5134
+ item.field,
5135
+ item.direction === "asc" ? 1 : -1
5136
+ ])
5137
+ );
5138
+ }
5139
+ if (parsed.fields.length > 0) {
5140
+ queryOptions.select = parsed.fields;
5141
+ }
5142
+ const docs = await repository.findAll(filter, queryOptions);
5143
+ const total = await repository.count(filter);
5144
+ return ApiResponse.paginated(
5145
+ res,
5146
+ docs,
5147
+ {
5148
+ total,
5149
+ page: parsed.pagination.page,
5150
+ limit: parsed.pagination.limit
5151
+ },
5152
+ `${resourceName} list fetched successfully`
5153
+ );
5154
+ }),
5155
+ /**
5156
+ * GET /:id
5157
+ */
5158
+ getOne: AsyncHandler(async (req, res) => {
5159
+ const id = String(req.params.id);
5160
+ const doc = await repository.findById(id);
5161
+ if (!doc) {
5162
+ throw new AppError(`${resourceName} not found`, 404, {
5163
+ code: "RESOURCE_NOT_FOUND"
5164
+ });
5165
+ }
5166
+ return ApiResponse.ok(res, doc, `${resourceName} fetched successfully`);
5167
+ }),
5168
+ /**
5169
+ * POST /
5170
+ */
5171
+ create: AsyncHandler(async (req, res) => {
5172
+ const doc = await repository.create(req.body);
5173
+ return ApiResponse.created(
5174
+ res,
5175
+ doc,
5176
+ `${resourceName} created successfully`
5177
+ );
5178
+ }),
5179
+ /**
5180
+ * PATCH /:id
5181
+ */
5182
+ update: AsyncHandler(async (req, res) => {
5183
+ const id = String(req.params.id);
5184
+ const doc = await repository.updateById(id, req.body);
5185
+ if (!doc) {
5186
+ throw new AppError(`${resourceName} not found`, 404, {
5187
+ code: "RESOURCE_NOT_FOUND"
5188
+ });
5189
+ }
5190
+ return ApiResponse.ok(res, doc, `${resourceName} updated successfully`);
5191
+ }),
5192
+ /**
5193
+ * DELETE /:id
5194
+ */
5195
+ delete: AsyncHandler(async (req, res) => {
5196
+ const id = String(req.params.id);
5197
+ const doc = await repository.deleteById(id);
5198
+ if (!doc) {
5199
+ throw new AppError(`${resourceName} not found`, 404, {
5200
+ code: "RESOURCE_NOT_FOUND"
5201
+ });
5202
+ }
5203
+ return ApiResponse.noContent(res);
5204
+ })
5205
+ };
5206
+ }
5207
+ };
5208
+
5209
+ // src/http/middleware/AuthMiddleware.ts
5091
5210
  var authMiddleware = (_req, _res, next) => {
5092
5211
  next();
5093
5212
  };
5094
5213
 
5095
- // src/middlewares/error.middleware.ts
5096
- var handleCastErrorDB = (err) => new AppError(`Invalid ${err.path}: ${err.value}`, 400, {
5214
+ // src/http/middleware/ErrorMiddleware.ts
5215
+ var handleCastError = (err) => new AppError(`Invalid ${err.path}: ${err.value}`, 400, {
5097
5216
  code: "INVALID_ID"
5098
5217
  });
5099
- var handleDuplicateFieldsDB = (err) => {
5100
- const value = err.keyValue ? JSON.stringify(err.keyValue) : "duplicate value";
5101
- return new AppError(`Duplicate field value: ${value}`, 400, {
5218
+ var handleDuplicateKey = (err) => {
5219
+ const field = err.keyValue ? Object.keys(err.keyValue)[0] : "field";
5220
+ return new AppError(`${field} already exists`, 400, {
5102
5221
  code: "DUPLICATE_FIELD"
5103
5222
  });
5104
5223
  };
5105
- var handleValidationErrorDB = (err) => {
5106
- const errors = Object.values(err.errors).map((el) => el.message);
5107
- return new AppError("Invalid input data", 400, {
5224
+ var handleValidationError = (err) => {
5225
+ const errors = Object.entries(err.errors).reduce(
5226
+ (acc, [key, value]) => {
5227
+ acc[key] = value.message;
5228
+ return acc;
5229
+ },
5230
+ {}
5231
+ );
5232
+ return new AppError("Validation failed", 400, {
5108
5233
  code: "VALIDATION_ERROR",
5109
5234
  details: errors
5110
5235
  });
5111
5236
  };
5112
- var handleJWTError = () => new AppError("Invalid token. Please log in again.", 401);
5113
- var handleJWTExpiredError = () => new AppError("Your token has expired. Please log in again.", 401);
5114
- var GlobalErrorHandler = (isProd = false) => (err, _req, res, _next) => {
5237
+ var handleJWTError = () => new AppError("Invalid token", 401);
5238
+ var handleJWTExpiredError = () => new AppError("Token expired", 401);
5239
+ var ErrorMiddleware = (isProduction = false) => (err, _req, res, _next) => {
5115
5240
  let error;
5116
5241
  if (err instanceof AppError) {
5117
5242
  error = err;
@@ -5123,15 +5248,15 @@ var GlobalErrorHandler = (isProd = false) => (err, _req, res, _next) => {
5123
5248
  error.isOperational = false;
5124
5249
  }
5125
5250
  if (err && err.name === "CastError") {
5126
- error = handleCastErrorDB(err);
5251
+ error = handleCastError(err);
5127
5252
  }
5128
5253
  if (err && typeof err === "object" && err.code === 11e3) {
5129
- error = handleDuplicateFieldsDB(
5254
+ error = handleDuplicateKey(
5130
5255
  err
5131
5256
  );
5132
5257
  }
5133
5258
  if (err && err.name === "ValidationError") {
5134
- error = handleValidationErrorDB(err);
5259
+ error = handleValidationError(err);
5135
5260
  }
5136
5261
  if (err?.name === "JsonWebTokenError") {
5137
5262
  error = handleJWTError();
@@ -5142,37 +5267,157 @@ var GlobalErrorHandler = (isProd = false) => (err, _req, res, _next) => {
5142
5267
  if (!error.isOperational) {
5143
5268
  logger.danger("Programming error", err);
5144
5269
  } else {
5145
- logger.danger("Operational error", { message: error.message });
5270
+ logger.danger("Operational error", {
5271
+ message: error.message
5272
+ });
5146
5273
  }
5147
- if (!isProd) {
5148
- return APIResponse.send(res, {
5149
- success: false,
5274
+ const response = {
5275
+ success: false,
5276
+ message: error.isOperational ? error.message : "Something went wrong",
5277
+ ...error.details && {
5278
+ meta: {
5279
+ errors: error.details
5280
+ }
5281
+ }
5282
+ };
5283
+ if (!isProduction) {
5284
+ return ApiResponse.send(res, {
5150
5285
  statusCode: error.statusCode,
5151
- message: error.message,
5286
+ ...response,
5152
5287
  data: {
5153
- stack: err?.stack,
5154
- details: error.details
5288
+ stack: err?.stack
5155
5289
  }
5156
5290
  });
5157
5291
  }
5158
- return APIResponse.send(res, {
5159
- success: false,
5292
+ return ApiResponse.send(res, {
5160
5293
  statusCode: error.statusCode,
5161
- message: error.isOperational ? error.message : "Something went wrong"
5294
+ ...response
5162
5295
  });
5163
5296
  };
5164
5297
 
5165
- // src/middlewares/validate.middleware.ts
5298
+ // src/http/validation/Validator.ts
5299
+ var isJoiSchema = (schema) => {
5300
+ return typeof schema === "object" && schema !== null && "validate" in schema;
5301
+ };
5302
+ var isZodSchema = (schema) => {
5303
+ return typeof schema === "object" && schema !== null && "safeParse" in schema;
5304
+ };
5305
+
5306
+ // src/http/validation/JoiValidator.ts
5307
+ var JoiValidator = class {
5308
+ constructor(schema) {
5309
+ this.schema = schema;
5310
+ }
5311
+ schema;
5312
+ validate(data) {
5313
+ const result = this.schema.validate(data, {
5314
+ abortEarly: false,
5315
+ stripUnknown: true
5316
+ });
5317
+ if (result.error) {
5318
+ const errors = {};
5319
+ result.error.details.forEach((detail) => {
5320
+ const field = detail.path.join(".");
5321
+ errors[field] = detail.message;
5322
+ });
5323
+ return {
5324
+ success: false,
5325
+ errors
5326
+ };
5327
+ }
5328
+ return {
5329
+ success: true,
5330
+ data: result.value
5331
+ };
5332
+ }
5333
+ };
5334
+
5335
+ // src/http/validation/ZodValidator.ts
5336
+ var ZodValidator = class {
5337
+ constructor(schema) {
5338
+ this.schema = schema;
5339
+ }
5340
+ schema;
5341
+ validate(data) {
5342
+ const result = this.schema.safeParse(data);
5343
+ if (!result.success) {
5344
+ const errors = {};
5345
+ result.error.issues.forEach((issue) => {
5346
+ const field = issue.path.join(".");
5347
+ errors[field] = issue.message;
5348
+ });
5349
+ return {
5350
+ success: false,
5351
+ errors
5352
+ };
5353
+ }
5354
+ return {
5355
+ success: true,
5356
+ data: result.data
5357
+ };
5358
+ }
5359
+ };
5360
+
5361
+ // src/http/middleware/ValidationMiddleware.ts
5166
5362
  var validate = (schema) => (req, _res, next) => {
5167
- const { error } = schema.validate(req.body);
5168
- if (error) {
5169
- return next(error);
5363
+ let result;
5364
+ if (isJoiSchema(schema)) {
5365
+ result = new JoiValidator(schema).validate(req.body);
5366
+ } else if (isZodSchema(schema)) {
5367
+ result = new ZodValidator(schema).validate(req.body);
5368
+ } else {
5369
+ return next(new AppError("Unsupported validation schema", 500));
5370
+ }
5371
+ if (!result.success) {
5372
+ return next(
5373
+ new AppError("Validation failed", 400, {
5374
+ code: "VALIDATION_ERROR",
5375
+ details: result.errors
5376
+ })
5377
+ );
5170
5378
  }
5379
+ req.body = result.data;
5171
5380
  next();
5172
5381
  };
5173
5382
 
5174
- // src/repositories/providers/mongo.repository.ts
5175
- var BaseMongoRepository = class {
5383
+ // src/http/utils/PickFields.ts
5384
+ function pickFields(obj, fields, options = {}) {
5385
+ const removeUndefined = options.removeUndefined ?? false;
5386
+ const removeNull = options.removeNull ?? false;
5387
+ const removeEmptyString = options.removeEmptyString ?? false;
5388
+ const strict = options.strict ?? false;
5389
+ const defaults = options.defaults ?? {};
5390
+ const rename = options.rename ?? {};
5391
+ const transform = options.transform ?? {};
5392
+ const result = {};
5393
+ if (strict) {
5394
+ for (const key of Object.keys(obj)) {
5395
+ if (!fields.includes(key)) {
5396
+ throw new Error(`Unknown field: ${key}`);
5397
+ }
5398
+ }
5399
+ }
5400
+ for (const field of fields) {
5401
+ let value = obj[field];
5402
+ if (value === void 0 && field in defaults) {
5403
+ value = defaults[field];
5404
+ }
5405
+ if (removeUndefined && value === void 0) continue;
5406
+ if (removeNull && value === null) continue;
5407
+ if (removeEmptyString && value === "") continue;
5408
+ if (transform[field]) {
5409
+ value = transform[field](value);
5410
+ }
5411
+ const outputKey = rename[field] ?? field;
5412
+ if (Object.prototype.hasOwnProperty.call(obj, field) || field in defaults) {
5413
+ result[outputKey] = value;
5414
+ }
5415
+ }
5416
+ return result;
5417
+ }
5418
+
5419
+ // src/repositories/providers/MongoRepository.ts
5420
+ var MongoRepository = class {
5176
5421
  model;
5177
5422
  constructor(model) {
5178
5423
  this.model = model;
@@ -5726,6 +5971,6 @@ function sleep(ms) {
5726
5971
  return new Promise((resolve) => setTimeout(resolve, ms));
5727
5972
  }
5728
5973
 
5729
- export { APIFactory, apiFeatures_default as APIFeatures, APIResponse, AVLTree, AdjacencyList, AdjacencyMatrix, AppError, AzureBlobStorageService, BPlusTree, BTree, BaseMongoRepository, BinaryHeap, BinarySearchTree, BinaryTree, BloomFilter, CircularArray, CircularLinkedList, CircularQueue, CloudinaryService, ConsistentHash, CountMinSketch, Deque, DirectedGraph, DisjointSetUnion, DoublyLinkedList, DynamicArray, EmailService, FenwickTree, FibNode, FibonacciHeap, GlobalErrorHandler, Graph, HashMap, HashSet, HyperLogLog, IntervalTree, JWTService, KDTree, LFUCache, LRUCache, MaxHeap, MaxStack, MinHeap, MinStack, MulterFileHandlerService, MultiSet, Node, OrderedSet, PairingHeap, PairingNode, PriorityQueue, QuadTree, Queue, RadixTree, RedBlackTree, SMTPProvider, SegmentTree, Set2 as Set, SinglyLinkedList, SparseTable, SplayTree, Stack, StaticArray, SuffixArray, SuffixTree, TemplateEngine, TernarySearchTree, TreeNode, Trie, asyncHandler, authMiddleware, connectMongoDB, createAllowedOrigins, createCorsOptions, days, errorToString, hours, loadEnv, logger, milliseconds, minutes, pickFields, seconds, sleep, toHours, toMinutes, toSeconds, validate };
5974
+ export { AVLTree, AdjacencyList, AdjacencyMatrix, ApiFeatures, ApiResponse, AppError, AsyncHandler, AzureBlobStorageService, BPlusTree, BTree, BinaryHeap, BinarySearchTree, BinaryTree, BloomFilter, CircularArray, CircularLinkedList, CircularQueue, CloudinaryService, ConsistentHash, CountMinSketch, CrudControllerFactory, DEFAULT_LIMIT, DEFAULT_PAGE, DEFAULT_SORT_DIRECTION, DatabaseManager, Deque, DirectedGraph, DisjointSetUnion, DoublyLinkedList, DynamicArray, EmailService, ErrorMiddleware, FenwickTree, FibNode, FibonacciHeap, Graph, HashMap, HashSet, HyperLogLog, IntervalTree, JWTService, JoiValidator, KDTree, LFUCache, LRUCache, MaxHeap, MaxStack, MinHeap, MinStack, MongoDatabaseProvider, MongoRepository, MulterFileHandlerService, MultiSet, Node, OrderedSet, PairingHeap, PairingNode, PriorityQueue, QUERY_RESERVED_FIELDS, QuadTree, Queue, RadixTree, RedBlackTree, SMTPProvider, SUPPORTED_OPERATORS, SegmentTree, Set2 as Set, SinglyLinkedList, SparseTable, SplayTree, Stack, StaticArray, SuffixArray, SuffixTree, TemplateEngine, TernarySearchTree, TreeNode, Trie, ZodValidator, authMiddleware, createAllowedOrigins, createCorsOptions, days, errorToString, hours, isJoiSchema, isZodSchema, loadEnv, logger, milliseconds, minutes, pickFields, seconds, sleep, toHours, toMinutes, toSeconds, validate };
5730
5975
  //# sourceMappingURL=index.js.map
5731
5976
  //# sourceMappingURL=index.js.map