tsledge 0.1.25 → 0.1.27

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/src/index.js CHANGED
@@ -1,16 +1,66 @@
1
1
  // src/db/mongodb.ts
2
2
  import mongoose from "mongoose";
3
+ import { EXIT_CODE_GENERAL_ERROR, EXIT_CODE_INVALID_CONFIG, getCurrentDateString } from "tsledge-core";
4
+ async function connectMongoDB(uri) {
5
+ if (uri == null || uri == void 0 || uri.length == 0) {
6
+ console.error(`\u{1F6D1} [${getCurrentDateString()}] Error: MongoDB URI is not provided`);
7
+ process.exit(EXIT_CODE_INVALID_CONFIG);
8
+ }
9
+ try {
10
+ await mongoose.connect(uri);
11
+ console.log(`\u2705 [${getCurrentDateString()}] MongoDB connected`);
12
+ } catch (err) {
13
+ console.error(`\u{1F6D1} [${getCurrentDateString()}] Error: MongoDB connection failed`, err);
14
+ process.exit(EXIT_CODE_GENERAL_ERROR);
15
+ }
16
+ }
3
17
 
4
- // src/exitCodes.ts
5
- var EXIT_CODE_SUCCESS = 0;
6
- var EXIT_CODE_GENERAL_ERROR = 1;
7
- var EXIT_CODE_INVALID_CONFIG = 2;
18
+ // src/middleware/file-storage.ts
19
+ import multer from "multer";
20
+ import path from "node:path";
21
+ import fs from "node:fs";
22
+ var uploadDir = path.resolve(process.cwd(), "src", "files");
23
+ fs.mkdirSync(uploadDir, { recursive: true });
24
+ var sanitizeFilename = (name) => name.replace(/[^a-zA-Z0-9._-]/g, "_");
25
+ var diskStorage = multer.diskStorage({
26
+ destination: (_req, _file, next) => next(null, uploadDir),
27
+ filename: (_req, file, next) => {
28
+ const ext = path.extname(file.originalname);
29
+ const base = path.basename(file.originalname, ext);
30
+ const safe = sanitizeFilename(base);
31
+ const unique = Date.now() + "-" + Math.round(Math.random() * 1e9);
32
+ next(null, `${safe}-${unique}${ext}`);
33
+ }
34
+ });
35
+ var memoryStorage = multer.memoryStorage();
36
+ var diskFileUpload = multer({ storage: diskStorage });
37
+ var memoryFileUpload = multer({ storage: memoryStorage });
8
38
 
9
- // src/utils/date.ts
10
- function getCurrentDateString() {
11
- let date = /* @__PURE__ */ new Date();
12
- return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
39
+ // src/middleware/logger.ts
40
+ import { getCurrentDateString as getCurrentDateString2 } from "tsledge-core";
41
+ function requestLogger(req, res, next) {
42
+ res.on("finish", () => {
43
+ let emoji = "";
44
+ if (res.statusCode >= 100 && res.statusCode < 200) emoji = "\u{1F4A1}";
45
+ else if (res.statusCode >= 200 && res.statusCode < 300) emoji = "\u2705";
46
+ else if (res.statusCode >= 300 && res.statusCode < 400) emoji = "\u{1F6A6}";
47
+ else if (res.statusCode == 401) emoji = "\u{1F501}";
48
+ else if (res.statusCode >= 400 && res.statusCode < 500) emoji = "\u26A0\uFE0F";
49
+ else if (res.statusCode >= 500) emoji = "\u{1F525}";
50
+ console.log(
51
+ `${emoji} [${getCurrentDateString2()}] ${req.method} ${req.originalUrl} - ${res.statusCode}`
52
+ );
53
+ });
54
+ next();
13
55
  }
56
+ function errorLogger(err, req, res, next) {
57
+ console.error(`\u{1F6D1} [${getCurrentDateString2()}] Error in ${req.method} ${req.originalUrl}:`, err);
58
+ res.status(500).json();
59
+ }
60
+
61
+ // src/middleware/authentication/session.ts
62
+ import express from "express";
63
+ import bcrypt from "bcrypt";
14
64
 
15
65
  // src/utils/mongo-relation.ts
16
66
  function insertCollectionRelations(model, relations, compareFunc, validateFunc = void 0) {
@@ -74,17 +124,6 @@ async function mergeCollectionRelations(model, relationsToMerge, match, compareF
74
124
  }
75
125
  }
76
126
 
77
- // src/utils/validation.ts
78
- function isNonEmptyObjectOrArray(value) {
79
- return value != null && value != void 0 && (Array.isArray(value) && value.length > 0 || typeof value === "object" && !Array.isArray(value) && Object.keys(value).length > 0);
80
- }
81
- function validateString(value, fallback = null) {
82
- if (value !== void 0 && value !== null && value !== "" && value !== "undefined" && value !== "null") {
83
- return value;
84
- }
85
- return fallback;
86
- }
87
-
88
127
  // src/utils/env.ts
89
128
  var JwtSecret = process.env.JWT_SECRET || "secret";
90
129
  var JwtRefreshSecret = process.env.JWT_REFRESH_SECRET || "refresh_secret";
@@ -101,78 +140,7 @@ function isDebug(func = void 0) {
101
140
  return false;
102
141
  }
103
142
 
104
- // src/utils/encoding.ts
105
- function encodeToBase64(obj) {
106
- return btoa(encodeURIComponent(JSON.stringify(obj)));
107
- }
108
- function decodeFromBase64(str) {
109
- if (str == null || str == void 0) {
110
- return void 0;
111
- }
112
- let decoded = str ? decodeURIComponent(atob(str)) : void 0;
113
- return decoded ? JSON.parse(decoded) : void 0;
114
- }
115
-
116
- // src/db/mongodb.ts
117
- async function connectMongoDB(uri) {
118
- if (uri == null || uri == void 0 || uri.length == 0) {
119
- console.error(`\u{1F6D1} [${getCurrentDateString()}] Error: MongoDB URI is not provided`);
120
- process.exit(EXIT_CODE_INVALID_CONFIG);
121
- }
122
- try {
123
- await mongoose.connect(uri);
124
- console.log(`\u2705 [${getCurrentDateString()}] MongoDB connected`);
125
- } catch (err) {
126
- console.error(`\u{1F6D1} [${getCurrentDateString()}] Error: MongoDB connection failed`, err);
127
- process.exit(EXIT_CODE_GENERAL_ERROR);
128
- }
129
- }
130
-
131
- // src/middleware/file-storage.ts
132
- import multer from "multer";
133
- import path from "node:path";
134
- import fs from "node:fs";
135
- var uploadDir = path.resolve(process.cwd(), "src", "files");
136
- fs.mkdirSync(uploadDir, { recursive: true });
137
- var sanitizeFilename = (name) => name.replace(/[^a-zA-Z0-9._-]/g, "_");
138
- var diskStorage = multer.diskStorage({
139
- destination: (_req, _file, next) => next(null, uploadDir),
140
- filename: (_req, file, next) => {
141
- const ext = path.extname(file.originalname);
142
- const base = path.basename(file.originalname, ext);
143
- const safe = sanitizeFilename(base);
144
- const unique = Date.now() + "-" + Math.round(Math.random() * 1e9);
145
- next(null, `${safe}-${unique}${ext}`);
146
- }
147
- });
148
- var memoryStorage = multer.memoryStorage();
149
- var diskFileUpload = multer({ storage: diskStorage });
150
- var memoryFileUpload = multer({ storage: memoryStorage });
151
-
152
- // src/middleware/logger.ts
153
- function requestLogger(req, res, next) {
154
- res.on("finish", () => {
155
- let emoji = "";
156
- if (res.statusCode >= 100 && res.statusCode < 200) emoji = "\u{1F4A1}";
157
- else if (res.statusCode >= 200 && res.statusCode < 300) emoji = "\u2705";
158
- else if (res.statusCode >= 300 && res.statusCode < 400) emoji = "\u{1F6A6}";
159
- else if (res.statusCode == 401) emoji = "\u{1F501}";
160
- else if (res.statusCode >= 400 && res.statusCode < 500) emoji = "\u26A0\uFE0F";
161
- else if (res.statusCode >= 500) emoji = "\u{1F525}";
162
- console.log(
163
- `${emoji} [${getCurrentDateString()}] ${req.method} ${req.originalUrl} - ${res.statusCode}`
164
- );
165
- });
166
- next();
167
- }
168
- function errorLogger(err, req, res, next) {
169
- console.error(`\u{1F6D1} [${getCurrentDateString()}] Error in ${req.method} ${req.originalUrl}:`, err);
170
- res.status(500).json();
171
- }
172
-
173
143
  // src/middleware/authentication/session.ts
174
- import express from "express";
175
- import bcrypt from "bcrypt";
176
144
  import jwt2 from "jsonwebtoken";
177
145
 
178
146
  // src/middleware/authentication/validation.ts
@@ -311,6 +279,7 @@ async function socketToken(_socket, _next) {
311
279
  }
312
280
 
313
281
  // src/middleware/authentication/session.ts
282
+ import { encodeToBase64, validateString } from "tsledge-core";
314
283
  var router = express.Router();
315
284
  var FORBIDDEN2 = 403;
316
285
  var BAD_REQUEST = 400;
@@ -486,7 +455,7 @@ async function authRefresh(req, res, next) {
486
455
  }
487
456
 
488
457
  // src/fluent-interface/fluent-pattern-handler.ts
489
- import mongoose5 from "mongoose";
458
+ import mongoose4 from "mongoose";
490
459
 
491
460
  // src/fluent-interface/types.ts
492
461
  var fluentRequestQueryAttributes = {
@@ -499,34 +468,180 @@ var fluentRequestQueryAttributes = {
499
468
  excluded: ""
500
469
  };
501
470
 
502
- // src/core/query-builder.ts
503
- import mongoose4 from "mongoose";
504
-
505
- // src/core/types.ts
506
- var Codec = class {
507
- constructor(content, code = 202) {
508
- this.content = content;
509
- this.returnCode = code;
471
+ // src/fluent-interface/fluent-pattern-handler.ts
472
+ import { Codec } from "tsledge-core";
473
+ var FluentPatternHandler = class _FluentPatternHandler {
474
+ /**
475
+ * Constructor for FluentPatternHandler.
476
+ * @param execMiddleware - Optional array of middleware functions to be executed before the main query execution in the exec method.
477
+ */
478
+ constructor(execMiddleware = []) {
479
+ /**
480
+ * Array of middleware functions to be executed before the main query execution in the exec method. Each function receives the execution parameters and can modify them as needed.
481
+ */
482
+ this._execMiddlewareFunctions = [];
483
+ if (_FluentPatternHandler._singleton) {
484
+ throw new Error(
485
+ "FluentPatternHandler is a singleton class. Use FluentPatternHandler.getInstance() to access the instance."
486
+ );
487
+ }
488
+ this._execMiddlewareFunctions = execMiddleware;
489
+ _FluentPatternHandler._singleton = this;
510
490
  }
511
- sendToClient(res) {
512
- if (this.content == null) {
513
- res.status(404).json({});
491
+ /**
492
+ * Initializes the singleton instance of FluentPatternHandler with the provided options.
493
+ * @param execMiddleware - Optional array of middleware functions to be executed before the main query execution in the exec method.
494
+ * @returns Singleton instance of FluentPatternHandler.
495
+ */
496
+ static init(execMiddleware = []) {
497
+ if (_FluentPatternHandler._singleton != void 0) {
498
+ throw new Error("FluentPatternHandler is already initialized");
514
499
  }
515
- res.status(this.returnCode).json(this.content);
500
+ _FluentPatternHandler._singleton = new _FluentPatternHandler(execMiddleware);
501
+ return _FluentPatternHandler._singleton;
516
502
  }
517
- is1xx() {
518
- return this.returnCode >= 100 && this.returnCode <= 199;
503
+ /**
504
+ * Returns the singleton instance of FluentPatternHandler.
505
+ * @returns Singleton instance of FluentPatternHandler.
506
+ */
507
+ static getInstance() {
508
+ if (_FluentPatternHandler._singleton == void 0) {
509
+ throw new Error(
510
+ "FluentPatternHandler instance has not been created yet. Please create an instance before calling getInstance()."
511
+ );
512
+ }
513
+ return _FluentPatternHandler._singleton;
519
514
  }
520
- is2xx() {
521
- return this.returnCode >= 200 && this.returnCode <= 299;
515
+ /**
516
+ * Parses and validates query parameters from the request.
517
+ * @param query - The query object from the request.
518
+ * @returns Parsed query parameters.
519
+ */
520
+ _parseFluentRequestQuery(query) {
521
+ const { filter, limit = "5", offset = "0", id, excluded: excludedJSON, ids: idsJSON } = query;
522
+ const queryKeys = Object.keys(fluentRequestQueryAttributes);
523
+ let filterFields = {};
524
+ for (const [key, value] of Object.entries(query)) {
525
+ if (queryKeys.includes(key)) continue;
526
+ if (value == void 0) continue;
527
+ try {
528
+ filterFields[key] = typeof value === "string" ? value : JSON.stringify(value);
529
+ } catch (e) {
530
+ filterFields[key] = String(value);
531
+ }
532
+ }
533
+ let excluded;
534
+ if (excludedJSON) {
535
+ try {
536
+ excluded = JSON.parse(excludedJSON);
537
+ if (!Array.isArray(excluded)) throw new Error("Excluded must be an array");
538
+ } catch (error) {
539
+ console.warn("[FluentPatternHandler] Invalid excluded parameter:", error);
540
+ }
541
+ }
542
+ let ids;
543
+ if (idsJSON) {
544
+ try {
545
+ ids = JSON.parse(idsJSON);
546
+ if (!Array.isArray(ids)) throw new Error("Ids must be an array");
547
+ } catch (error) {
548
+ console.warn("[FluentPatternHandler] Invalid ids parameter:", error);
549
+ }
550
+ }
551
+ return { filter, limit, offset, id, excluded, ids, filterFields };
522
552
  }
523
- is3xx() {
524
- return this.returnCode >= 300 && this.returnCode <= 399;
553
+ /**
554
+ * Applies filters to the query builder based on parsed parameters and options.
555
+ * @param queryBuilder - The QueryBuilder instance.
556
+ * @param params - Parsed query parameters.
557
+ * @param config - Query request configuration.
558
+ */
559
+ _applyParameters(queryBuilder, params) {
560
+ const { id, ids, filter, excluded } = params;
561
+ if (id) {
562
+ queryBuilder.match({ _id: new mongoose4.Types.ObjectId(id) });
563
+ } else if (ids && ids.length > 0) {
564
+ const objectIds = ids.map((id2) => new mongoose4.Types.ObjectId(id2));
565
+ queryBuilder.match({ _id: { $in: objectIds } });
566
+ } else {
567
+ const modelFilterFields = this._getFilterFieldsForModel(queryBuilder.getConfig().model);
568
+ if (filter && modelFilterFields.length > 0) {
569
+ const ors = modelFilterFields.map((field) => ({
570
+ [field]: { $regex: filter, $options: "i" }
571
+ }));
572
+ queryBuilder.match({ $or: ors });
573
+ }
574
+ if (params.filterFields && Object.keys(params.filterFields).length > 0) {
575
+ for (const [field, value] of Object.entries(params.filterFields)) {
576
+ if (!modelFilterFields.includes(field)) {
577
+ continue;
578
+ }
579
+ const match = {};
580
+ match[field] = { $regex: value, $options: "i" };
581
+ queryBuilder.match(match);
582
+ }
583
+ }
584
+ if (excluded && excluded.length > 0) {
585
+ const objectIds = excluded.map((id2) => new mongoose4.Types.ObjectId(id2));
586
+ queryBuilder.match({ _id: { $nin: objectIds } });
587
+ }
588
+ }
525
589
  }
526
- is4xx() {
527
- return this.returnCode >= 400 && this.returnCode <= 499;
590
+ /**
591
+ * Generates filter fields for the given Mongoose model based on schema options.
592
+ * @param model - The Mongoose model.
593
+ * @returns Array of filter fields.
594
+ */
595
+ _getFilterFieldsForModel(model) {
596
+ let filterFields = [];
597
+ let schema = model.schema;
598
+ schema.eachPath((path2, type) => {
599
+ if (type?.options?.filter == true) {
600
+ filterFields.push(path2);
601
+ }
602
+ });
603
+ return filterFields;
604
+ }
605
+ /**
606
+ * Builds execution parameters for the query builder.
607
+ * @param params - Parsed query parameters.
608
+ * @returns Execution parameters.
609
+ */
610
+ _buildExecutionConfig(params) {
611
+ const { id, limit, offset } = params;
612
+ return {
613
+ isOne: Boolean(id),
614
+ limit: limit === "full" ? void 0 : parseInt(limit || "5", 10),
615
+ skip: parseInt(offset || "0", 10)
616
+ };
617
+ }
618
+ /**
619
+ * Executes the query builder with applied filters and returns the result.
620
+ * @param params Execution parameters including the query builder and request query.
621
+ * @returns
622
+ */
623
+ async exec(params) {
624
+ try {
625
+ if (this._execMiddlewareFunctions && this._execMiddlewareFunctions.length > 0) {
626
+ for (const func of this._execMiddlewareFunctions) {
627
+ await func(params);
628
+ }
629
+ }
630
+ const queryParams = this._parseFluentRequestQuery(params.req.query);
631
+ this._applyParameters(params.queryBuilder, queryParams);
632
+ const execConfig = this._buildExecutionConfig(queryParams);
633
+ return await params.queryBuilder.exec(execConfig);
634
+ } catch (err) {
635
+ console.error("[ERROR - FluentPatternHandler]", err);
636
+ return new Codec({ data: [], meta: { total: 0 } }, 500);
637
+ }
528
638
  }
529
639
  };
640
+
641
+ // src/query-builder/query-builder.ts
642
+ import mongoose5 from "mongoose";
643
+
644
+ // src/query-builder/types.ts
530
645
  var JoinRelation = class {
531
646
  constructor(localField, ref, alias = void 0) {
532
647
  this.ref = ref;
@@ -534,15 +649,9 @@ var JoinRelation = class {
534
649
  this.alias = alias ? alias : ref.collection.name;
535
650
  }
536
651
  };
537
- var HttpMethod = /* @__PURE__ */ ((HttpMethod2) => {
538
- HttpMethod2["POST"] = "post";
539
- HttpMethod2["GET"] = "get";
540
- HttpMethod2["DELETE"] = "delete";
541
- HttpMethod2["PUT"] = "put";
542
- return HttpMethod2;
543
- })(HttpMethod || {});
544
652
 
545
- // src/core/query-builder.ts
653
+ // src/query-builder/query-builder.ts
654
+ import { Codec as Codec2 } from "tsledge-core";
546
655
  var QueryBuilder = class {
547
656
  constructor(config) {
548
657
  this._matchConditions = {};
@@ -645,7 +754,7 @@ var QueryBuilder = class {
645
754
  if (schematype.options?.ref) {
646
755
  const refModelName = schematype.options.ref;
647
756
  try {
648
- const refModel = mongoose4.model(refModelName);
757
+ const refModel = mongoose5.model(refModelName);
649
758
  let alias = schematype.options?.alias ?? refModel.collection.name;
650
759
  this.join(new JoinRelation(fullPath, refModel, alias));
651
760
  } catch (err) {
@@ -654,10 +763,10 @@ var QueryBuilder = class {
654
763
  );
655
764
  }
656
765
  }
657
- if (schematype instanceof mongoose4.Schema.Types.Array && schematype.caster?.options?.ref) {
766
+ if (schematype instanceof mongoose5.Schema.Types.Array && schematype.caster?.options?.ref) {
658
767
  const refModelName = schematype.caster.options.ref;
659
768
  try {
660
- const refModel = mongoose4.model(refModelName);
769
+ const refModel = mongoose5.model(refModelName);
661
770
  let alias = schematype.caster.options?.alias ?? refModel.collection.name;
662
771
  this.join(new JoinRelation(fullPath, refModel, alias));
663
772
  } catch (err) {
@@ -739,10 +848,10 @@ var QueryBuilder = class {
739
848
  ]);
740
849
  const totalCount = countRes && countRes[0] ? countRes[0].n : 0;
741
850
  const documents = config && config.isOne ? await this._processSingleDocument(res) : await this._processMultipleDocuments(res);
742
- return new Codec({ data: documents, meta: { total: totalCount } }, 200);
851
+ return new Codec2({ data: documents, meta: { total: totalCount } }, 200);
743
852
  } catch (err) {
744
853
  console.error("[ERROR - QueryBuilder]", err);
745
- return new Codec({ data: [], meta: { total: 0 } }, 500);
854
+ return new Codec2({ data: [], meta: { total: 0 } }, 500);
746
855
  }
747
856
  }
748
857
  /**
@@ -783,236 +892,6 @@ var QueryBuilder = class {
783
892
  }
784
893
  };
785
894
 
786
- // src/core/http.ts
787
- function formDataParser(obj) {
788
- return new URLSearchParams(obj).toString();
789
- }
790
- async function httpRequest(config) {
791
- const url = new URL(config.url);
792
- if (config.urlSearchParams) {
793
- Object.keys(config.urlSearchParams).forEach((key) => {
794
- url.searchParams.set(key, String(config.urlSearchParams[key]));
795
- });
796
- }
797
- let body;
798
- let defaultContentType = "application/x-www-form-urlencoded";
799
- if (config.body) {
800
- if (typeof config.body === "object") {
801
- body = JSON.stringify(config.body);
802
- defaultContentType = "application/json";
803
- } else {
804
- body = String(config.body);
805
- }
806
- }
807
- const fetchImpl = globalThis.fetch;
808
- if (!fetchImpl) {
809
- throw new Error("fetch() is not available. Please use Node.js 18+ or add a fetch polyfill.");
810
- }
811
- const defaultHeaders = {};
812
- if (!config.headers || !Object.keys(config.headers).find((key) => key.toLowerCase() === "content-type")) {
813
- defaultHeaders["Content-Type"] = defaultContentType;
814
- }
815
- const response = await fetchImpl(url.toString(), {
816
- method: config.method?.toUpperCase() || "GET",
817
- body,
818
- headers: {
819
- ...defaultHeaders,
820
- ...config.headers
821
- }
822
- });
823
- const contentType = response.headers.get("content-type") || "";
824
- const isJson = contentType.includes("application/json");
825
- const isBlob = contentType.includes("application/octet-stream");
826
- if (!response.ok) {
827
- const text = await response.text().catch(() => "");
828
- throw new Error(`HTTP ${response.status} ${response.statusText}: ${text}`);
829
- }
830
- let result;
831
- if (isJson) {
832
- result = await response.json().catch(async (error) => {
833
- throw new Error(`Failed to parse JSON response: ${error.message}`);
834
- });
835
- } else if (isBlob) {
836
- result = await response.blob().catch(async (error) => {
837
- throw new Error(`Failed to get blob response: ${error.message}`);
838
- });
839
- } else {
840
- result = await response.text().catch(async (error) => {
841
- throw new Error(`Failed to get text response: ${error.message}`);
842
- });
843
- }
844
- return result;
845
- }
846
-
847
- // src/fluent-interface/fluent-pattern-handler.ts
848
- var FluentPatternHandler = class _FluentPatternHandler {
849
- /**
850
- * Constructor for FluentPatternHandler.
851
- * @param execMiddleware - Optional array of middleware functions to be executed before the main query execution in the exec method.
852
- */
853
- constructor(execMiddleware = []) {
854
- /**
855
- * Array of middleware functions to be executed before the main query execution in the exec method. Each function receives the execution parameters and can modify them as needed.
856
- */
857
- this._execMiddlewareFunctions = [];
858
- if (_FluentPatternHandler._singleton) {
859
- throw new Error(
860
- "FluentPatternHandler is a singleton class. Use FluentPatternHandler.getInstance() to access the instance."
861
- );
862
- }
863
- this._execMiddlewareFunctions = execMiddleware;
864
- _FluentPatternHandler._singleton = this;
865
- }
866
- /**
867
- * Initializes the singleton instance of FluentPatternHandler with the provided options.
868
- * @param execMiddleware - Optional array of middleware functions to be executed before the main query execution in the exec method.
869
- * @returns Singleton instance of FluentPatternHandler.
870
- */
871
- static init(execMiddleware = []) {
872
- if (_FluentPatternHandler._singleton != void 0) {
873
- throw new Error("FluentPatternHandler is already initialized");
874
- }
875
- _FluentPatternHandler._singleton = new _FluentPatternHandler(execMiddleware);
876
- return _FluentPatternHandler._singleton;
877
- }
878
- /**
879
- * Returns the singleton instance of FluentPatternHandler.
880
- * @returns Singleton instance of FluentPatternHandler.
881
- */
882
- static getInstance() {
883
- if (_FluentPatternHandler._singleton == void 0) {
884
- throw new Error(
885
- "FluentPatternHandler instance has not been created yet. Please create an instance before calling getInstance()."
886
- );
887
- }
888
- return _FluentPatternHandler._singleton;
889
- }
890
- /**
891
- * Parses and validates query parameters from the request.
892
- * @param query - The query object from the request.
893
- * @returns Parsed query parameters.
894
- */
895
- _parseFluentRequestQuery(query) {
896
- const { filter, limit = "5", offset = "0", id, excluded: excludedJSON, ids: idsJSON } = query;
897
- const queryKeys = Object.keys(fluentRequestQueryAttributes);
898
- let filterFields = {};
899
- for (const [key, value] of Object.entries(query)) {
900
- if (queryKeys.includes(key)) continue;
901
- if (value == void 0) continue;
902
- try {
903
- filterFields[key] = typeof value === "string" ? value : JSON.stringify(value);
904
- } catch (e) {
905
- filterFields[key] = String(value);
906
- }
907
- }
908
- let excluded;
909
- if (excludedJSON) {
910
- try {
911
- excluded = JSON.parse(excludedJSON);
912
- if (!Array.isArray(excluded)) throw new Error("Excluded must be an array");
913
- } catch (error) {
914
- console.warn("[FluentPatternHandler] Invalid excluded parameter:", error);
915
- }
916
- }
917
- let ids;
918
- if (idsJSON) {
919
- try {
920
- ids = JSON.parse(idsJSON);
921
- if (!Array.isArray(ids)) throw new Error("Ids must be an array");
922
- } catch (error) {
923
- console.warn("[FluentPatternHandler] Invalid ids parameter:", error);
924
- }
925
- }
926
- return { filter, limit, offset, id, excluded, ids, filterFields };
927
- }
928
- /**
929
- * Applies filters to the query builder based on parsed parameters and options.
930
- * @param queryBuilder - The QueryBuilder instance.
931
- * @param params - Parsed query parameters.
932
- * @param config - Query request configuration.
933
- */
934
- _applyParameters(queryBuilder, params) {
935
- const { id, ids, filter, excluded } = params;
936
- if (id) {
937
- queryBuilder.match({ _id: new mongoose5.Types.ObjectId(id) });
938
- } else if (ids && ids.length > 0) {
939
- const objectIds = ids.map((id2) => new mongoose5.Types.ObjectId(id2));
940
- queryBuilder.match({ _id: { $in: objectIds } });
941
- } else {
942
- const modelFilterFields = this._getFilterFieldsForModel(queryBuilder.getConfig().model);
943
- if (filter && modelFilterFields.length > 0) {
944
- const ors = modelFilterFields.map((field) => ({
945
- [field]: { $regex: filter, $options: "i" }
946
- }));
947
- queryBuilder.match({ $or: ors });
948
- }
949
- if (params.filterFields && Object.keys(params.filterFields).length > 0) {
950
- for (const [field, value] of Object.entries(params.filterFields)) {
951
- if (!modelFilterFields.includes(field)) {
952
- continue;
953
- }
954
- const match = {};
955
- match[field] = { $regex: value, $options: "i" };
956
- queryBuilder.match(match);
957
- }
958
- }
959
- if (excluded && excluded.length > 0) {
960
- const objectIds = excluded.map((id2) => new mongoose5.Types.ObjectId(id2));
961
- queryBuilder.match({ _id: { $nin: objectIds } });
962
- }
963
- }
964
- }
965
- /**
966
- * Generates filter fields for the given Mongoose model based on schema options.
967
- * @param model - The Mongoose model.
968
- * @returns Array of filter fields.
969
- */
970
- _getFilterFieldsForModel(model) {
971
- let filterFields = [];
972
- let schema = model.schema;
973
- schema.eachPath((path2, type) => {
974
- if (type?.options?.filter == true) {
975
- filterFields.push(path2);
976
- }
977
- });
978
- return filterFields;
979
- }
980
- /**
981
- * Builds execution parameters for the query builder.
982
- * @param params - Parsed query parameters.
983
- * @returns Execution parameters.
984
- */
985
- _buildExecutionConfig(params) {
986
- const { id, limit, offset } = params;
987
- return {
988
- isOne: Boolean(id),
989
- limit: limit === "full" ? void 0 : parseInt(limit || "5", 10),
990
- skip: parseInt(offset || "0", 10)
991
- };
992
- }
993
- /**
994
- * Executes the query builder with applied filters and returns the result.
995
- * @param params Execution parameters including the query builder and request query.
996
- * @returns
997
- */
998
- async exec(params) {
999
- try {
1000
- if (this._execMiddlewareFunctions && this._execMiddlewareFunctions.length > 0) {
1001
- for (const func of this._execMiddlewareFunctions) {
1002
- await func(params);
1003
- }
1004
- }
1005
- const queryParams = this._parseFluentRequestQuery(params.req.query);
1006
- this._applyParameters(params.queryBuilder, queryParams);
1007
- const execConfig = this._buildExecutionConfig(queryParams);
1008
- return await params.queryBuilder.exec(execConfig);
1009
- } catch (err) {
1010
- console.error("[ERROR - FluentPatternHandler]", err);
1011
- return new Codec({ data: [], meta: { total: 0 } }, 500);
1012
- }
1013
- }
1014
- };
1015
-
1016
895
  // src/app.ts
1017
896
  import express2 from "express";
1018
897
  import cors from "cors";
@@ -1028,12 +907,7 @@ function createApp() {
1028
907
  export {
1029
908
  AuthTokenBlocklistModel,
1030
909
  AuthUserModel,
1031
- Codec,
1032
- EXIT_CODE_GENERAL_ERROR,
1033
- EXIT_CODE_INVALID_CONFIG,
1034
- EXIT_CODE_SUCCESS,
1035
910
  FluentPatternHandler,
1036
- HttpMethod,
1037
911
  JoinRelation,
1038
912
  JwtRefreshSecret,
1039
913
  JwtSecret,
@@ -1044,24 +918,17 @@ export {
1044
918
  authRegister,
1045
919
  connectMongoDB,
1046
920
  createApp,
1047
- decodeFromBase64,
1048
921
  diskFileUpload,
1049
- encodeToBase64,
1050
922
  errorLogger,
1051
923
  fluentRequestQueryAttributes,
1052
- formDataParser,
1053
- getCurrentDateString,
1054
- httpRequest,
1055
924
  insertCollectionRelations,
1056
925
  isDebug,
1057
- isNonEmptyObjectOrArray,
1058
926
  jwtRefreshRequired,
1059
927
  jwtRequired,
1060
928
  memoryFileUpload,
1061
929
  mergeCollectionRelations,
1062
930
  requestLogger,
1063
931
  socketToken,
1064
- validateString,
1065
932
  verifyToken
1066
933
  };
1067
934
  //# sourceMappingURL=index.js.map