tsledge 0.1.30 → 0.1.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +238 -366
- package/dist/index.js.map +4 -4
- package/dist/tests/main.js +419 -283
- package/dist/tests/main.js.map +7 -0
- package/package.json +2 -2
- package/dist/core/http.d.ts +0 -15
- package/dist/core/http.d.ts.map +0 -1
- package/dist/core/index.d.ts +0 -4
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/query-builder.d.ts +0 -96
- package/dist/core/query-builder.d.ts.map +0 -1
- package/dist/core/types.d.ts +0 -58
- package/dist/core/types.d.ts.map +0 -1
- package/dist/exitCodes.d.ts +0 -4
- package/dist/exitCodes.d.ts.map +0 -1
- package/dist/middleware/authentication/types.d.ts +0 -34
- package/dist/middleware/authentication/types.d.ts.map +0 -1
- package/dist/scripts/repl.js +0 -1089
- package/dist/scripts/repl.js.map +0 -7
- package/dist/utils/date.d.ts +0 -2
- package/dist/utils/date.d.ts.map +0 -1
- package/dist/utils/encoding.d.ts +0 -13
- package/dist/utils/encoding.d.ts.map +0 -1
- package/dist/utils/validation.d.ts +0 -16
- package/dist/utils/validation.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,16 +1,71 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
export * from "tsledge-core";
|
|
3
|
+
|
|
1
4
|
// src/db/mongodb.ts
|
|
2
5
|
import mongoose from "mongoose";
|
|
6
|
+
import { EXIT_CODE_GENERAL_ERROR, EXIT_CODE_INVALID_CONFIG, getCurrentDateString } from "tsledge-core";
|
|
7
|
+
async function connectMongoDB(uri) {
|
|
8
|
+
if (uri == null || uri == void 0 || uri.length == 0) {
|
|
9
|
+
console.error(`\u{1F6D1} [${getCurrentDateString()}] Error: MongoDB URI is not provided`);
|
|
10
|
+
process.exit(EXIT_CODE_INVALID_CONFIG);
|
|
11
|
+
}
|
|
12
|
+
try {
|
|
13
|
+
await mongoose.connect(uri);
|
|
14
|
+
console.log(`\u2705 [${getCurrentDateString()}] MongoDB connected`);
|
|
15
|
+
} catch (err) {
|
|
16
|
+
console.error(`\u{1F6D1} [${getCurrentDateString()}] Error: MongoDB connection failed`, err);
|
|
17
|
+
process.exit(EXIT_CODE_GENERAL_ERROR);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
3
20
|
|
|
4
|
-
// src/
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
21
|
+
// src/middleware/file-storage.ts
|
|
22
|
+
import multer from "multer";
|
|
23
|
+
import path from "node:path";
|
|
24
|
+
import fs from "node:fs";
|
|
25
|
+
var sanitizeFilename = (name) => name.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
26
|
+
var diskStorage = (directory = "files") => {
|
|
27
|
+
let uploadDir = path.resolve(process.cwd(), "src", directory);
|
|
28
|
+
fs.mkdirSync(uploadDir, { recursive: true });
|
|
29
|
+
return multer.diskStorage({
|
|
30
|
+
destination: (_req, _file, next) => next(null, uploadDir),
|
|
31
|
+
filename: (_req, file, next) => {
|
|
32
|
+
const ext = path.extname(file.originalname);
|
|
33
|
+
const base = path.basename(file.originalname, ext);
|
|
34
|
+
const safe = sanitizeFilename(base);
|
|
35
|
+
const unique = Date.now() + "-" + Math.round(Math.random() * 1e9);
|
|
36
|
+
next(null, `${safe}-${unique}${ext}`);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
var memoryStorage = multer.memoryStorage();
|
|
41
|
+
var diskFileUpload = (directory = "files") => multer({ storage: diskStorage(directory) });
|
|
42
|
+
var memoryFileUpload = multer({ storage: memoryStorage });
|
|
8
43
|
|
|
9
|
-
// src/
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
44
|
+
// src/middleware/logger.ts
|
|
45
|
+
import { getCurrentDateString as getCurrentDateString2 } from "tsledge-core";
|
|
46
|
+
function requestLogger(req, res, next) {
|
|
47
|
+
res.on("finish", () => {
|
|
48
|
+
let emoji = "";
|
|
49
|
+
if (res.statusCode >= 100 && res.statusCode < 200) emoji = "\u{1F4A1}";
|
|
50
|
+
else if (res.statusCode >= 200 && res.statusCode < 300) emoji = "\u2705";
|
|
51
|
+
else if (res.statusCode >= 300 && res.statusCode < 400) emoji = "\u{1F6A6}";
|
|
52
|
+
else if (res.statusCode == 401) emoji = "\u{1F501}";
|
|
53
|
+
else if (res.statusCode >= 400 && res.statusCode < 500) emoji = "\u26A0\uFE0F";
|
|
54
|
+
else if (res.statusCode >= 500) emoji = "\u{1F525}";
|
|
55
|
+
console.log(
|
|
56
|
+
`${emoji} [${getCurrentDateString2()}] ${req.method} ${req.originalUrl} - ${res.statusCode}`
|
|
57
|
+
);
|
|
58
|
+
});
|
|
59
|
+
next();
|
|
13
60
|
}
|
|
61
|
+
function errorLogger(err, req, res, next) {
|
|
62
|
+
console.error(`\u{1F6D1} [${getCurrentDateString2()}] Error in ${req.method} ${req.originalUrl}:`, err);
|
|
63
|
+
res.status(500).json();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// src/middleware/authentication/session.ts
|
|
67
|
+
import express from "express";
|
|
68
|
+
import bcrypt from "bcrypt";
|
|
14
69
|
|
|
15
70
|
// src/utils/mongo-relation.ts
|
|
16
71
|
function insertCollectionRelations(model, relations, compareFunc, validateFunc = void 0) {
|
|
@@ -74,17 +129,6 @@ async function mergeCollectionRelations(model, relationsToMerge, match, compareF
|
|
|
74
129
|
}
|
|
75
130
|
}
|
|
76
131
|
|
|
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
132
|
// src/utils/env.ts
|
|
89
133
|
var JwtSecret = process.env.JWT_SECRET || "secret";
|
|
90
134
|
var JwtRefreshSecret = process.env.JWT_REFRESH_SECRET || "refresh_secret";
|
|
@@ -101,78 +145,7 @@ function isDebug(func = void 0) {
|
|
|
101
145
|
return false;
|
|
102
146
|
}
|
|
103
147
|
|
|
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
148
|
// src/middleware/authentication/session.ts
|
|
174
|
-
import express from "express";
|
|
175
|
-
import bcrypt from "bcrypt";
|
|
176
149
|
import jwt2 from "jsonwebtoken";
|
|
177
150
|
|
|
178
151
|
// src/middleware/authentication/validation.ts
|
|
@@ -311,6 +284,7 @@ async function socketToken(_socket, _next) {
|
|
|
311
284
|
}
|
|
312
285
|
|
|
313
286
|
// src/middleware/authentication/session.ts
|
|
287
|
+
import { encodeToBase64, validateString } from "tsledge-core";
|
|
314
288
|
var router = express.Router();
|
|
315
289
|
var FORBIDDEN2 = 403;
|
|
316
290
|
var BAD_REQUEST = 400;
|
|
@@ -486,7 +460,7 @@ async function authRefresh(req, res, next) {
|
|
|
486
460
|
}
|
|
487
461
|
|
|
488
462
|
// src/fluent-interface/fluent-pattern-handler.ts
|
|
489
|
-
import
|
|
463
|
+
import mongoose4 from "mongoose";
|
|
490
464
|
|
|
491
465
|
// src/fluent-interface/types.ts
|
|
492
466
|
var fluentRequestQueryAttributes = {
|
|
@@ -499,34 +473,180 @@ var fluentRequestQueryAttributes = {
|
|
|
499
473
|
excluded: ""
|
|
500
474
|
};
|
|
501
475
|
|
|
502
|
-
// src/
|
|
503
|
-
import
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
476
|
+
// src/fluent-interface/fluent-pattern-handler.ts
|
|
477
|
+
import { Codec } from "tsledge-core";
|
|
478
|
+
var FluentPatternHandler = class _FluentPatternHandler {
|
|
479
|
+
/**
|
|
480
|
+
* Constructor for FluentPatternHandler.
|
|
481
|
+
* @param execMiddleware - Optional array of middleware functions to be executed before the main query execution in the exec method.
|
|
482
|
+
*/
|
|
483
|
+
constructor(execMiddleware = []) {
|
|
484
|
+
/**
|
|
485
|
+
* 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.
|
|
486
|
+
*/
|
|
487
|
+
this._execMiddlewareFunctions = [];
|
|
488
|
+
if (_FluentPatternHandler._singleton) {
|
|
489
|
+
throw new Error(
|
|
490
|
+
"FluentPatternHandler is a singleton class. Use FluentPatternHandler.getInstance() to access the instance."
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
this._execMiddlewareFunctions = execMiddleware;
|
|
494
|
+
_FluentPatternHandler._singleton = this;
|
|
510
495
|
}
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
496
|
+
/**
|
|
497
|
+
* Initializes the singleton instance of FluentPatternHandler with the provided options.
|
|
498
|
+
* @param execMiddleware - Optional array of middleware functions to be executed before the main query execution in the exec method.
|
|
499
|
+
* @returns Singleton instance of FluentPatternHandler.
|
|
500
|
+
*/
|
|
501
|
+
static init(execMiddleware = []) {
|
|
502
|
+
if (_FluentPatternHandler._singleton != void 0) {
|
|
503
|
+
throw new Error("FluentPatternHandler is already initialized");
|
|
514
504
|
}
|
|
515
|
-
|
|
505
|
+
_FluentPatternHandler._singleton = new _FluentPatternHandler(execMiddleware);
|
|
506
|
+
return _FluentPatternHandler._singleton;
|
|
516
507
|
}
|
|
517
|
-
|
|
518
|
-
|
|
508
|
+
/**
|
|
509
|
+
* Returns the singleton instance of FluentPatternHandler.
|
|
510
|
+
* @returns Singleton instance of FluentPatternHandler.
|
|
511
|
+
*/
|
|
512
|
+
static getInstance() {
|
|
513
|
+
if (_FluentPatternHandler._singleton == void 0) {
|
|
514
|
+
throw new Error(
|
|
515
|
+
"FluentPatternHandler instance has not been created yet. Please create an instance before calling getInstance()."
|
|
516
|
+
);
|
|
517
|
+
}
|
|
518
|
+
return _FluentPatternHandler._singleton;
|
|
519
519
|
}
|
|
520
|
-
|
|
521
|
-
|
|
520
|
+
/**
|
|
521
|
+
* Parses and validates query parameters from the request.
|
|
522
|
+
* @param query - The query object from the request.
|
|
523
|
+
* @returns Parsed query parameters.
|
|
524
|
+
*/
|
|
525
|
+
_parseFluentRequestQuery(query) {
|
|
526
|
+
const { filter, limit = "5", offset = "0", id, excluded: excludedJSON, ids: idsJSON } = query;
|
|
527
|
+
const queryKeys = Object.keys(fluentRequestQueryAttributes);
|
|
528
|
+
let filterFields = {};
|
|
529
|
+
for (const [key, value] of Object.entries(query)) {
|
|
530
|
+
if (queryKeys.includes(key)) continue;
|
|
531
|
+
if (value == void 0) continue;
|
|
532
|
+
try {
|
|
533
|
+
filterFields[key] = typeof value === "string" ? value : JSON.stringify(value);
|
|
534
|
+
} catch (e) {
|
|
535
|
+
filterFields[key] = String(value);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
let excluded;
|
|
539
|
+
if (excludedJSON) {
|
|
540
|
+
try {
|
|
541
|
+
excluded = JSON.parse(excludedJSON);
|
|
542
|
+
if (!Array.isArray(excluded)) throw new Error("Excluded must be an array");
|
|
543
|
+
} catch (error) {
|
|
544
|
+
console.warn("[FluentPatternHandler] Invalid excluded parameter:", error);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
let ids;
|
|
548
|
+
if (idsJSON) {
|
|
549
|
+
try {
|
|
550
|
+
ids = JSON.parse(idsJSON);
|
|
551
|
+
if (!Array.isArray(ids)) throw new Error("Ids must be an array");
|
|
552
|
+
} catch (error) {
|
|
553
|
+
console.warn("[FluentPatternHandler] Invalid ids parameter:", error);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
return { filter, limit, offset, id, excluded, ids, filterFields };
|
|
522
557
|
}
|
|
523
|
-
|
|
524
|
-
|
|
558
|
+
/**
|
|
559
|
+
* Applies filters to the query builder based on parsed parameters and options.
|
|
560
|
+
* @param queryBuilder - The QueryBuilder instance.
|
|
561
|
+
* @param params - Parsed query parameters.
|
|
562
|
+
* @param config - Query request configuration.
|
|
563
|
+
*/
|
|
564
|
+
_applyParameters(queryBuilder, params) {
|
|
565
|
+
const { id, ids, filter, excluded } = params;
|
|
566
|
+
if (id) {
|
|
567
|
+
queryBuilder.match({ _id: new mongoose4.Types.ObjectId(id) });
|
|
568
|
+
} else if (ids && ids.length > 0) {
|
|
569
|
+
const objectIds = ids.map((id2) => new mongoose4.Types.ObjectId(id2));
|
|
570
|
+
queryBuilder.match({ _id: { $in: objectIds } });
|
|
571
|
+
} else {
|
|
572
|
+
const modelFilterFields = this._getFilterFieldsForModel(queryBuilder.getConfig().model);
|
|
573
|
+
if (filter && modelFilterFields.length > 0) {
|
|
574
|
+
const ors = modelFilterFields.map((field) => ({
|
|
575
|
+
[field]: { $regex: filter, $options: "i" }
|
|
576
|
+
}));
|
|
577
|
+
queryBuilder.match({ $or: ors });
|
|
578
|
+
}
|
|
579
|
+
if (params.filterFields && Object.keys(params.filterFields).length > 0) {
|
|
580
|
+
for (const [field, value] of Object.entries(params.filterFields)) {
|
|
581
|
+
if (!modelFilterFields.includes(field)) {
|
|
582
|
+
continue;
|
|
583
|
+
}
|
|
584
|
+
const match = {};
|
|
585
|
+
match[field] = { $regex: value, $options: "i" };
|
|
586
|
+
queryBuilder.match(match);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
if (excluded && excluded.length > 0) {
|
|
590
|
+
const objectIds = excluded.map((id2) => new mongoose4.Types.ObjectId(id2));
|
|
591
|
+
queryBuilder.match({ _id: { $nin: objectIds } });
|
|
592
|
+
}
|
|
593
|
+
}
|
|
525
594
|
}
|
|
526
|
-
|
|
527
|
-
|
|
595
|
+
/**
|
|
596
|
+
* Generates filter fields for the given Mongoose model based on schema options.
|
|
597
|
+
* @param model - The Mongoose model.
|
|
598
|
+
* @returns Array of filter fields.
|
|
599
|
+
*/
|
|
600
|
+
_getFilterFieldsForModel(model) {
|
|
601
|
+
let filterFields = [];
|
|
602
|
+
let schema = model.schema;
|
|
603
|
+
schema.eachPath((path2, type) => {
|
|
604
|
+
if (type?.options?.filter == true) {
|
|
605
|
+
filterFields.push(path2);
|
|
606
|
+
}
|
|
607
|
+
});
|
|
608
|
+
return filterFields;
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Builds execution parameters for the query builder.
|
|
612
|
+
* @param params - Parsed query parameters.
|
|
613
|
+
* @returns Execution parameters.
|
|
614
|
+
*/
|
|
615
|
+
_buildExecutionConfig(params) {
|
|
616
|
+
const { id, limit, offset } = params;
|
|
617
|
+
return {
|
|
618
|
+
isOne: Boolean(id),
|
|
619
|
+
limit: limit === "full" ? void 0 : parseInt(limit || "5", 10),
|
|
620
|
+
skip: parseInt(offset || "0", 10)
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Executes the query builder with applied filters and returns the result.
|
|
625
|
+
* @param params Execution parameters including the query builder and request query.
|
|
626
|
+
* @returns
|
|
627
|
+
*/
|
|
628
|
+
async exec(params) {
|
|
629
|
+
try {
|
|
630
|
+
if (this._execMiddlewareFunctions && this._execMiddlewareFunctions.length > 0) {
|
|
631
|
+
for (const func of this._execMiddlewareFunctions) {
|
|
632
|
+
await func(params);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
const queryParams = this._parseFluentRequestQuery(params.req.query);
|
|
636
|
+
this._applyParameters(params.queryBuilder, queryParams);
|
|
637
|
+
const execConfig = this._buildExecutionConfig(queryParams);
|
|
638
|
+
return await params.queryBuilder.exec(execConfig);
|
|
639
|
+
} catch (err) {
|
|
640
|
+
console.error("[ERROR - FluentPatternHandler]", err);
|
|
641
|
+
return new Codec({ data: [], meta: { total: 0 } }, 500);
|
|
642
|
+
}
|
|
528
643
|
}
|
|
529
644
|
};
|
|
645
|
+
|
|
646
|
+
// src/query-builder/query-builder.ts
|
|
647
|
+
import mongoose5 from "mongoose";
|
|
648
|
+
|
|
649
|
+
// src/query-builder/types.ts
|
|
530
650
|
var JoinRelation = class {
|
|
531
651
|
constructor(localField, ref, alias = void 0) {
|
|
532
652
|
this.ref = ref;
|
|
@@ -534,15 +654,9 @@ var JoinRelation = class {
|
|
|
534
654
|
this.alias = alias ? alias : ref.collection.name;
|
|
535
655
|
}
|
|
536
656
|
};
|
|
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
657
|
|
|
545
|
-
// src/
|
|
658
|
+
// src/query-builder/query-builder.ts
|
|
659
|
+
import { Codec as Codec2 } from "tsledge-core";
|
|
546
660
|
var QueryBuilder = class {
|
|
547
661
|
constructor(config) {
|
|
548
662
|
this._matchConditions = {};
|
|
@@ -645,7 +759,7 @@ var QueryBuilder = class {
|
|
|
645
759
|
if (schematype.options?.ref) {
|
|
646
760
|
const refModelName = schematype.options.ref;
|
|
647
761
|
try {
|
|
648
|
-
const refModel =
|
|
762
|
+
const refModel = mongoose5.model(refModelName);
|
|
649
763
|
let alias = schematype.options?.alias ?? refModel.collection.name;
|
|
650
764
|
this.join(new JoinRelation(fullPath, refModel, alias));
|
|
651
765
|
} catch (err) {
|
|
@@ -654,10 +768,10 @@ var QueryBuilder = class {
|
|
|
654
768
|
);
|
|
655
769
|
}
|
|
656
770
|
}
|
|
657
|
-
if (schematype instanceof
|
|
771
|
+
if (schematype instanceof mongoose5.Schema.Types.Array && schematype.caster?.options?.ref) {
|
|
658
772
|
const refModelName = schematype.caster.options.ref;
|
|
659
773
|
try {
|
|
660
|
-
const refModel =
|
|
774
|
+
const refModel = mongoose5.model(refModelName);
|
|
661
775
|
let alias = schematype.caster.options?.alias ?? refModel.collection.name;
|
|
662
776
|
this.join(new JoinRelation(fullPath, refModel, alias));
|
|
663
777
|
} catch (err) {
|
|
@@ -739,10 +853,10 @@ var QueryBuilder = class {
|
|
|
739
853
|
]);
|
|
740
854
|
const totalCount = countRes && countRes[0] ? countRes[0].n : 0;
|
|
741
855
|
const documents = config && config.isOne ? await this._processSingleDocument(res) : await this._processMultipleDocuments(res);
|
|
742
|
-
return new
|
|
856
|
+
return new Codec2({ data: documents, meta: { total: totalCount } }, 200);
|
|
743
857
|
} catch (err) {
|
|
744
858
|
console.error("[ERROR - QueryBuilder]", err);
|
|
745
|
-
return new
|
|
859
|
+
return new Codec2({ data: [], meta: { total: 0 } }, 500);
|
|
746
860
|
}
|
|
747
861
|
}
|
|
748
862
|
/**
|
|
@@ -783,236 +897,6 @@ var QueryBuilder = class {
|
|
|
783
897
|
}
|
|
784
898
|
};
|
|
785
899
|
|
|
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
|
-
this._execMiddlewareFunctions.forEach((func) => {
|
|
1002
|
-
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
900
|
// src/app.ts
|
|
1017
901
|
import express2 from "express";
|
|
1018
902
|
import cors from "cors";
|
|
@@ -1028,12 +912,7 @@ function createApp() {
|
|
|
1028
912
|
export {
|
|
1029
913
|
AuthTokenBlocklistModel,
|
|
1030
914
|
AuthUserModel,
|
|
1031
|
-
Codec,
|
|
1032
|
-
EXIT_CODE_GENERAL_ERROR,
|
|
1033
|
-
EXIT_CODE_INVALID_CONFIG,
|
|
1034
|
-
EXIT_CODE_SUCCESS,
|
|
1035
915
|
FluentPatternHandler,
|
|
1036
|
-
HttpMethod,
|
|
1037
916
|
JoinRelation,
|
|
1038
917
|
JwtRefreshSecret,
|
|
1039
918
|
JwtSecret,
|
|
@@ -1044,24 +923,17 @@ export {
|
|
|
1044
923
|
authRegister,
|
|
1045
924
|
connectMongoDB,
|
|
1046
925
|
createApp,
|
|
1047
|
-
decodeFromBase64,
|
|
1048
926
|
diskFileUpload,
|
|
1049
|
-
encodeToBase64,
|
|
1050
927
|
errorLogger,
|
|
1051
928
|
fluentRequestQueryAttributes,
|
|
1052
|
-
formDataParser,
|
|
1053
|
-
getCurrentDateString,
|
|
1054
|
-
httpRequest,
|
|
1055
929
|
insertCollectionRelations,
|
|
1056
930
|
isDebug,
|
|
1057
|
-
isNonEmptyObjectOrArray,
|
|
1058
931
|
jwtRefreshRequired,
|
|
1059
932
|
jwtRequired,
|
|
1060
933
|
memoryFileUpload,
|
|
1061
934
|
mergeCollectionRelations,
|
|
1062
935
|
requestLogger,
|
|
1063
936
|
socketToken,
|
|
1064
|
-
validateString,
|
|
1065
937
|
verifyToken
|
|
1066
938
|
};
|
|
1067
939
|
//# sourceMappingURL=index.js.map
|