elseware-nodejs 1.8.7 → 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,22 +4840,403 @@ 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) => {
5233
+ var handleDuplicateKey = (err) => {
5115
5234
  const field = err.keyValue ? Object.keys(err.keyValue)[0] : "field";
5116
5235
  return new AppError(`${field} already exists`, 400, {
5117
5236
  code: "DUPLICATE_FIELD"
5118
5237
  });
5119
5238
  };
5120
- var handleValidationErrorDB = (err) => {
5239
+ var handleValidationError = (err) => {
5121
5240
  const errors = Object.entries(err.errors).reduce(
5122
5241
  (acc, [key, value]) => {
5123
5242
  acc[key] = value.message;
@@ -5128,12 +5247,11 @@ var handleValidationErrorDB = (err) => {
5128
5247
  return new AppError("Validation failed", 400, {
5129
5248
  code: "VALIDATION_ERROR",
5130
5249
  details: errors
5131
- // structured field errors
5132
5250
  });
5133
5251
  };
5134
- var handleJWTError = () => new AppError("Invalid token. Please log in again.", 401);
5135
- var handleJWTExpiredError = () => new AppError("Your token has expired. Please log in again.", 401);
5136
- 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) => {
5137
5255
  let error;
5138
5256
  if (err instanceof AppError) {
5139
5257
  error = err;
@@ -5145,15 +5263,15 @@ var GlobalErrorHandler = (isProd = false) => (err, _req, res, _next) => {
5145
5263
  error.isOperational = false;
5146
5264
  }
5147
5265
  if (err && err.name === "CastError") {
5148
- error = handleCastErrorDB(err);
5266
+ error = handleCastError(err);
5149
5267
  }
5150
5268
  if (err && typeof err === "object" && err.code === 11e3) {
5151
- error = handleDuplicateFieldsDB(
5269
+ error = handleDuplicateKey(
5152
5270
  err
5153
5271
  );
5154
5272
  }
5155
5273
  if (err && err.name === "ValidationError") {
5156
- error = handleValidationErrorDB(err);
5274
+ error = handleValidationError(err);
5157
5275
  }
5158
5276
  if (err?.name === "JsonWebTokenError") {
5159
5277
  error = handleJWTError();
@@ -5164,9 +5282,11 @@ var GlobalErrorHandler = (isProd = false) => (err, _req, res, _next) => {
5164
5282
  if (!error.isOperational) {
5165
5283
  logger.danger("Programming error", err);
5166
5284
  } else {
5167
- logger.danger("Operational error", { message: error.message });
5285
+ logger.danger("Operational error", {
5286
+ message: error.message
5287
+ });
5168
5288
  }
5169
- const baseResponse = {
5289
+ const response = {
5170
5290
  success: false,
5171
5291
  message: error.isOperational ? error.message : "Something went wrong",
5172
5292
  ...error.details && {
@@ -5175,32 +5295,144 @@ var GlobalErrorHandler = (isProd = false) => (err, _req, res, _next) => {
5175
5295
  }
5176
5296
  }
5177
5297
  };
5178
- if (!isProd) {
5179
- return APIResponse.send(res, {
5298
+ if (!isProduction) {
5299
+ return ApiResponse.send(res, {
5180
5300
  statusCode: error.statusCode,
5181
- ...baseResponse,
5301
+ ...response,
5182
5302
  data: {
5183
5303
  stack: err?.stack
5184
5304
  }
5185
5305
  });
5186
5306
  }
5187
- return APIResponse.send(res, {
5307
+ return ApiResponse.send(res, {
5188
5308
  statusCode: error.statusCode,
5189
- ...baseResponse
5309
+ ...response
5190
5310
  });
5191
5311
  };
5192
5312
 
5193
- // 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
5194
5377
  var validate = (schema) => (req, _res, next) => {
5195
- const { error } = schema.validate(req.body);
5196
- if (error) {
5197
- 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
+ );
5198
5393
  }
5394
+ req.body = result.data;
5199
5395
  next();
5200
5396
  };
5201
5397
 
5202
- // src/repositories/providers/mongo.repository.ts
5203
- 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 {
5204
5436
  model;
5205
5437
  constructor(model) {
5206
5438
  this.model = model;
@@ -5754,17 +5986,16 @@ function sleep(ms) {
5754
5986
  return new Promise((resolve) => setTimeout(resolve, ms));
5755
5987
  }
5756
5988
 
5757
- exports.APIFactory = APIFactory;
5758
- exports.APIFeatures = apiFeatures_default;
5759
- exports.APIResponse = APIResponse;
5760
5989
  exports.AVLTree = AVLTree;
5761
5990
  exports.AdjacencyList = AdjacencyList;
5762
5991
  exports.AdjacencyMatrix = AdjacencyMatrix;
5992
+ exports.ApiFeatures = ApiFeatures;
5993
+ exports.ApiResponse = ApiResponse;
5763
5994
  exports.AppError = AppError;
5995
+ exports.AsyncHandler = AsyncHandler;
5764
5996
  exports.AzureBlobStorageService = AzureBlobStorageService;
5765
5997
  exports.BPlusTree = BPlusTree;
5766
5998
  exports.BTree = BTree;
5767
- exports.BaseMongoRepository = BaseMongoRepository;
5768
5999
  exports.BinaryHeap = BinaryHeap;
5769
6000
  exports.BinarySearchTree = BinarySearchTree;
5770
6001
  exports.BinaryTree = BinaryTree;
@@ -5775,22 +6006,28 @@ exports.CircularQueue = CircularQueue;
5775
6006
  exports.CloudinaryService = CloudinaryService;
5776
6007
  exports.ConsistentHash = ConsistentHash;
5777
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;
5778
6014
  exports.Deque = Deque;
5779
6015
  exports.DirectedGraph = DirectedGraph;
5780
6016
  exports.DisjointSetUnion = DisjointSetUnion;
5781
6017
  exports.DoublyLinkedList = DoublyLinkedList;
5782
6018
  exports.DynamicArray = DynamicArray;
5783
6019
  exports.EmailService = EmailService;
6020
+ exports.ErrorMiddleware = ErrorMiddleware;
5784
6021
  exports.FenwickTree = FenwickTree;
5785
6022
  exports.FibNode = FibNode;
5786
6023
  exports.FibonacciHeap = FibonacciHeap;
5787
- exports.GlobalErrorHandler = GlobalErrorHandler;
5788
6024
  exports.Graph = Graph;
5789
6025
  exports.HashMap = HashMap;
5790
6026
  exports.HashSet = HashSet;
5791
6027
  exports.HyperLogLog = HyperLogLog;
5792
6028
  exports.IntervalTree = IntervalTree;
5793
6029
  exports.JWTService = JWTService;
6030
+ exports.JoiValidator = JoiValidator;
5794
6031
  exports.KDTree = KDTree;
5795
6032
  exports.LFUCache = LFUCache;
5796
6033
  exports.LRUCache = LRUCache;
@@ -5798,6 +6035,8 @@ exports.MaxHeap = MaxHeap;
5798
6035
  exports.MaxStack = MaxStack;
5799
6036
  exports.MinHeap = MinHeap;
5800
6037
  exports.MinStack = MinStack;
6038
+ exports.MongoDatabaseProvider = MongoDatabaseProvider;
6039
+ exports.MongoRepository = MongoRepository;
5801
6040
  exports.MulterFileHandlerService = MulterFileHandlerService;
5802
6041
  exports.MultiSet = MultiSet;
5803
6042
  exports.Node = Node;
@@ -5805,11 +6044,13 @@ exports.OrderedSet = OrderedSet;
5805
6044
  exports.PairingHeap = PairingHeap;
5806
6045
  exports.PairingNode = PairingNode;
5807
6046
  exports.PriorityQueue = PriorityQueue;
6047
+ exports.QUERY_RESERVED_FIELDS = QUERY_RESERVED_FIELDS;
5808
6048
  exports.QuadTree = QuadTree;
5809
6049
  exports.Queue = Queue;
5810
6050
  exports.RadixTree = RadixTree;
5811
6051
  exports.RedBlackTree = RedBlackTree;
5812
6052
  exports.SMTPProvider = SMTPProvider;
6053
+ exports.SUPPORTED_OPERATORS = SUPPORTED_OPERATORS;
5813
6054
  exports.SegmentTree = SegmentTree;
5814
6055
  exports.Set = Set2;
5815
6056
  exports.SinglyLinkedList = SinglyLinkedList;
@@ -5823,14 +6064,15 @@ exports.TemplateEngine = TemplateEngine;
5823
6064
  exports.TernarySearchTree = TernarySearchTree;
5824
6065
  exports.TreeNode = TreeNode;
5825
6066
  exports.Trie = Trie;
5826
- exports.asyncHandler = asyncHandler;
6067
+ exports.ZodValidator = ZodValidator;
5827
6068
  exports.authMiddleware = authMiddleware;
5828
- exports.connectMongoDB = connectMongoDB;
5829
6069
  exports.createAllowedOrigins = createAllowedOrigins;
5830
6070
  exports.createCorsOptions = createCorsOptions;
5831
6071
  exports.days = days;
5832
6072
  exports.errorToString = errorToString;
5833
6073
  exports.hours = hours;
6074
+ exports.isJoiSchema = isJoiSchema;
6075
+ exports.isZodSchema = isZodSchema;
5834
6076
  exports.loadEnv = loadEnv;
5835
6077
  exports.logger = logger;
5836
6078
  exports.milliseconds = milliseconds;