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.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var mongoose = require('mongoose');
4
3
  var dotenv = require('dotenv');
4
+ var mongoose = require('mongoose');
5
5
  var storageBlob = require('@azure/storage-blob');
6
6
  var fs3 = require('fs');
7
7
  var cloudinaryModule = require('cloudinary');
@@ -14,8 +14,8 @@ var multer = require('multer');
14
14
 
15
15
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
16
16
 
17
- var mongoose__default = /*#__PURE__*/_interopDefault(mongoose);
18
17
  var dotenv__default = /*#__PURE__*/_interopDefault(dotenv);
18
+ var mongoose__default = /*#__PURE__*/_interopDefault(mongoose);
19
19
  var fs3__default = /*#__PURE__*/_interopDefault(fs3);
20
20
  var cloudinaryModule__default = /*#__PURE__*/_interopDefault(cloudinaryModule);
21
21
  var nodemailer__default = /*#__PURE__*/_interopDefault(nodemailer);
@@ -25,252 +25,7 @@ var juice__default = /*#__PURE__*/_interopDefault(juice);
25
25
  var jwt__default = /*#__PURE__*/_interopDefault(jwt);
26
26
  var multer__default = /*#__PURE__*/_interopDefault(multer);
27
27
 
28
- // src/errors/appError.ts
29
- var AppError = class extends Error {
30
- statusCode;
31
- status;
32
- isOperational;
33
- code;
34
- details;
35
- constructor(message, statusCode = 500, options) {
36
- super(message);
37
- this.statusCode = statusCode;
38
- this.status = `${statusCode}`.startsWith("4") ? "fail" : "error";
39
- this.isOperational = true;
40
- this.code = options?.code;
41
- this.details = options?.details;
42
- Error.captureStackTrace(this, this.constructor);
43
- }
44
- };
45
-
46
- // src/api/apiResponse.ts
47
- var APIResponse = class _APIResponse {
48
- static send(res, options) {
49
- const {
50
- statusCode = 200,
51
- success = true,
52
- message = "Success",
53
- data,
54
- meta
55
- } = options;
56
- if (statusCode === 204) {
57
- return res.status(204).send();
58
- }
59
- const body = {
60
- success,
61
- message,
62
- ...data !== void 0 && { data },
63
- ...meta !== void 0 && { meta }
64
- };
65
- return res.status(statusCode).json(body);
66
- }
67
- static ok(res, data, message = "Success", meta) {
68
- return _APIResponse.send(res, { statusCode: 200, data, message, meta });
69
- }
70
- static created(res, data, message = "Resource created successfully") {
71
- return _APIResponse.send(res, { statusCode: 201, data, message });
72
- }
73
- static noContent(res) {
74
- return _APIResponse.send(res, { statusCode: 204 });
75
- }
76
- static error(res, message = "Internal Server Error", statusCode = 500, meta) {
77
- return _APIResponse.send(res, {
78
- success: false,
79
- statusCode,
80
- message,
81
- meta
82
- });
83
- }
84
- static paginated(res, data, options, message = "Data fetched successfully") {
85
- return _APIResponse.ok(res, data, message, {
86
- total: options.total,
87
- page: options.page,
88
- limit: options.limit,
89
- totalPages: Math.ceil(options.total / options.limit)
90
- });
91
- }
92
- };
93
-
94
- // src/api/asyncHandler.ts
95
- var asyncHandler = (fn) => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next);
96
-
97
- // src/api/apiFeatures.ts
98
- var APIFeatures = class {
99
- query;
100
- queryString;
101
- page;
102
- limit;
103
- constructor(query, queryString) {
104
- this.query = query;
105
- this.queryString = queryString;
106
- this.page = 1;
107
- this.limit = 100;
108
- }
109
- filter() {
110
- const queryObj = { ...this.queryString };
111
- ["page", "sort", "limit", "fields"].forEach((el) => delete queryObj[el]);
112
- let queryStr = JSON.stringify(queryObj);
113
- queryStr = queryStr.replace(
114
- /\b(gte|gt|lte|lt|in)\b/g,
115
- (match) => `$${match}`
116
- );
117
- this.query = this.query.find(JSON.parse(queryStr));
118
- return this;
119
- }
120
- sort() {
121
- if (this.queryString.sort) {
122
- const sortBy = String(this.queryString.sort).split(",").join(" ");
123
- this.query = this.query.sort(sortBy);
124
- } else {
125
- this.query = this.query.sort("-createdAt");
126
- }
127
- return this;
128
- }
129
- limitFields() {
130
- if (this.queryString.fields) {
131
- const fields = String(this.queryString.fields).split(",").join(" ");
132
- this.query = this.query.select(fields);
133
- } else {
134
- this.query = this.query.select("-__v");
135
- }
136
- return this;
137
- }
138
- paginate() {
139
- this.page = Number(this.queryString.page) || 1;
140
- this.limit = Number(this.queryString.limit) || 100;
141
- const skip = (this.page - 1) * this.limit;
142
- this.query = this.query.skip(skip).limit(this.limit);
143
- return this;
144
- }
145
- populate(populateOptions) {
146
- if (populateOptions) {
147
- this.query = this.query.populate(populateOptions);
148
- }
149
- return this;
150
- }
151
- };
152
- var apiFeatures_default = APIFeatures;
153
-
154
- // src/api/apiFactory.ts
155
- var APIFactory = class {
156
- static getAll(Model, options = {}) {
157
- return asyncHandler(async (req, res) => {
158
- const filter = options.filter ? options.filter(req) : {};
159
- const features = new apiFeatures_default(Model.find(filter), req.query).filter().sort().limitFields().paginate().populate(options.populate);
160
- const docs = await features.query;
161
- APIResponse.ok(
162
- res,
163
- docs,
164
- options.message ?? "Records fetched successfully",
165
- {
166
- count: docs.length,
167
- page: features.page,
168
- limit: features.limit
169
- }
170
- );
171
- });
172
- }
173
- static getOne(Model, options = {}) {
174
- return asyncHandler(async (req, res) => {
175
- let query = Model.findById(req.params.id);
176
- if (options.populate) {
177
- query = query.populate(
178
- options.populate
179
- );
180
- }
181
- const doc = await query;
182
- if (!doc) {
183
- throw new AppError(
184
- options.notFoundMessage ?? "Resource not found",
185
- 404,
186
- { code: "RESOURCE_NOT_FOUND" }
187
- );
188
- }
189
- APIResponse.ok(res, doc);
190
- });
191
- }
192
- static createOne(Model, options = {}) {
193
- return asyncHandler(async (req, res) => {
194
- const doc = await Model.create(req.body);
195
- APIResponse.created(
196
- res,
197
- doc,
198
- options.message ?? "Resource created successfully"
199
- );
200
- });
201
- }
202
- static updateOne(Model, options = {}) {
203
- return asyncHandler(async (req, res) => {
204
- const doc = await Model.findByIdAndUpdate(
205
- req.params.id,
206
- req.body,
207
- {
208
- new: true,
209
- runValidators: true
210
- }
211
- );
212
- if (!doc) {
213
- throw new AppError(
214
- options.notFoundMessage ?? "Resource not found",
215
- 404
216
- );
217
- }
218
- APIResponse.ok(
219
- res,
220
- doc,
221
- options.message ?? "Resource updated successfully"
222
- );
223
- });
224
- }
225
- static deleteOne(Model, options = {}) {
226
- return asyncHandler(async (req, res) => {
227
- const doc = await Model.findByIdAndDelete(req.params.id);
228
- if (!doc) {
229
- throw new AppError(
230
- options.notFoundMessage ?? "Resource not found",
231
- 404
232
- );
233
- }
234
- APIResponse.noContent(res);
235
- });
236
- }
237
- };
238
-
239
- // src/api/pickFields.ts
240
- function pickFields(obj, fields, options = {}) {
241
- const removeUndefined = options.removeUndefined ?? false;
242
- const removeNull = options.removeNull ?? false;
243
- const removeEmptyString = options.removeEmptyString ?? false;
244
- const strict = options.strict ?? false;
245
- const defaults = options.defaults ?? {};
246
- const rename = options.rename ?? {};
247
- const transform = options.transform ?? {};
248
- const result = {};
249
- if (strict) {
250
- for (const key of Object.keys(obj)) {
251
- if (!fields.includes(key)) {
252
- throw new Error(`Unknown field: ${key}`);
253
- }
254
- }
255
- }
256
- for (const field of fields) {
257
- let value = obj[field];
258
- if (value === void 0 && field in defaults) {
259
- value = defaults[field];
260
- }
261
- if (removeUndefined && value === void 0) continue;
262
- if (removeNull && value === null) continue;
263
- if (removeEmptyString && value === "") continue;
264
- if (transform[field]) {
265
- value = transform[field](value);
266
- }
267
- const outputKey = rename[field] ?? field;
268
- if (Object.prototype.hasOwnProperty.call(obj, field) || field in defaults) {
269
- result[outputKey] = value;
270
- }
271
- }
272
- return result;
273
- }
28
+ // src/configs/env.ts
274
29
 
275
30
  // src/utils/errorToString.ts
276
31
  function errorToString(error) {
@@ -329,25 +84,7 @@ ${errorToString(error)}`);
329
84
  };
330
85
  var logger = new Logger();
331
86
 
332
- // src/configs/database.ts
333
- async function connectMongoDB(config) {
334
- try {
335
- mongoose__default.default.set("strictQuery", true);
336
- await mongoose__default.default.connect(config.uri);
337
- logger.success("Database connected successfully");
338
- process.on("SIGINT", async () => {
339
- await mongoose__default.default.connection.close();
340
- logger.success("Database connection closed");
341
- process.exit(0);
342
- });
343
- } catch (error) {
344
- logger.danger("Database connection failed");
345
- if (error instanceof Error) {
346
- logger.danger(error.message);
347
- }
348
- process.exit(1);
349
- }
350
- }
87
+ // src/configs/env.ts
351
88
  dotenv__default.default.config({ quiet: true });
352
89
  function loadEnv(options) {
353
90
  const result = options.schema.safeParse(process.env);
@@ -2182,6 +1919,7 @@ var CircularQueue = class {
2182
1919
  }
2183
1920
  this.data = new Array(capacity);
2184
1921
  }
1922
+ capacity;
2185
1923
  data;
2186
1924
  head = 0;
2187
1925
  tail = 0;
@@ -5102,31 +4840,418 @@ var SegmentTree = class {
5102
4840
  }
5103
4841
  };
5104
4842
 
5105
- // src/middlewares/auth.middleware.ts
4843
+ // src/database/DatabaseManager.ts
4844
+ var DatabaseManager = class {
4845
+ constructor(provider, options = {
4846
+ enableShutdownHooks: true
4847
+ }) {
4848
+ this.provider = provider;
4849
+ this.options = options;
4850
+ }
4851
+ provider;
4852
+ options;
4853
+ async connect() {
4854
+ try {
4855
+ await this.provider.connect();
4856
+ logger.success("Database connected successfully");
4857
+ if (this.options.enableShutdownHooks) {
4858
+ this.registerShutdownHooks();
4859
+ }
4860
+ } catch (error) {
4861
+ logger.danger("Database connection failed", error);
4862
+ throw error;
4863
+ }
4864
+ }
4865
+ async disconnect() {
4866
+ try {
4867
+ await this.provider.disconnect();
4868
+ logger.success("Database connection closed");
4869
+ } catch (error) {
4870
+ logger.danger("Database disconnect failed", error);
4871
+ throw error;
4872
+ }
4873
+ }
4874
+ isConnected() {
4875
+ return this.provider.isConnected();
4876
+ }
4877
+ getConnectionState() {
4878
+ return this.provider.getConnectionState();
4879
+ }
4880
+ registerShutdownHooks() {
4881
+ process.once("SIGINT", async () => {
4882
+ await this.disconnect();
4883
+ process.exit(0);
4884
+ });
4885
+ process.once("SIGTERM", async () => {
4886
+ await this.disconnect();
4887
+ process.exit(0);
4888
+ });
4889
+ }
4890
+ };
4891
+ var MongoDatabaseProvider = class {
4892
+ constructor(config) {
4893
+ this.config = config;
4894
+ }
4895
+ config;
4896
+ async connect() {
4897
+ mongoose__default.default.set("strictQuery", this.config.strictQuery ?? true);
4898
+ await mongoose__default.default.connect(this.config.uri);
4899
+ }
4900
+ async disconnect() {
4901
+ await mongoose__default.default.disconnect();
4902
+ }
4903
+ isConnected() {
4904
+ return mongoose__default.default.connection.readyState === 1;
4905
+ }
4906
+ getConnectionState() {
4907
+ switch (mongoose__default.default.connection.readyState) {
4908
+ case 0:
4909
+ return "disconnected";
4910
+ case 1:
4911
+ return "connected";
4912
+ case 2:
4913
+ return "connecting";
4914
+ case 3:
4915
+ return "disconnecting";
4916
+ default:
4917
+ return "unknown";
4918
+ }
4919
+ }
4920
+ };
4921
+
4922
+ // src/errors/appError.ts
4923
+ var AppError = class extends Error {
4924
+ statusCode;
4925
+ status;
4926
+ isOperational;
4927
+ code;
4928
+ details;
4929
+ constructor(message, statusCode = 500, options) {
4930
+ super(message);
4931
+ this.statusCode = statusCode;
4932
+ this.status = `${statusCode}`.startsWith("4") ? "fail" : "error";
4933
+ this.isOperational = true;
4934
+ this.code = options?.code;
4935
+ this.details = options?.details;
4936
+ Error.captureStackTrace(this, this.constructor);
4937
+ }
4938
+ };
4939
+
4940
+ // src/http/handlers/AsyncHandler.ts
4941
+ var AsyncHandler = (fn) => (req, res, next) => {
4942
+ Promise.resolve(fn(req, res, next)).catch(next);
4943
+ };
4944
+
4945
+ // src/http/query/QueryConstants.ts
4946
+ var QUERY_RESERVED_FIELDS = [
4947
+ "page",
4948
+ "limit",
4949
+ "sort",
4950
+ "fields",
4951
+ "populate"
4952
+ ];
4953
+ var DEFAULT_PAGE = 1;
4954
+ var DEFAULT_LIMIT = 100;
4955
+ var DEFAULT_SORT_DIRECTION = "asc";
4956
+ var SUPPORTED_OPERATORS = [
4957
+ "eq",
4958
+ "ne",
4959
+ "gt",
4960
+ "gte",
4961
+ "lt",
4962
+ "lte",
4963
+ "in",
4964
+ "nin",
4965
+ "contains",
4966
+ "startsWith",
4967
+ "endsWith"
4968
+ ];
4969
+
4970
+ // src/http/query/ApiFeatures.ts
4971
+ var ApiFeatures = class {
4972
+ static parse(query) {
4973
+ return {
4974
+ filters: this.parseFilters(query),
4975
+ sort: this.parseSort(query),
4976
+ fields: this.parseFields(query),
4977
+ pagination: this.parsePagination(query),
4978
+ populate: this.parsePopulate(query)
4979
+ };
4980
+ }
4981
+ static parseFilters(query) {
4982
+ const filters = [];
4983
+ Object.entries(query).forEach(([key, value]) => {
4984
+ if (QUERY_RESERVED_FIELDS.includes(key)) {
4985
+ return;
4986
+ }
4987
+ const match = key.match(
4988
+ /^(.+)\[(eq|ne|gt|gte|lt|lte|in|nin|contains|startsWith|endsWith)\]$/
4989
+ );
4990
+ if (match) {
4991
+ filters.push({
4992
+ field: match[1],
4993
+ operator: match[2],
4994
+ value
4995
+ });
4996
+ return;
4997
+ }
4998
+ filters.push({
4999
+ field: key,
5000
+ operator: "eq",
5001
+ value
5002
+ });
5003
+ });
5004
+ return filters;
5005
+ }
5006
+ static parseSort(query) {
5007
+ const sort = query.sort?.toString();
5008
+ if (!sort) {
5009
+ return [];
5010
+ }
5011
+ return sort.split(",").filter(Boolean).map((item) => {
5012
+ if (item.startsWith("-")) {
5013
+ return {
5014
+ field: item.substring(1),
5015
+ direction: "desc"
5016
+ };
5017
+ }
5018
+ return {
5019
+ field: item,
5020
+ direction: "asc"
5021
+ };
5022
+ });
5023
+ }
5024
+ static parseFields(query) {
5025
+ const fields = query.fields?.toString();
5026
+ if (!fields) {
5027
+ return [];
5028
+ }
5029
+ return fields.split(",").map((field) => field.trim()).filter(Boolean);
5030
+ }
5031
+ static parsePopulate(query) {
5032
+ const populate = query.populate?.toString();
5033
+ if (!populate) {
5034
+ return [];
5035
+ }
5036
+ return populate.split(",").map((field) => field.trim()).filter(Boolean);
5037
+ }
5038
+ static parsePagination(query) {
5039
+ const page = Number(query.page) || DEFAULT_PAGE;
5040
+ const limit = Number(query.limit) || DEFAULT_LIMIT;
5041
+ return {
5042
+ page,
5043
+ limit
5044
+ };
5045
+ }
5046
+ };
5047
+
5048
+ // src/http/responses/ApiResponse.ts
5049
+ var ApiResponse = class _ApiResponse {
5050
+ static send(res, options) {
5051
+ const {
5052
+ statusCode = 200,
5053
+ success = true,
5054
+ message = "Success",
5055
+ data,
5056
+ meta
5057
+ } = options;
5058
+ if (statusCode === 204) {
5059
+ return res.status(204).send();
5060
+ }
5061
+ const body = {
5062
+ success,
5063
+ message
5064
+ };
5065
+ if (data !== void 0) {
5066
+ body.data = data;
5067
+ }
5068
+ if (meta !== void 0) {
5069
+ body.meta = meta;
5070
+ }
5071
+ return res.status(statusCode).json(body);
5072
+ }
5073
+ static ok(res, data, message = "Success", meta) {
5074
+ return _ApiResponse.send(res, {
5075
+ statusCode: 200,
5076
+ success: true,
5077
+ message,
5078
+ data,
5079
+ meta
5080
+ });
5081
+ }
5082
+ static created(res, data, message = "Resource created successfully") {
5083
+ return _ApiResponse.send(res, {
5084
+ statusCode: 201,
5085
+ success: true,
5086
+ message,
5087
+ data
5088
+ });
5089
+ }
5090
+ static accepted(res, data, message = "Request accepted") {
5091
+ return _ApiResponse.send(res, {
5092
+ statusCode: 202,
5093
+ success: true,
5094
+ message,
5095
+ data
5096
+ });
5097
+ }
5098
+ static noContent(res) {
5099
+ return _ApiResponse.send(res, {
5100
+ statusCode: 204
5101
+ });
5102
+ }
5103
+ static error(res, message = "Internal Server Error", statusCode = 500, meta) {
5104
+ return _ApiResponse.send(res, {
5105
+ success: false,
5106
+ statusCode,
5107
+ message,
5108
+ meta
5109
+ });
5110
+ }
5111
+ static paginated(res, data, options, message = "Data fetched successfully") {
5112
+ const meta = {
5113
+ total: options.total,
5114
+ page: options.page,
5115
+ limit: options.limit,
5116
+ totalPages: Math.ceil(options.total / options.limit)
5117
+ };
5118
+ return _ApiResponse.ok(res, data, message, meta);
5119
+ }
5120
+ };
5121
+
5122
+ // src/http/controllers/CrudControllerFactory.ts
5123
+ var CrudControllerFactory = class {
5124
+ static create(repository, options = {}) {
5125
+ const resourceName = options.resourceName ?? "Resource";
5126
+ return {
5127
+ /**
5128
+ * GET /
5129
+ */
5130
+ getAll: AsyncHandler(async (req, res) => {
5131
+ const parsed = ApiFeatures.parse(req.query);
5132
+ const baseFilter = options.filter?.(req) ?? {};
5133
+ const filter = {
5134
+ ...baseFilter
5135
+ };
5136
+ parsed.filters.forEach((item) => {
5137
+ if (item.operator === "eq") {
5138
+ filter[item.field] = item.value;
5139
+ }
5140
+ });
5141
+ const queryOptions = {
5142
+ limit: parsed.pagination.limit,
5143
+ skip: (parsed.pagination.page - 1) * parsed.pagination.limit,
5144
+ populate: parsed.populate
5145
+ };
5146
+ if (parsed.sort.length > 0) {
5147
+ queryOptions.sort = Object.fromEntries(
5148
+ parsed.sort.map((item) => [
5149
+ item.field,
5150
+ item.direction === "asc" ? 1 : -1
5151
+ ])
5152
+ );
5153
+ }
5154
+ if (parsed.fields.length > 0) {
5155
+ queryOptions.select = parsed.fields;
5156
+ }
5157
+ const docs = await repository.findAll(filter, queryOptions);
5158
+ const total = await repository.count(filter);
5159
+ return ApiResponse.paginated(
5160
+ res,
5161
+ docs,
5162
+ {
5163
+ total,
5164
+ page: parsed.pagination.page,
5165
+ limit: parsed.pagination.limit
5166
+ },
5167
+ `${resourceName} list fetched successfully`
5168
+ );
5169
+ }),
5170
+ /**
5171
+ * GET /:id
5172
+ */
5173
+ getOne: AsyncHandler(async (req, res) => {
5174
+ const id = String(req.params.id);
5175
+ const doc = await repository.findById(id);
5176
+ if (!doc) {
5177
+ throw new AppError(`${resourceName} not found`, 404, {
5178
+ code: "RESOURCE_NOT_FOUND"
5179
+ });
5180
+ }
5181
+ return ApiResponse.ok(res, doc, `${resourceName} fetched successfully`);
5182
+ }),
5183
+ /**
5184
+ * POST /
5185
+ */
5186
+ create: AsyncHandler(async (req, res) => {
5187
+ const doc = await repository.create(req.body);
5188
+ return ApiResponse.created(
5189
+ res,
5190
+ doc,
5191
+ `${resourceName} created successfully`
5192
+ );
5193
+ }),
5194
+ /**
5195
+ * PATCH /:id
5196
+ */
5197
+ update: AsyncHandler(async (req, res) => {
5198
+ const id = String(req.params.id);
5199
+ const doc = await repository.updateById(id, req.body);
5200
+ if (!doc) {
5201
+ throw new AppError(`${resourceName} not found`, 404, {
5202
+ code: "RESOURCE_NOT_FOUND"
5203
+ });
5204
+ }
5205
+ return ApiResponse.ok(res, doc, `${resourceName} updated successfully`);
5206
+ }),
5207
+ /**
5208
+ * DELETE /:id
5209
+ */
5210
+ delete: AsyncHandler(async (req, res) => {
5211
+ const id = String(req.params.id);
5212
+ const doc = await repository.deleteById(id);
5213
+ if (!doc) {
5214
+ throw new AppError(`${resourceName} not found`, 404, {
5215
+ code: "RESOURCE_NOT_FOUND"
5216
+ });
5217
+ }
5218
+ return ApiResponse.noContent(res);
5219
+ })
5220
+ };
5221
+ }
5222
+ };
5223
+
5224
+ // src/http/middleware/AuthMiddleware.ts
5106
5225
  var authMiddleware = (_req, _res, next) => {
5107
5226
  next();
5108
5227
  };
5109
5228
 
5110
- // src/middlewares/error.middleware.ts
5111
- var handleCastErrorDB = (err) => new AppError(`Invalid ${err.path}: ${err.value}`, 400, {
5229
+ // src/http/middleware/ErrorMiddleware.ts
5230
+ var handleCastError = (err) => new AppError(`Invalid ${err.path}: ${err.value}`, 400, {
5112
5231
  code: "INVALID_ID"
5113
5232
  });
5114
- var handleDuplicateFieldsDB = (err) => {
5115
- const value = err.keyValue ? JSON.stringify(err.keyValue) : "duplicate value";
5116
- return new AppError(`Duplicate field value: ${value}`, 400, {
5233
+ var handleDuplicateKey = (err) => {
5234
+ const field = err.keyValue ? Object.keys(err.keyValue)[0] : "field";
5235
+ return new AppError(`${field} already exists`, 400, {
5117
5236
  code: "DUPLICATE_FIELD"
5118
5237
  });
5119
5238
  };
5120
- var handleValidationErrorDB = (err) => {
5121
- const errors = Object.values(err.errors).map((el) => el.message);
5122
- return new AppError("Invalid input data", 400, {
5239
+ var handleValidationError = (err) => {
5240
+ const errors = Object.entries(err.errors).reduce(
5241
+ (acc, [key, value]) => {
5242
+ acc[key] = value.message;
5243
+ return acc;
5244
+ },
5245
+ {}
5246
+ );
5247
+ return new AppError("Validation failed", 400, {
5123
5248
  code: "VALIDATION_ERROR",
5124
5249
  details: errors
5125
5250
  });
5126
5251
  };
5127
- var handleJWTError = () => new AppError("Invalid token. Please log in again.", 401);
5128
- var handleJWTExpiredError = () => new AppError("Your token has expired. Please log in again.", 401);
5129
- var GlobalErrorHandler = (isProd = false) => (err, _req, res, _next) => {
5252
+ var handleJWTError = () => new AppError("Invalid token", 401);
5253
+ var handleJWTExpiredError = () => new AppError("Token expired", 401);
5254
+ var ErrorMiddleware = (isProduction = false) => (err, _req, res, _next) => {
5130
5255
  let error;
5131
5256
  if (err instanceof AppError) {
5132
5257
  error = err;
@@ -5138,15 +5263,15 @@ var GlobalErrorHandler = (isProd = false) => (err, _req, res, _next) => {
5138
5263
  error.isOperational = false;
5139
5264
  }
5140
5265
  if (err && err.name === "CastError") {
5141
- error = handleCastErrorDB(err);
5266
+ error = handleCastError(err);
5142
5267
  }
5143
5268
  if (err && typeof err === "object" && err.code === 11e3) {
5144
- error = handleDuplicateFieldsDB(
5269
+ error = handleDuplicateKey(
5145
5270
  err
5146
5271
  );
5147
5272
  }
5148
5273
  if (err && err.name === "ValidationError") {
5149
- error = handleValidationErrorDB(err);
5274
+ error = handleValidationError(err);
5150
5275
  }
5151
5276
  if (err?.name === "JsonWebTokenError") {
5152
5277
  error = handleJWTError();
@@ -5157,37 +5282,157 @@ var GlobalErrorHandler = (isProd = false) => (err, _req, res, _next) => {
5157
5282
  if (!error.isOperational) {
5158
5283
  logger.danger("Programming error", err);
5159
5284
  } else {
5160
- logger.danger("Operational error", { message: error.message });
5285
+ logger.danger("Operational error", {
5286
+ message: error.message
5287
+ });
5161
5288
  }
5162
- if (!isProd) {
5163
- return APIResponse.send(res, {
5164
- success: false,
5289
+ const response = {
5290
+ success: false,
5291
+ message: error.isOperational ? error.message : "Something went wrong",
5292
+ ...error.details && {
5293
+ meta: {
5294
+ errors: error.details
5295
+ }
5296
+ }
5297
+ };
5298
+ if (!isProduction) {
5299
+ return ApiResponse.send(res, {
5165
5300
  statusCode: error.statusCode,
5166
- message: error.message,
5301
+ ...response,
5167
5302
  data: {
5168
- stack: err?.stack,
5169
- details: error.details
5303
+ stack: err?.stack
5170
5304
  }
5171
5305
  });
5172
5306
  }
5173
- return APIResponse.send(res, {
5174
- success: false,
5307
+ return ApiResponse.send(res, {
5175
5308
  statusCode: error.statusCode,
5176
- message: error.isOperational ? error.message : "Something went wrong"
5309
+ ...response
5177
5310
  });
5178
5311
  };
5179
5312
 
5180
- // src/middlewares/validate.middleware.ts
5313
+ // src/http/validation/Validator.ts
5314
+ var isJoiSchema = (schema) => {
5315
+ return typeof schema === "object" && schema !== null && "validate" in schema;
5316
+ };
5317
+ var isZodSchema = (schema) => {
5318
+ return typeof schema === "object" && schema !== null && "safeParse" in schema;
5319
+ };
5320
+
5321
+ // src/http/validation/JoiValidator.ts
5322
+ var JoiValidator = class {
5323
+ constructor(schema) {
5324
+ this.schema = schema;
5325
+ }
5326
+ schema;
5327
+ validate(data) {
5328
+ const result = this.schema.validate(data, {
5329
+ abortEarly: false,
5330
+ stripUnknown: true
5331
+ });
5332
+ if (result.error) {
5333
+ const errors = {};
5334
+ result.error.details.forEach((detail) => {
5335
+ const field = detail.path.join(".");
5336
+ errors[field] = detail.message;
5337
+ });
5338
+ return {
5339
+ success: false,
5340
+ errors
5341
+ };
5342
+ }
5343
+ return {
5344
+ success: true,
5345
+ data: result.value
5346
+ };
5347
+ }
5348
+ };
5349
+
5350
+ // src/http/validation/ZodValidator.ts
5351
+ var ZodValidator = class {
5352
+ constructor(schema) {
5353
+ this.schema = schema;
5354
+ }
5355
+ schema;
5356
+ validate(data) {
5357
+ const result = this.schema.safeParse(data);
5358
+ if (!result.success) {
5359
+ const errors = {};
5360
+ result.error.issues.forEach((issue) => {
5361
+ const field = issue.path.join(".");
5362
+ errors[field] = issue.message;
5363
+ });
5364
+ return {
5365
+ success: false,
5366
+ errors
5367
+ };
5368
+ }
5369
+ return {
5370
+ success: true,
5371
+ data: result.data
5372
+ };
5373
+ }
5374
+ };
5375
+
5376
+ // src/http/middleware/ValidationMiddleware.ts
5181
5377
  var validate = (schema) => (req, _res, next) => {
5182
- const { error } = schema.validate(req.body);
5183
- if (error) {
5184
- return next(error);
5378
+ let result;
5379
+ if (isJoiSchema(schema)) {
5380
+ result = new JoiValidator(schema).validate(req.body);
5381
+ } else if (isZodSchema(schema)) {
5382
+ result = new ZodValidator(schema).validate(req.body);
5383
+ } else {
5384
+ return next(new AppError("Unsupported validation schema", 500));
5385
+ }
5386
+ if (!result.success) {
5387
+ return next(
5388
+ new AppError("Validation failed", 400, {
5389
+ code: "VALIDATION_ERROR",
5390
+ details: result.errors
5391
+ })
5392
+ );
5185
5393
  }
5394
+ req.body = result.data;
5186
5395
  next();
5187
5396
  };
5188
5397
 
5189
- // src/repositories/providers/mongo.repository.ts
5190
- var BaseMongoRepository = class {
5398
+ // src/http/utils/PickFields.ts
5399
+ function pickFields(obj, fields, options = {}) {
5400
+ const removeUndefined = options.removeUndefined ?? false;
5401
+ const removeNull = options.removeNull ?? false;
5402
+ const removeEmptyString = options.removeEmptyString ?? false;
5403
+ const strict = options.strict ?? false;
5404
+ const defaults = options.defaults ?? {};
5405
+ const rename = options.rename ?? {};
5406
+ const transform = options.transform ?? {};
5407
+ const result = {};
5408
+ if (strict) {
5409
+ for (const key of Object.keys(obj)) {
5410
+ if (!fields.includes(key)) {
5411
+ throw new Error(`Unknown field: ${key}`);
5412
+ }
5413
+ }
5414
+ }
5415
+ for (const field of fields) {
5416
+ let value = obj[field];
5417
+ if (value === void 0 && field in defaults) {
5418
+ value = defaults[field];
5419
+ }
5420
+ if (removeUndefined && value === void 0) continue;
5421
+ if (removeNull && value === null) continue;
5422
+ if (removeEmptyString && value === "") continue;
5423
+ if (transform[field]) {
5424
+ value = transform[field](value);
5425
+ }
5426
+ const outputKey = rename[field] ?? field;
5427
+ if (Object.prototype.hasOwnProperty.call(obj, field) || field in defaults) {
5428
+ result[outputKey] = value;
5429
+ }
5430
+ }
5431
+ return result;
5432
+ }
5433
+
5434
+ // src/repositories/providers/MongoRepository.ts
5435
+ var MongoRepository = class {
5191
5436
  model;
5192
5437
  constructor(model) {
5193
5438
  this.model = model;
@@ -5741,17 +5986,16 @@ function sleep(ms) {
5741
5986
  return new Promise((resolve) => setTimeout(resolve, ms));
5742
5987
  }
5743
5988
 
5744
- exports.APIFactory = APIFactory;
5745
- exports.APIFeatures = apiFeatures_default;
5746
- exports.APIResponse = APIResponse;
5747
5989
  exports.AVLTree = AVLTree;
5748
5990
  exports.AdjacencyList = AdjacencyList;
5749
5991
  exports.AdjacencyMatrix = AdjacencyMatrix;
5992
+ exports.ApiFeatures = ApiFeatures;
5993
+ exports.ApiResponse = ApiResponse;
5750
5994
  exports.AppError = AppError;
5995
+ exports.AsyncHandler = AsyncHandler;
5751
5996
  exports.AzureBlobStorageService = AzureBlobStorageService;
5752
5997
  exports.BPlusTree = BPlusTree;
5753
5998
  exports.BTree = BTree;
5754
- exports.BaseMongoRepository = BaseMongoRepository;
5755
5999
  exports.BinaryHeap = BinaryHeap;
5756
6000
  exports.BinarySearchTree = BinarySearchTree;
5757
6001
  exports.BinaryTree = BinaryTree;
@@ -5762,22 +6006,28 @@ exports.CircularQueue = CircularQueue;
5762
6006
  exports.CloudinaryService = CloudinaryService;
5763
6007
  exports.ConsistentHash = ConsistentHash;
5764
6008
  exports.CountMinSketch = CountMinSketch;
6009
+ exports.CrudControllerFactory = CrudControllerFactory;
6010
+ exports.DEFAULT_LIMIT = DEFAULT_LIMIT;
6011
+ exports.DEFAULT_PAGE = DEFAULT_PAGE;
6012
+ exports.DEFAULT_SORT_DIRECTION = DEFAULT_SORT_DIRECTION;
6013
+ exports.DatabaseManager = DatabaseManager;
5765
6014
  exports.Deque = Deque;
5766
6015
  exports.DirectedGraph = DirectedGraph;
5767
6016
  exports.DisjointSetUnion = DisjointSetUnion;
5768
6017
  exports.DoublyLinkedList = DoublyLinkedList;
5769
6018
  exports.DynamicArray = DynamicArray;
5770
6019
  exports.EmailService = EmailService;
6020
+ exports.ErrorMiddleware = ErrorMiddleware;
5771
6021
  exports.FenwickTree = FenwickTree;
5772
6022
  exports.FibNode = FibNode;
5773
6023
  exports.FibonacciHeap = FibonacciHeap;
5774
- exports.GlobalErrorHandler = GlobalErrorHandler;
5775
6024
  exports.Graph = Graph;
5776
6025
  exports.HashMap = HashMap;
5777
6026
  exports.HashSet = HashSet;
5778
6027
  exports.HyperLogLog = HyperLogLog;
5779
6028
  exports.IntervalTree = IntervalTree;
5780
6029
  exports.JWTService = JWTService;
6030
+ exports.JoiValidator = JoiValidator;
5781
6031
  exports.KDTree = KDTree;
5782
6032
  exports.LFUCache = LFUCache;
5783
6033
  exports.LRUCache = LRUCache;
@@ -5785,6 +6035,8 @@ exports.MaxHeap = MaxHeap;
5785
6035
  exports.MaxStack = MaxStack;
5786
6036
  exports.MinHeap = MinHeap;
5787
6037
  exports.MinStack = MinStack;
6038
+ exports.MongoDatabaseProvider = MongoDatabaseProvider;
6039
+ exports.MongoRepository = MongoRepository;
5788
6040
  exports.MulterFileHandlerService = MulterFileHandlerService;
5789
6041
  exports.MultiSet = MultiSet;
5790
6042
  exports.Node = Node;
@@ -5792,11 +6044,13 @@ exports.OrderedSet = OrderedSet;
5792
6044
  exports.PairingHeap = PairingHeap;
5793
6045
  exports.PairingNode = PairingNode;
5794
6046
  exports.PriorityQueue = PriorityQueue;
6047
+ exports.QUERY_RESERVED_FIELDS = QUERY_RESERVED_FIELDS;
5795
6048
  exports.QuadTree = QuadTree;
5796
6049
  exports.Queue = Queue;
5797
6050
  exports.RadixTree = RadixTree;
5798
6051
  exports.RedBlackTree = RedBlackTree;
5799
6052
  exports.SMTPProvider = SMTPProvider;
6053
+ exports.SUPPORTED_OPERATORS = SUPPORTED_OPERATORS;
5800
6054
  exports.SegmentTree = SegmentTree;
5801
6055
  exports.Set = Set2;
5802
6056
  exports.SinglyLinkedList = SinglyLinkedList;
@@ -5810,14 +6064,15 @@ exports.TemplateEngine = TemplateEngine;
5810
6064
  exports.TernarySearchTree = TernarySearchTree;
5811
6065
  exports.TreeNode = TreeNode;
5812
6066
  exports.Trie = Trie;
5813
- exports.asyncHandler = asyncHandler;
6067
+ exports.ZodValidator = ZodValidator;
5814
6068
  exports.authMiddleware = authMiddleware;
5815
- exports.connectMongoDB = connectMongoDB;
5816
6069
  exports.createAllowedOrigins = createAllowedOrigins;
5817
6070
  exports.createCorsOptions = createCorsOptions;
5818
6071
  exports.days = days;
5819
6072
  exports.errorToString = errorToString;
5820
6073
  exports.hours = hours;
6074
+ exports.isJoiSchema = isJoiSchema;
6075
+ exports.isZodSchema = isZodSchema;
5821
6076
  exports.loadEnv = loadEnv;
5822
6077
  exports.logger = logger;
5823
6078
  exports.milliseconds = milliseconds;