tsledge 0.1.25 → 0.1.26
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/bin/repl.js +233 -366
- package/dist/bin/repl.js.map +4 -4
- package/dist/db/mongodb.d.ts.map +1 -1
- package/dist/fluent-interface/fluent-pattern-handler.d.ts +1 -1
- package/dist/fluent-interface/fluent-pattern-handler.d.ts.map +1 -1
- package/dist/fluent-interface/types.d.ts +2 -1
- package/dist/fluent-interface/types.d.ts.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/middleware/authentication/session.d.ts.map +1 -1
- package/dist/query-builder/index.d.ts +3 -0
- package/dist/query-builder/index.d.ts.map +1 -0
- package/dist/query-builder/query-builder.d.ts +97 -0
- package/dist/query-builder/query-builder.d.ts.map +1 -0
- package/dist/query-builder/types.d.ts +14 -0
- package/dist/query-builder/types.d.ts.map +1 -0
- package/dist/src/index.js +233 -366
- package/dist/src/index.js.map +4 -4
- package/dist/utils/index.d.ts +0 -3
- package/dist/utils/index.d.ts.map +1 -1
- package/package.json +6 -5
package/dist/bin/repl.js
CHANGED
|
@@ -9,12 +9,7 @@ var src_exports = {};
|
|
|
9
9
|
__export(src_exports, {
|
|
10
10
|
AuthTokenBlocklistModel: () => AuthTokenBlocklistModel,
|
|
11
11
|
AuthUserModel: () => AuthUserModel,
|
|
12
|
-
Codec: () => Codec,
|
|
13
|
-
EXIT_CODE_GENERAL_ERROR: () => EXIT_CODE_GENERAL_ERROR,
|
|
14
|
-
EXIT_CODE_INVALID_CONFIG: () => EXIT_CODE_INVALID_CONFIG,
|
|
15
|
-
EXIT_CODE_SUCCESS: () => EXIT_CODE_SUCCESS,
|
|
16
12
|
FluentPatternHandler: () => FluentPatternHandler,
|
|
17
|
-
HttpMethod: () => HttpMethod,
|
|
18
13
|
JoinRelation: () => JoinRelation,
|
|
19
14
|
JwtRefreshSecret: () => JwtRefreshSecret,
|
|
20
15
|
JwtSecret: () => JwtSecret,
|
|
@@ -25,40 +20,83 @@ __export(src_exports, {
|
|
|
25
20
|
authRegister: () => authRegister,
|
|
26
21
|
connectMongoDB: () => connectMongoDB,
|
|
27
22
|
createApp: () => createApp,
|
|
28
|
-
decodeFromBase64: () => decodeFromBase64,
|
|
29
23
|
diskFileUpload: () => diskFileUpload,
|
|
30
|
-
encodeToBase64: () => encodeToBase64,
|
|
31
24
|
errorLogger: () => errorLogger,
|
|
32
25
|
fluentRequestQueryAttributes: () => fluentRequestQueryAttributes,
|
|
33
|
-
formDataParser: () => formDataParser,
|
|
34
|
-
getCurrentDateString: () => getCurrentDateString,
|
|
35
|
-
httpRequest: () => httpRequest,
|
|
36
26
|
insertCollectionRelations: () => insertCollectionRelations,
|
|
37
27
|
isDebug: () => isDebug,
|
|
38
|
-
isNonEmptyObjectOrArray: () => isNonEmptyObjectOrArray,
|
|
39
28
|
jwtRefreshRequired: () => jwtRefreshRequired,
|
|
40
29
|
jwtRequired: () => jwtRequired,
|
|
41
30
|
memoryFileUpload: () => memoryFileUpload,
|
|
42
31
|
mergeCollectionRelations: () => mergeCollectionRelations,
|
|
43
32
|
requestLogger: () => requestLogger,
|
|
44
33
|
socketToken: () => socketToken,
|
|
45
|
-
validateString: () => validateString,
|
|
46
34
|
verifyToken: () => verifyToken
|
|
47
35
|
});
|
|
48
36
|
|
|
49
37
|
// src/db/mongodb.ts
|
|
50
38
|
import mongoose from "mongoose";
|
|
39
|
+
import { EXIT_CODE_GENERAL_ERROR, EXIT_CODE_INVALID_CONFIG, getCurrentDateString } from "tsledge-core";
|
|
40
|
+
async function connectMongoDB(uri) {
|
|
41
|
+
if (uri == null || uri == void 0 || uri.length == 0) {
|
|
42
|
+
console.error(`\u{1F6D1} [${getCurrentDateString()}] Error: MongoDB URI is not provided`);
|
|
43
|
+
process.exit(EXIT_CODE_INVALID_CONFIG);
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
await mongoose.connect(uri);
|
|
47
|
+
console.log(`\u2705 [${getCurrentDateString()}] MongoDB connected`);
|
|
48
|
+
} catch (err) {
|
|
49
|
+
console.error(`\u{1F6D1} [${getCurrentDateString()}] Error: MongoDB connection failed`, err);
|
|
50
|
+
process.exit(EXIT_CODE_GENERAL_ERROR);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
51
53
|
|
|
52
|
-
// src/
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
// src/middleware/file-storage.ts
|
|
55
|
+
import multer from "multer";
|
|
56
|
+
import path from "node:path";
|
|
57
|
+
import fs from "node:fs";
|
|
58
|
+
var uploadDir = path.resolve(process.cwd(), "src", "files");
|
|
59
|
+
fs.mkdirSync(uploadDir, { recursive: true });
|
|
60
|
+
var sanitizeFilename = (name) => name.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
61
|
+
var diskStorage = multer.diskStorage({
|
|
62
|
+
destination: (_req, _file, next) => next(null, uploadDir),
|
|
63
|
+
filename: (_req, file, next) => {
|
|
64
|
+
const ext = path.extname(file.originalname);
|
|
65
|
+
const base = path.basename(file.originalname, ext);
|
|
66
|
+
const safe = sanitizeFilename(base);
|
|
67
|
+
const unique = Date.now() + "-" + Math.round(Math.random() * 1e9);
|
|
68
|
+
next(null, `${safe}-${unique}${ext}`);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
var memoryStorage = multer.memoryStorage();
|
|
72
|
+
var diskFileUpload = multer({ storage: diskStorage });
|
|
73
|
+
var memoryFileUpload = multer({ storage: memoryStorage });
|
|
56
74
|
|
|
57
|
-
// src/
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
75
|
+
// src/middleware/logger.ts
|
|
76
|
+
import { getCurrentDateString as getCurrentDateString2 } from "tsledge-core";
|
|
77
|
+
function requestLogger(req, res, next) {
|
|
78
|
+
res.on("finish", () => {
|
|
79
|
+
let emoji = "";
|
|
80
|
+
if (res.statusCode >= 100 && res.statusCode < 200) emoji = "\u{1F4A1}";
|
|
81
|
+
else if (res.statusCode >= 200 && res.statusCode < 300) emoji = "\u2705";
|
|
82
|
+
else if (res.statusCode >= 300 && res.statusCode < 400) emoji = "\u{1F6A6}";
|
|
83
|
+
else if (res.statusCode == 401) emoji = "\u{1F501}";
|
|
84
|
+
else if (res.statusCode >= 400 && res.statusCode < 500) emoji = "\u26A0\uFE0F";
|
|
85
|
+
else if (res.statusCode >= 500) emoji = "\u{1F525}";
|
|
86
|
+
console.log(
|
|
87
|
+
`${emoji} [${getCurrentDateString2()}] ${req.method} ${req.originalUrl} - ${res.statusCode}`
|
|
88
|
+
);
|
|
89
|
+
});
|
|
90
|
+
next();
|
|
61
91
|
}
|
|
92
|
+
function errorLogger(err, req, res, next) {
|
|
93
|
+
console.error(`\u{1F6D1} [${getCurrentDateString2()}] Error in ${req.method} ${req.originalUrl}:`, err);
|
|
94
|
+
res.status(500).json();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// src/middleware/authentication/session.ts
|
|
98
|
+
import express from "express";
|
|
99
|
+
import bcrypt from "bcrypt";
|
|
62
100
|
|
|
63
101
|
// src/utils/mongo-relation.ts
|
|
64
102
|
function insertCollectionRelations(model, relations, compareFunc, validateFunc = void 0) {
|
|
@@ -122,17 +160,6 @@ async function mergeCollectionRelations(model, relationsToMerge, match, compareF
|
|
|
122
160
|
}
|
|
123
161
|
}
|
|
124
162
|
|
|
125
|
-
// src/utils/validation.ts
|
|
126
|
-
function isNonEmptyObjectOrArray(value) {
|
|
127
|
-
return value != null && value != void 0 && (Array.isArray(value) && value.length > 0 || typeof value === "object" && !Array.isArray(value) && Object.keys(value).length > 0);
|
|
128
|
-
}
|
|
129
|
-
function validateString(value, fallback = null) {
|
|
130
|
-
if (value !== void 0 && value !== null && value !== "" && value !== "undefined" && value !== "null") {
|
|
131
|
-
return value;
|
|
132
|
-
}
|
|
133
|
-
return fallback;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
163
|
// src/utils/env.ts
|
|
137
164
|
var JwtSecret = process.env.JWT_SECRET || "secret";
|
|
138
165
|
var JwtRefreshSecret = process.env.JWT_REFRESH_SECRET || "refresh_secret";
|
|
@@ -149,78 +176,7 @@ function isDebug(func = void 0) {
|
|
|
149
176
|
return false;
|
|
150
177
|
}
|
|
151
178
|
|
|
152
|
-
// src/utils/encoding.ts
|
|
153
|
-
function encodeToBase64(obj) {
|
|
154
|
-
return btoa(encodeURIComponent(JSON.stringify(obj)));
|
|
155
|
-
}
|
|
156
|
-
function decodeFromBase64(str) {
|
|
157
|
-
if (str == null || str == void 0) {
|
|
158
|
-
return void 0;
|
|
159
|
-
}
|
|
160
|
-
let decoded = str ? decodeURIComponent(atob(str)) : void 0;
|
|
161
|
-
return decoded ? JSON.parse(decoded) : void 0;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// src/db/mongodb.ts
|
|
165
|
-
async function connectMongoDB(uri) {
|
|
166
|
-
if (uri == null || uri == void 0 || uri.length == 0) {
|
|
167
|
-
console.error(`\u{1F6D1} [${getCurrentDateString()}] Error: MongoDB URI is not provided`);
|
|
168
|
-
process.exit(EXIT_CODE_INVALID_CONFIG);
|
|
169
|
-
}
|
|
170
|
-
try {
|
|
171
|
-
await mongoose.connect(uri);
|
|
172
|
-
console.log(`\u2705 [${getCurrentDateString()}] MongoDB connected`);
|
|
173
|
-
} catch (err) {
|
|
174
|
-
console.error(`\u{1F6D1} [${getCurrentDateString()}] Error: MongoDB connection failed`, err);
|
|
175
|
-
process.exit(EXIT_CODE_GENERAL_ERROR);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// src/middleware/file-storage.ts
|
|
180
|
-
import multer from "multer";
|
|
181
|
-
import path from "node:path";
|
|
182
|
-
import fs from "node:fs";
|
|
183
|
-
var uploadDir = path.resolve(process.cwd(), "src", "files");
|
|
184
|
-
fs.mkdirSync(uploadDir, { recursive: true });
|
|
185
|
-
var sanitizeFilename = (name) => name.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
186
|
-
var diskStorage = multer.diskStorage({
|
|
187
|
-
destination: (_req, _file, next) => next(null, uploadDir),
|
|
188
|
-
filename: (_req, file, next) => {
|
|
189
|
-
const ext = path.extname(file.originalname);
|
|
190
|
-
const base = path.basename(file.originalname, ext);
|
|
191
|
-
const safe = sanitizeFilename(base);
|
|
192
|
-
const unique = Date.now() + "-" + Math.round(Math.random() * 1e9);
|
|
193
|
-
next(null, `${safe}-${unique}${ext}`);
|
|
194
|
-
}
|
|
195
|
-
});
|
|
196
|
-
var memoryStorage = multer.memoryStorage();
|
|
197
|
-
var diskFileUpload = multer({ storage: diskStorage });
|
|
198
|
-
var memoryFileUpload = multer({ storage: memoryStorage });
|
|
199
|
-
|
|
200
|
-
// src/middleware/logger.ts
|
|
201
|
-
function requestLogger(req, res, next) {
|
|
202
|
-
res.on("finish", () => {
|
|
203
|
-
let emoji = "";
|
|
204
|
-
if (res.statusCode >= 100 && res.statusCode < 200) emoji = "\u{1F4A1}";
|
|
205
|
-
else if (res.statusCode >= 200 && res.statusCode < 300) emoji = "\u2705";
|
|
206
|
-
else if (res.statusCode >= 300 && res.statusCode < 400) emoji = "\u{1F6A6}";
|
|
207
|
-
else if (res.statusCode == 401) emoji = "\u{1F501}";
|
|
208
|
-
else if (res.statusCode >= 400 && res.statusCode < 500) emoji = "\u26A0\uFE0F";
|
|
209
|
-
else if (res.statusCode >= 500) emoji = "\u{1F525}";
|
|
210
|
-
console.log(
|
|
211
|
-
`${emoji} [${getCurrentDateString()}] ${req.method} ${req.originalUrl} - ${res.statusCode}`
|
|
212
|
-
);
|
|
213
|
-
});
|
|
214
|
-
next();
|
|
215
|
-
}
|
|
216
|
-
function errorLogger(err, req, res, next) {
|
|
217
|
-
console.error(`\u{1F6D1} [${getCurrentDateString()}] Error in ${req.method} ${req.originalUrl}:`, err);
|
|
218
|
-
res.status(500).json();
|
|
219
|
-
}
|
|
220
|
-
|
|
221
179
|
// src/middleware/authentication/session.ts
|
|
222
|
-
import express from "express";
|
|
223
|
-
import bcrypt from "bcrypt";
|
|
224
180
|
import jwt2 from "jsonwebtoken";
|
|
225
181
|
|
|
226
182
|
// src/middleware/authentication/validation.ts
|
|
@@ -359,6 +315,7 @@ async function socketToken(_socket, _next) {
|
|
|
359
315
|
}
|
|
360
316
|
|
|
361
317
|
// src/middleware/authentication/session.ts
|
|
318
|
+
import { encodeToBase64, validateString } from "tsledge-core";
|
|
362
319
|
var router = express.Router();
|
|
363
320
|
var FORBIDDEN2 = 403;
|
|
364
321
|
var BAD_REQUEST = 400;
|
|
@@ -534,7 +491,7 @@ async function authRefresh(req, res, next) {
|
|
|
534
491
|
}
|
|
535
492
|
|
|
536
493
|
// src/fluent-interface/fluent-pattern-handler.ts
|
|
537
|
-
import
|
|
494
|
+
import mongoose4 from "mongoose";
|
|
538
495
|
|
|
539
496
|
// src/fluent-interface/types.ts
|
|
540
497
|
var fluentRequestQueryAttributes = {
|
|
@@ -547,34 +504,180 @@ var fluentRequestQueryAttributes = {
|
|
|
547
504
|
excluded: ""
|
|
548
505
|
};
|
|
549
506
|
|
|
550
|
-
// src/
|
|
551
|
-
import
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
507
|
+
// src/fluent-interface/fluent-pattern-handler.ts
|
|
508
|
+
import { Codec } from "tsledge-core";
|
|
509
|
+
var FluentPatternHandler = class _FluentPatternHandler {
|
|
510
|
+
/**
|
|
511
|
+
* Constructor for FluentPatternHandler.
|
|
512
|
+
* @param execMiddleware - Optional array of middleware functions to be executed before the main query execution in the exec method.
|
|
513
|
+
*/
|
|
514
|
+
constructor(execMiddleware = []) {
|
|
515
|
+
/**
|
|
516
|
+
* 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.
|
|
517
|
+
*/
|
|
518
|
+
this._execMiddlewareFunctions = [];
|
|
519
|
+
if (_FluentPatternHandler._singleton) {
|
|
520
|
+
throw new Error(
|
|
521
|
+
"FluentPatternHandler is a singleton class. Use FluentPatternHandler.getInstance() to access the instance."
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
this._execMiddlewareFunctions = execMiddleware;
|
|
525
|
+
_FluentPatternHandler._singleton = this;
|
|
558
526
|
}
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
527
|
+
/**
|
|
528
|
+
* Initializes the singleton instance of FluentPatternHandler with the provided options.
|
|
529
|
+
* @param execMiddleware - Optional array of middleware functions to be executed before the main query execution in the exec method.
|
|
530
|
+
* @returns Singleton instance of FluentPatternHandler.
|
|
531
|
+
*/
|
|
532
|
+
static init(execMiddleware = []) {
|
|
533
|
+
if (_FluentPatternHandler._singleton != void 0) {
|
|
534
|
+
throw new Error("FluentPatternHandler is already initialized");
|
|
562
535
|
}
|
|
563
|
-
|
|
536
|
+
_FluentPatternHandler._singleton = new _FluentPatternHandler(execMiddleware);
|
|
537
|
+
return _FluentPatternHandler._singleton;
|
|
564
538
|
}
|
|
565
|
-
|
|
566
|
-
|
|
539
|
+
/**
|
|
540
|
+
* Returns the singleton instance of FluentPatternHandler.
|
|
541
|
+
* @returns Singleton instance of FluentPatternHandler.
|
|
542
|
+
*/
|
|
543
|
+
static getInstance() {
|
|
544
|
+
if (_FluentPatternHandler._singleton == void 0) {
|
|
545
|
+
throw new Error(
|
|
546
|
+
"FluentPatternHandler instance has not been created yet. Please create an instance before calling getInstance()."
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
return _FluentPatternHandler._singleton;
|
|
567
550
|
}
|
|
568
|
-
|
|
569
|
-
|
|
551
|
+
/**
|
|
552
|
+
* Parses and validates query parameters from the request.
|
|
553
|
+
* @param query - The query object from the request.
|
|
554
|
+
* @returns Parsed query parameters.
|
|
555
|
+
*/
|
|
556
|
+
_parseFluentRequestQuery(query) {
|
|
557
|
+
const { filter, limit = "5", offset = "0", id, excluded: excludedJSON, ids: idsJSON } = query;
|
|
558
|
+
const queryKeys = Object.keys(fluentRequestQueryAttributes);
|
|
559
|
+
let filterFields = {};
|
|
560
|
+
for (const [key, value] of Object.entries(query)) {
|
|
561
|
+
if (queryKeys.includes(key)) continue;
|
|
562
|
+
if (value == void 0) continue;
|
|
563
|
+
try {
|
|
564
|
+
filterFields[key] = typeof value === "string" ? value : JSON.stringify(value);
|
|
565
|
+
} catch (e) {
|
|
566
|
+
filterFields[key] = String(value);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
let excluded;
|
|
570
|
+
if (excludedJSON) {
|
|
571
|
+
try {
|
|
572
|
+
excluded = JSON.parse(excludedJSON);
|
|
573
|
+
if (!Array.isArray(excluded)) throw new Error("Excluded must be an array");
|
|
574
|
+
} catch (error) {
|
|
575
|
+
console.warn("[FluentPatternHandler] Invalid excluded parameter:", error);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
let ids;
|
|
579
|
+
if (idsJSON) {
|
|
580
|
+
try {
|
|
581
|
+
ids = JSON.parse(idsJSON);
|
|
582
|
+
if (!Array.isArray(ids)) throw new Error("Ids must be an array");
|
|
583
|
+
} catch (error) {
|
|
584
|
+
console.warn("[FluentPatternHandler] Invalid ids parameter:", error);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
return { filter, limit, offset, id, excluded, ids, filterFields };
|
|
570
588
|
}
|
|
571
|
-
|
|
572
|
-
|
|
589
|
+
/**
|
|
590
|
+
* Applies filters to the query builder based on parsed parameters and options.
|
|
591
|
+
* @param queryBuilder - The QueryBuilder instance.
|
|
592
|
+
* @param params - Parsed query parameters.
|
|
593
|
+
* @param config - Query request configuration.
|
|
594
|
+
*/
|
|
595
|
+
_applyParameters(queryBuilder, params) {
|
|
596
|
+
const { id, ids, filter, excluded } = params;
|
|
597
|
+
if (id) {
|
|
598
|
+
queryBuilder.match({ _id: new mongoose4.Types.ObjectId(id) });
|
|
599
|
+
} else if (ids && ids.length > 0) {
|
|
600
|
+
const objectIds = ids.map((id2) => new mongoose4.Types.ObjectId(id2));
|
|
601
|
+
queryBuilder.match({ _id: { $in: objectIds } });
|
|
602
|
+
} else {
|
|
603
|
+
const modelFilterFields = this._getFilterFieldsForModel(queryBuilder.getConfig().model);
|
|
604
|
+
if (filter && modelFilterFields.length > 0) {
|
|
605
|
+
const ors = modelFilterFields.map((field) => ({
|
|
606
|
+
[field]: { $regex: filter, $options: "i" }
|
|
607
|
+
}));
|
|
608
|
+
queryBuilder.match({ $or: ors });
|
|
609
|
+
}
|
|
610
|
+
if (params.filterFields && Object.keys(params.filterFields).length > 0) {
|
|
611
|
+
for (const [field, value] of Object.entries(params.filterFields)) {
|
|
612
|
+
if (!modelFilterFields.includes(field)) {
|
|
613
|
+
continue;
|
|
614
|
+
}
|
|
615
|
+
const match = {};
|
|
616
|
+
match[field] = { $regex: value, $options: "i" };
|
|
617
|
+
queryBuilder.match(match);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
if (excluded && excluded.length > 0) {
|
|
621
|
+
const objectIds = excluded.map((id2) => new mongoose4.Types.ObjectId(id2));
|
|
622
|
+
queryBuilder.match({ _id: { $nin: objectIds } });
|
|
623
|
+
}
|
|
624
|
+
}
|
|
573
625
|
}
|
|
574
|
-
|
|
575
|
-
|
|
626
|
+
/**
|
|
627
|
+
* Generates filter fields for the given Mongoose model based on schema options.
|
|
628
|
+
* @param model - The Mongoose model.
|
|
629
|
+
* @returns Array of filter fields.
|
|
630
|
+
*/
|
|
631
|
+
_getFilterFieldsForModel(model) {
|
|
632
|
+
let filterFields = [];
|
|
633
|
+
let schema = model.schema;
|
|
634
|
+
schema.eachPath((path2, type) => {
|
|
635
|
+
if (type?.options?.filter == true) {
|
|
636
|
+
filterFields.push(path2);
|
|
637
|
+
}
|
|
638
|
+
});
|
|
639
|
+
return filterFields;
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* Builds execution parameters for the query builder.
|
|
643
|
+
* @param params - Parsed query parameters.
|
|
644
|
+
* @returns Execution parameters.
|
|
645
|
+
*/
|
|
646
|
+
_buildExecutionConfig(params) {
|
|
647
|
+
const { id, limit, offset } = params;
|
|
648
|
+
return {
|
|
649
|
+
isOne: Boolean(id),
|
|
650
|
+
limit: limit === "full" ? void 0 : parseInt(limit || "5", 10),
|
|
651
|
+
skip: parseInt(offset || "0", 10)
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* Executes the query builder with applied filters and returns the result.
|
|
656
|
+
* @param params Execution parameters including the query builder and request query.
|
|
657
|
+
* @returns
|
|
658
|
+
*/
|
|
659
|
+
async exec(params) {
|
|
660
|
+
try {
|
|
661
|
+
if (this._execMiddlewareFunctions && this._execMiddlewareFunctions.length > 0) {
|
|
662
|
+
for (const func of this._execMiddlewareFunctions) {
|
|
663
|
+
await func(params);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
const queryParams = this._parseFluentRequestQuery(params.req.query);
|
|
667
|
+
this._applyParameters(params.queryBuilder, queryParams);
|
|
668
|
+
const execConfig = this._buildExecutionConfig(queryParams);
|
|
669
|
+
return await params.queryBuilder.exec(execConfig);
|
|
670
|
+
} catch (err) {
|
|
671
|
+
console.error("[ERROR - FluentPatternHandler]", err);
|
|
672
|
+
return new Codec({ data: [], meta: { total: 0 } }, 500);
|
|
673
|
+
}
|
|
576
674
|
}
|
|
577
675
|
};
|
|
676
|
+
|
|
677
|
+
// src/query-builder/query-builder.ts
|
|
678
|
+
import mongoose5 from "mongoose";
|
|
679
|
+
|
|
680
|
+
// src/query-builder/types.ts
|
|
578
681
|
var JoinRelation = class {
|
|
579
682
|
constructor(localField, ref, alias = void 0) {
|
|
580
683
|
this.ref = ref;
|
|
@@ -582,15 +685,9 @@ var JoinRelation = class {
|
|
|
582
685
|
this.alias = alias ? alias : ref.collection.name;
|
|
583
686
|
}
|
|
584
687
|
};
|
|
585
|
-
var HttpMethod = /* @__PURE__ */ ((HttpMethod2) => {
|
|
586
|
-
HttpMethod2["POST"] = "post";
|
|
587
|
-
HttpMethod2["GET"] = "get";
|
|
588
|
-
HttpMethod2["DELETE"] = "delete";
|
|
589
|
-
HttpMethod2["PUT"] = "put";
|
|
590
|
-
return HttpMethod2;
|
|
591
|
-
})(HttpMethod || {});
|
|
592
688
|
|
|
593
|
-
// src/
|
|
689
|
+
// src/query-builder/query-builder.ts
|
|
690
|
+
import { Codec as Codec2 } from "tsledge-core";
|
|
594
691
|
var QueryBuilder = class {
|
|
595
692
|
constructor(config) {
|
|
596
693
|
this._matchConditions = {};
|
|
@@ -693,7 +790,7 @@ var QueryBuilder = class {
|
|
|
693
790
|
if (schematype.options?.ref) {
|
|
694
791
|
const refModelName = schematype.options.ref;
|
|
695
792
|
try {
|
|
696
|
-
const refModel =
|
|
793
|
+
const refModel = mongoose5.model(refModelName);
|
|
697
794
|
let alias = schematype.options?.alias ?? refModel.collection.name;
|
|
698
795
|
this.join(new JoinRelation(fullPath, refModel, alias));
|
|
699
796
|
} catch (err) {
|
|
@@ -702,10 +799,10 @@ var QueryBuilder = class {
|
|
|
702
799
|
);
|
|
703
800
|
}
|
|
704
801
|
}
|
|
705
|
-
if (schematype instanceof
|
|
802
|
+
if (schematype instanceof mongoose5.Schema.Types.Array && schematype.caster?.options?.ref) {
|
|
706
803
|
const refModelName = schematype.caster.options.ref;
|
|
707
804
|
try {
|
|
708
|
-
const refModel =
|
|
805
|
+
const refModel = mongoose5.model(refModelName);
|
|
709
806
|
let alias = schematype.caster.options?.alias ?? refModel.collection.name;
|
|
710
807
|
this.join(new JoinRelation(fullPath, refModel, alias));
|
|
711
808
|
} catch (err) {
|
|
@@ -787,10 +884,10 @@ var QueryBuilder = class {
|
|
|
787
884
|
]);
|
|
788
885
|
const totalCount = countRes && countRes[0] ? countRes[0].n : 0;
|
|
789
886
|
const documents = config && config.isOne ? await this._processSingleDocument(res) : await this._processMultipleDocuments(res);
|
|
790
|
-
return new
|
|
887
|
+
return new Codec2({ data: documents, meta: { total: totalCount } }, 200);
|
|
791
888
|
} catch (err) {
|
|
792
889
|
console.error("[ERROR - QueryBuilder]", err);
|
|
793
|
-
return new
|
|
890
|
+
return new Codec2({ data: [], meta: { total: 0 } }, 500);
|
|
794
891
|
}
|
|
795
892
|
}
|
|
796
893
|
/**
|
|
@@ -831,236 +928,6 @@ var QueryBuilder = class {
|
|
|
831
928
|
}
|
|
832
929
|
};
|
|
833
930
|
|
|
834
|
-
// src/core/http.ts
|
|
835
|
-
function formDataParser(obj) {
|
|
836
|
-
return new URLSearchParams(obj).toString();
|
|
837
|
-
}
|
|
838
|
-
async function httpRequest(config) {
|
|
839
|
-
const url = new URL(config.url);
|
|
840
|
-
if (config.urlSearchParams) {
|
|
841
|
-
Object.keys(config.urlSearchParams).forEach((key) => {
|
|
842
|
-
url.searchParams.set(key, String(config.urlSearchParams[key]));
|
|
843
|
-
});
|
|
844
|
-
}
|
|
845
|
-
let body;
|
|
846
|
-
let defaultContentType = "application/x-www-form-urlencoded";
|
|
847
|
-
if (config.body) {
|
|
848
|
-
if (typeof config.body === "object") {
|
|
849
|
-
body = JSON.stringify(config.body);
|
|
850
|
-
defaultContentType = "application/json";
|
|
851
|
-
} else {
|
|
852
|
-
body = String(config.body);
|
|
853
|
-
}
|
|
854
|
-
}
|
|
855
|
-
const fetchImpl = globalThis.fetch;
|
|
856
|
-
if (!fetchImpl) {
|
|
857
|
-
throw new Error("fetch() is not available. Please use Node.js 18+ or add a fetch polyfill.");
|
|
858
|
-
}
|
|
859
|
-
const defaultHeaders = {};
|
|
860
|
-
if (!config.headers || !Object.keys(config.headers).find((key) => key.toLowerCase() === "content-type")) {
|
|
861
|
-
defaultHeaders["Content-Type"] = defaultContentType;
|
|
862
|
-
}
|
|
863
|
-
const response = await fetchImpl(url.toString(), {
|
|
864
|
-
method: config.method?.toUpperCase() || "GET",
|
|
865
|
-
body,
|
|
866
|
-
headers: {
|
|
867
|
-
...defaultHeaders,
|
|
868
|
-
...config.headers
|
|
869
|
-
}
|
|
870
|
-
});
|
|
871
|
-
const contentType = response.headers.get("content-type") || "";
|
|
872
|
-
const isJson = contentType.includes("application/json");
|
|
873
|
-
const isBlob = contentType.includes("application/octet-stream");
|
|
874
|
-
if (!response.ok) {
|
|
875
|
-
const text = await response.text().catch(() => "");
|
|
876
|
-
throw new Error(`HTTP ${response.status} ${response.statusText}: ${text}`);
|
|
877
|
-
}
|
|
878
|
-
let result;
|
|
879
|
-
if (isJson) {
|
|
880
|
-
result = await response.json().catch(async (error) => {
|
|
881
|
-
throw new Error(`Failed to parse JSON response: ${error.message}`);
|
|
882
|
-
});
|
|
883
|
-
} else if (isBlob) {
|
|
884
|
-
result = await response.blob().catch(async (error) => {
|
|
885
|
-
throw new Error(`Failed to get blob response: ${error.message}`);
|
|
886
|
-
});
|
|
887
|
-
} else {
|
|
888
|
-
result = await response.text().catch(async (error) => {
|
|
889
|
-
throw new Error(`Failed to get text response: ${error.message}`);
|
|
890
|
-
});
|
|
891
|
-
}
|
|
892
|
-
return result;
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
// src/fluent-interface/fluent-pattern-handler.ts
|
|
896
|
-
var FluentPatternHandler = class _FluentPatternHandler {
|
|
897
|
-
/**
|
|
898
|
-
* Constructor for FluentPatternHandler.
|
|
899
|
-
* @param execMiddleware - Optional array of middleware functions to be executed before the main query execution in the exec method.
|
|
900
|
-
*/
|
|
901
|
-
constructor(execMiddleware = []) {
|
|
902
|
-
/**
|
|
903
|
-
* 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.
|
|
904
|
-
*/
|
|
905
|
-
this._execMiddlewareFunctions = [];
|
|
906
|
-
if (_FluentPatternHandler._singleton) {
|
|
907
|
-
throw new Error(
|
|
908
|
-
"FluentPatternHandler is a singleton class. Use FluentPatternHandler.getInstance() to access the instance."
|
|
909
|
-
);
|
|
910
|
-
}
|
|
911
|
-
this._execMiddlewareFunctions = execMiddleware;
|
|
912
|
-
_FluentPatternHandler._singleton = this;
|
|
913
|
-
}
|
|
914
|
-
/**
|
|
915
|
-
* Initializes the singleton instance of FluentPatternHandler with the provided options.
|
|
916
|
-
* @param execMiddleware - Optional array of middleware functions to be executed before the main query execution in the exec method.
|
|
917
|
-
* @returns Singleton instance of FluentPatternHandler.
|
|
918
|
-
*/
|
|
919
|
-
static init(execMiddleware = []) {
|
|
920
|
-
if (_FluentPatternHandler._singleton != void 0) {
|
|
921
|
-
throw new Error("FluentPatternHandler is already initialized");
|
|
922
|
-
}
|
|
923
|
-
_FluentPatternHandler._singleton = new _FluentPatternHandler(execMiddleware);
|
|
924
|
-
return _FluentPatternHandler._singleton;
|
|
925
|
-
}
|
|
926
|
-
/**
|
|
927
|
-
* Returns the singleton instance of FluentPatternHandler.
|
|
928
|
-
* @returns Singleton instance of FluentPatternHandler.
|
|
929
|
-
*/
|
|
930
|
-
static getInstance() {
|
|
931
|
-
if (_FluentPatternHandler._singleton == void 0) {
|
|
932
|
-
throw new Error(
|
|
933
|
-
"FluentPatternHandler instance has not been created yet. Please create an instance before calling getInstance()."
|
|
934
|
-
);
|
|
935
|
-
}
|
|
936
|
-
return _FluentPatternHandler._singleton;
|
|
937
|
-
}
|
|
938
|
-
/**
|
|
939
|
-
* Parses and validates query parameters from the request.
|
|
940
|
-
* @param query - The query object from the request.
|
|
941
|
-
* @returns Parsed query parameters.
|
|
942
|
-
*/
|
|
943
|
-
_parseFluentRequestQuery(query) {
|
|
944
|
-
const { filter, limit = "5", offset = "0", id, excluded: excludedJSON, ids: idsJSON } = query;
|
|
945
|
-
const queryKeys = Object.keys(fluentRequestQueryAttributes);
|
|
946
|
-
let filterFields = {};
|
|
947
|
-
for (const [key, value] of Object.entries(query)) {
|
|
948
|
-
if (queryKeys.includes(key)) continue;
|
|
949
|
-
if (value == void 0) continue;
|
|
950
|
-
try {
|
|
951
|
-
filterFields[key] = typeof value === "string" ? value : JSON.stringify(value);
|
|
952
|
-
} catch (e) {
|
|
953
|
-
filterFields[key] = String(value);
|
|
954
|
-
}
|
|
955
|
-
}
|
|
956
|
-
let excluded;
|
|
957
|
-
if (excludedJSON) {
|
|
958
|
-
try {
|
|
959
|
-
excluded = JSON.parse(excludedJSON);
|
|
960
|
-
if (!Array.isArray(excluded)) throw new Error("Excluded must be an array");
|
|
961
|
-
} catch (error) {
|
|
962
|
-
console.warn("[FluentPatternHandler] Invalid excluded parameter:", error);
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
let ids;
|
|
966
|
-
if (idsJSON) {
|
|
967
|
-
try {
|
|
968
|
-
ids = JSON.parse(idsJSON);
|
|
969
|
-
if (!Array.isArray(ids)) throw new Error("Ids must be an array");
|
|
970
|
-
} catch (error) {
|
|
971
|
-
console.warn("[FluentPatternHandler] Invalid ids parameter:", error);
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
return { filter, limit, offset, id, excluded, ids, filterFields };
|
|
975
|
-
}
|
|
976
|
-
/**
|
|
977
|
-
* Applies filters to the query builder based on parsed parameters and options.
|
|
978
|
-
* @param queryBuilder - The QueryBuilder instance.
|
|
979
|
-
* @param params - Parsed query parameters.
|
|
980
|
-
* @param config - Query request configuration.
|
|
981
|
-
*/
|
|
982
|
-
_applyParameters(queryBuilder, params) {
|
|
983
|
-
const { id, ids, filter, excluded } = params;
|
|
984
|
-
if (id) {
|
|
985
|
-
queryBuilder.match({ _id: new mongoose5.Types.ObjectId(id) });
|
|
986
|
-
} else if (ids && ids.length > 0) {
|
|
987
|
-
const objectIds = ids.map((id2) => new mongoose5.Types.ObjectId(id2));
|
|
988
|
-
queryBuilder.match({ _id: { $in: objectIds } });
|
|
989
|
-
} else {
|
|
990
|
-
const modelFilterFields = this._getFilterFieldsForModel(queryBuilder.getConfig().model);
|
|
991
|
-
if (filter && modelFilterFields.length > 0) {
|
|
992
|
-
const ors = modelFilterFields.map((field) => ({
|
|
993
|
-
[field]: { $regex: filter, $options: "i" }
|
|
994
|
-
}));
|
|
995
|
-
queryBuilder.match({ $or: ors });
|
|
996
|
-
}
|
|
997
|
-
if (params.filterFields && Object.keys(params.filterFields).length > 0) {
|
|
998
|
-
for (const [field, value] of Object.entries(params.filterFields)) {
|
|
999
|
-
if (!modelFilterFields.includes(field)) {
|
|
1000
|
-
continue;
|
|
1001
|
-
}
|
|
1002
|
-
const match = {};
|
|
1003
|
-
match[field] = { $regex: value, $options: "i" };
|
|
1004
|
-
queryBuilder.match(match);
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
if (excluded && excluded.length > 0) {
|
|
1008
|
-
const objectIds = excluded.map((id2) => new mongoose5.Types.ObjectId(id2));
|
|
1009
|
-
queryBuilder.match({ _id: { $nin: objectIds } });
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
/**
|
|
1014
|
-
* Generates filter fields for the given Mongoose model based on schema options.
|
|
1015
|
-
* @param model - The Mongoose model.
|
|
1016
|
-
* @returns Array of filter fields.
|
|
1017
|
-
*/
|
|
1018
|
-
_getFilterFieldsForModel(model) {
|
|
1019
|
-
let filterFields = [];
|
|
1020
|
-
let schema = model.schema;
|
|
1021
|
-
schema.eachPath((path2, type) => {
|
|
1022
|
-
if (type?.options?.filter == true) {
|
|
1023
|
-
filterFields.push(path2);
|
|
1024
|
-
}
|
|
1025
|
-
});
|
|
1026
|
-
return filterFields;
|
|
1027
|
-
}
|
|
1028
|
-
/**
|
|
1029
|
-
* Builds execution parameters for the query builder.
|
|
1030
|
-
* @param params - Parsed query parameters.
|
|
1031
|
-
* @returns Execution parameters.
|
|
1032
|
-
*/
|
|
1033
|
-
_buildExecutionConfig(params) {
|
|
1034
|
-
const { id, limit, offset } = params;
|
|
1035
|
-
return {
|
|
1036
|
-
isOne: Boolean(id),
|
|
1037
|
-
limit: limit === "full" ? void 0 : parseInt(limit || "5", 10),
|
|
1038
|
-
skip: parseInt(offset || "0", 10)
|
|
1039
|
-
};
|
|
1040
|
-
}
|
|
1041
|
-
/**
|
|
1042
|
-
* Executes the query builder with applied filters and returns the result.
|
|
1043
|
-
* @param params Execution parameters including the query builder and request query.
|
|
1044
|
-
* @returns
|
|
1045
|
-
*/
|
|
1046
|
-
async exec(params) {
|
|
1047
|
-
try {
|
|
1048
|
-
if (this._execMiddlewareFunctions && this._execMiddlewareFunctions.length > 0) {
|
|
1049
|
-
for (const func of this._execMiddlewareFunctions) {
|
|
1050
|
-
await func(params);
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
const queryParams = this._parseFluentRequestQuery(params.req.query);
|
|
1054
|
-
this._applyParameters(params.queryBuilder, queryParams);
|
|
1055
|
-
const execConfig = this._buildExecutionConfig(queryParams);
|
|
1056
|
-
return await params.queryBuilder.exec(execConfig);
|
|
1057
|
-
} catch (err) {
|
|
1058
|
-
console.error("[ERROR - FluentPatternHandler]", err);
|
|
1059
|
-
return new Codec({ data: [], meta: { total: 0 } }, 500);
|
|
1060
|
-
}
|
|
1061
|
-
}
|
|
1062
|
-
};
|
|
1063
|
-
|
|
1064
931
|
// src/app.ts
|
|
1065
932
|
import express2 from "express";
|
|
1066
933
|
import cors from "cors";
|