elseware-nodejs 1.8.7 → 1.9.1

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,38 +5295,152 @@ 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));
5198
5385
  }
5386
+ if (!result.success) {
5387
+ return next(
5388
+ new AppError("Validation failed", 400, {
5389
+ code: "VALIDATION_ERROR",
5390
+ details: result.errors
5391
+ })
5392
+ );
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;
5207
5439
  }
5208
5440
  buildSelect(select) {
5209
- if (!select) return void 0;
5441
+ if (!select) {
5442
+ return void 0;
5443
+ }
5210
5444
  if (Array.isArray(select)) {
5211
5445
  return select.join(" ");
5212
5446
  }
@@ -5214,14 +5448,22 @@ var BaseMongoRepository = class {
5214
5448
  }
5215
5449
  applyQueryOptions(query, options) {
5216
5450
  const select = this.buildSelect(options.select);
5217
- if (select) query = query.select(select);
5218
- if (options.sort) query = query.sort(options.sort);
5219
- if (options.limit) query = query.limit(options.limit);
5220
- if (options.skip) query = query.skip(options.skip);
5451
+ if (select) {
5452
+ query = query.select(select);
5453
+ }
5454
+ if (options.sort) {
5455
+ query = query.sort(options.sort);
5456
+ }
5457
+ if (options.limit) {
5458
+ query = query.limit(options.limit);
5459
+ }
5460
+ if (options.skip) {
5461
+ query = query.skip(options.skip);
5462
+ }
5221
5463
  if (options.populate) {
5222
5464
  if (Array.isArray(options.populate)) {
5223
- options.populate.forEach((p) => {
5224
- query = query.populate(p);
5465
+ options.populate.forEach((item) => {
5466
+ query = query.populate(item);
5225
5467
  });
5226
5468
  } else {
5227
5469
  query = query.populate(options.populate);
@@ -5230,7 +5472,13 @@ var BaseMongoRepository = class {
5230
5472
  return query;
5231
5473
  }
5232
5474
  toPlain(doc) {
5233
- return doc.toObject();
5475
+ if (!doc) {
5476
+ return doc;
5477
+ }
5478
+ return typeof doc.toObject === "function" ? doc.toObject() : doc;
5479
+ }
5480
+ toPlainArray(docs) {
5481
+ return docs.map((doc) => this.toPlain(doc));
5234
5482
  }
5235
5483
  /**
5236
5484
  * Create
@@ -5241,7 +5489,7 @@ var BaseMongoRepository = class {
5241
5489
  }
5242
5490
  async createMany(data) {
5243
5491
  const docs = await this.model.insertMany(data);
5244
- return docs.map((doc) => this.toPlain(doc));
5492
+ return this.toPlainArray(docs);
5245
5493
  }
5246
5494
  /**
5247
5495
  * Read
@@ -5249,22 +5497,26 @@ var BaseMongoRepository = class {
5249
5497
  async findAll(filter = {}, options = {}) {
5250
5498
  let query = this.model.find(filter);
5251
5499
  query = this.applyQueryOptions(query, options);
5252
- return query.exec();
5500
+ return query.lean().exec();
5253
5501
  }
5254
5502
  async findById(id, options = {}) {
5255
5503
  let query = this.model.findById(id);
5256
5504
  query = this.applyQueryOptions(query, options);
5257
- return query.exec();
5505
+ return query.lean().exec();
5258
5506
  }
5259
5507
  async findOne(filter = {}, options = {}) {
5260
5508
  let query = this.model.findOne(filter);
5261
5509
  query = this.applyQueryOptions(query, options);
5262
- return query.exec();
5510
+ return query.lean().exec();
5263
5511
  }
5264
5512
  async findManyByIds(ids, options = {}) {
5265
- let query = this.model.find({ _id: { $in: ids } });
5513
+ let query = this.model.find({
5514
+ _id: {
5515
+ $in: ids
5516
+ }
5517
+ });
5266
5518
  query = this.applyQueryOptions(query, options);
5267
- return query.exec();
5519
+ return query.lean().exec();
5268
5520
  }
5269
5521
  async count(filter) {
5270
5522
  return this.model.countDocuments(filter).exec();
@@ -5277,12 +5529,22 @@ var BaseMongoRepository = class {
5277
5529
  * Update
5278
5530
  */
5279
5531
  async updateById(id, data, options) {
5280
- const finalOptions = { new: true, runValidators: true, ...options };
5281
- return this.model.findByIdAndUpdate(id, data, finalOptions).exec();
5532
+ const finalOptions = {
5533
+ new: true,
5534
+ runValidators: true,
5535
+ ...options
5536
+ };
5537
+ const doc = await this.model.findByIdAndUpdate(id, data, finalOptions).exec();
5538
+ return doc ? this.toPlain(doc) : null;
5282
5539
  }
5283
5540
  async updateOne(filter, data, options) {
5284
- const finalOptions = { new: true, runValidators: true, ...options };
5285
- return this.model.findOneAndUpdate(filter, data, finalOptions).exec();
5541
+ const finalOptions = {
5542
+ new: true,
5543
+ runValidators: true,
5544
+ ...options
5545
+ };
5546
+ const doc = await this.model.findOneAndUpdate(filter, data, finalOptions).exec();
5547
+ return doc ? this.toPlain(doc) : null;
5286
5548
  }
5287
5549
  async updateMany(filter, data) {
5288
5550
  const result = await this.model.updateMany(filter, data).exec();
@@ -5292,10 +5554,12 @@ var BaseMongoRepository = class {
5292
5554
  * Delete
5293
5555
  */
5294
5556
  async deleteById(id) {
5295
- return this.model.findByIdAndDelete(id).exec();
5557
+ const doc = await this.model.findByIdAndDelete(id).exec();
5558
+ return doc ? this.toPlain(doc) : null;
5296
5559
  }
5297
5560
  async deleteOne(filter) {
5298
- return this.model.findOneAndDelete(filter).exec();
5561
+ const doc = await this.model.findOneAndDelete(filter).exec();
5562
+ return doc ? this.toPlain(doc) : null;
5299
5563
  }
5300
5564
  async deleteMany(filter) {
5301
5565
  const result = await this.model.deleteMany(filter).exec();
@@ -5754,17 +6018,16 @@ function sleep(ms) {
5754
6018
  return new Promise((resolve) => setTimeout(resolve, ms));
5755
6019
  }
5756
6020
 
5757
- exports.APIFactory = APIFactory;
5758
- exports.APIFeatures = apiFeatures_default;
5759
- exports.APIResponse = APIResponse;
5760
6021
  exports.AVLTree = AVLTree;
5761
6022
  exports.AdjacencyList = AdjacencyList;
5762
6023
  exports.AdjacencyMatrix = AdjacencyMatrix;
6024
+ exports.ApiFeatures = ApiFeatures;
6025
+ exports.ApiResponse = ApiResponse;
5763
6026
  exports.AppError = AppError;
6027
+ exports.AsyncHandler = AsyncHandler;
5764
6028
  exports.AzureBlobStorageService = AzureBlobStorageService;
5765
6029
  exports.BPlusTree = BPlusTree;
5766
6030
  exports.BTree = BTree;
5767
- exports.BaseMongoRepository = BaseMongoRepository;
5768
6031
  exports.BinaryHeap = BinaryHeap;
5769
6032
  exports.BinarySearchTree = BinarySearchTree;
5770
6033
  exports.BinaryTree = BinaryTree;
@@ -5775,22 +6038,28 @@ exports.CircularQueue = CircularQueue;
5775
6038
  exports.CloudinaryService = CloudinaryService;
5776
6039
  exports.ConsistentHash = ConsistentHash;
5777
6040
  exports.CountMinSketch = CountMinSketch;
6041
+ exports.CrudControllerFactory = CrudControllerFactory;
6042
+ exports.DEFAULT_LIMIT = DEFAULT_LIMIT;
6043
+ exports.DEFAULT_PAGE = DEFAULT_PAGE;
6044
+ exports.DEFAULT_SORT_DIRECTION = DEFAULT_SORT_DIRECTION;
6045
+ exports.DatabaseManager = DatabaseManager;
5778
6046
  exports.Deque = Deque;
5779
6047
  exports.DirectedGraph = DirectedGraph;
5780
6048
  exports.DisjointSetUnion = DisjointSetUnion;
5781
6049
  exports.DoublyLinkedList = DoublyLinkedList;
5782
6050
  exports.DynamicArray = DynamicArray;
5783
6051
  exports.EmailService = EmailService;
6052
+ exports.ErrorMiddleware = ErrorMiddleware;
5784
6053
  exports.FenwickTree = FenwickTree;
5785
6054
  exports.FibNode = FibNode;
5786
6055
  exports.FibonacciHeap = FibonacciHeap;
5787
- exports.GlobalErrorHandler = GlobalErrorHandler;
5788
6056
  exports.Graph = Graph;
5789
6057
  exports.HashMap = HashMap;
5790
6058
  exports.HashSet = HashSet;
5791
6059
  exports.HyperLogLog = HyperLogLog;
5792
6060
  exports.IntervalTree = IntervalTree;
5793
6061
  exports.JWTService = JWTService;
6062
+ exports.JoiValidator = JoiValidator;
5794
6063
  exports.KDTree = KDTree;
5795
6064
  exports.LFUCache = LFUCache;
5796
6065
  exports.LRUCache = LRUCache;
@@ -5798,6 +6067,8 @@ exports.MaxHeap = MaxHeap;
5798
6067
  exports.MaxStack = MaxStack;
5799
6068
  exports.MinHeap = MinHeap;
5800
6069
  exports.MinStack = MinStack;
6070
+ exports.MongoDatabaseProvider = MongoDatabaseProvider;
6071
+ exports.MongoRepository = MongoRepository;
5801
6072
  exports.MulterFileHandlerService = MulterFileHandlerService;
5802
6073
  exports.MultiSet = MultiSet;
5803
6074
  exports.Node = Node;
@@ -5805,11 +6076,13 @@ exports.OrderedSet = OrderedSet;
5805
6076
  exports.PairingHeap = PairingHeap;
5806
6077
  exports.PairingNode = PairingNode;
5807
6078
  exports.PriorityQueue = PriorityQueue;
6079
+ exports.QUERY_RESERVED_FIELDS = QUERY_RESERVED_FIELDS;
5808
6080
  exports.QuadTree = QuadTree;
5809
6081
  exports.Queue = Queue;
5810
6082
  exports.RadixTree = RadixTree;
5811
6083
  exports.RedBlackTree = RedBlackTree;
5812
6084
  exports.SMTPProvider = SMTPProvider;
6085
+ exports.SUPPORTED_OPERATORS = SUPPORTED_OPERATORS;
5813
6086
  exports.SegmentTree = SegmentTree;
5814
6087
  exports.Set = Set2;
5815
6088
  exports.SinglyLinkedList = SinglyLinkedList;
@@ -5823,14 +6096,15 @@ exports.TemplateEngine = TemplateEngine;
5823
6096
  exports.TernarySearchTree = TernarySearchTree;
5824
6097
  exports.TreeNode = TreeNode;
5825
6098
  exports.Trie = Trie;
5826
- exports.asyncHandler = asyncHandler;
6099
+ exports.ZodValidator = ZodValidator;
5827
6100
  exports.authMiddleware = authMiddleware;
5828
- exports.connectMongoDB = connectMongoDB;
5829
6101
  exports.createAllowedOrigins = createAllowedOrigins;
5830
6102
  exports.createCorsOptions = createCorsOptions;
5831
6103
  exports.days = days;
5832
6104
  exports.errorToString = errorToString;
5833
6105
  exports.hours = hours;
6106
+ exports.isJoiSchema = isJoiSchema;
6107
+ exports.isZodSchema = isZodSchema;
5834
6108
  exports.loadEnv = loadEnv;
5835
6109
  exports.logger = logger;
5836
6110
  exports.milliseconds = milliseconds;