@avleon/core 0.0.46 → 0.0.49
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/License +21 -21
- package/README.md +666 -666
- package/dist/chunk-9hOWP6kD.cjs +64 -0
- package/dist/chunk-DORXReHP.js +37 -0
- package/dist/index-CWUcfHYp.d.ts +1283 -0
- package/dist/index-CzAzauXZ.d.cts +1282 -0
- package/dist/index.cjs +3194 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +3022 -79
- package/dist/index.js.map +1 -0
- package/dist/lib-Bk8hUm06.cjs +7847 -0
- package/dist/lib-Bk8hUm06.cjs.map +1 -0
- package/dist/lib-CvDxBMkR.js +7843 -0
- package/dist/lib-CvDxBMkR.js.map +1 -0
- package/package.json +41 -103
- package/dist/application.test.d.ts +0 -1
- package/dist/application.test.js +0 -15
- package/dist/authentication.d.ts +0 -13
- package/dist/authentication.js +0 -16
- package/dist/cache.d.ts +0 -12
- package/dist/cache.js +0 -78
- package/dist/cache.test.d.ts +0 -1
- package/dist/cache.test.js +0 -36
- package/dist/collection.d.ts +0 -43
- package/dist/collection.js +0 -231
- package/dist/collection.test.d.ts +0 -1
- package/dist/collection.test.js +0 -59
- package/dist/config.d.ts +0 -18
- package/dist/config.js +0 -58
- package/dist/config.test.d.ts +0 -1
- package/dist/config.test.js +0 -40
- package/dist/constants.d.ts +0 -1
- package/dist/constants.js +0 -4
- package/dist/container.d.ts +0 -30
- package/dist/container.js +0 -55
- package/dist/controller.d.ts +0 -50
- package/dist/controller.js +0 -71
- package/dist/controller.test.d.ts +0 -1
- package/dist/controller.test.js +0 -97
- package/dist/core/application.d.ts +0 -74
- package/dist/core/application.js +0 -424
- package/dist/core/router.d.ts +0 -44
- package/dist/core/router.js +0 -520
- package/dist/core/testing.d.ts +0 -21
- package/dist/core/testing.js +0 -104
- package/dist/core/types.d.ts +0 -67
- package/dist/core/types.js +0 -2
- package/dist/decorators.d.ts +0 -15
- package/dist/decorators.js +0 -41
- package/dist/environment-variables.d.ts +0 -49
- package/dist/environment-variables.js +0 -130
- package/dist/environment-variables.test.d.ts +0 -1
- package/dist/environment-variables.test.js +0 -70
- package/dist/event-dispatcher.d.ts +0 -22
- package/dist/event-dispatcher.js +0 -97
- package/dist/event-subscriber.d.ts +0 -14
- package/dist/event-subscriber.js +0 -87
- package/dist/exceptions/http-exceptions.d.ts +0 -50
- package/dist/exceptions/http-exceptions.js +0 -85
- package/dist/exceptions/index.d.ts +0 -1
- package/dist/exceptions/index.js +0 -17
- package/dist/exceptions/system-exception.d.ts +0 -22
- package/dist/exceptions/system-exception.js +0 -26
- package/dist/file-storage.d.ts +0 -69
- package/dist/file-storage.js +0 -323
- package/dist/file-storage.test.d.ts +0 -1
- package/dist/file-storage.test.js +0 -117
- package/dist/helpers.d.ts +0 -11
- package/dist/helpers.js +0 -27
- package/dist/helpers.test.d.ts +0 -1
- package/dist/helpers.test.js +0 -95
- package/dist/index.d.ts +0 -57
- package/dist/interfaces/avleon-application.d.ts +0 -75
- package/dist/interfaces/avleon-application.js +0 -2
- package/dist/kenx-provider.d.ts +0 -7
- package/dist/kenx-provider.js +0 -44
- package/dist/kenx-provider.test.d.ts +0 -1
- package/dist/kenx-provider.test.js +0 -36
- package/dist/logger.d.ts +0 -12
- package/dist/logger.js +0 -87
- package/dist/logger.test.d.ts +0 -1
- package/dist/logger.test.js +0 -42
- package/dist/map-types.d.ts +0 -17
- package/dist/map-types.js +0 -89
- package/dist/middleware.d.ts +0 -34
- package/dist/middleware.js +0 -73
- package/dist/middleware.test.d.ts +0 -1
- package/dist/middleware.test.js +0 -121
- package/dist/multipart.d.ts +0 -17
- package/dist/multipart.js +0 -70
- package/dist/multipart.test.d.ts +0 -1
- package/dist/multipart.test.js +0 -87
- package/dist/openapi.d.ts +0 -410
- package/dist/openapi.js +0 -59
- package/dist/openapi.test.d.ts +0 -1
- package/dist/openapi.test.js +0 -111
- package/dist/params.d.ts +0 -17
- package/dist/params.js +0 -64
- package/dist/params.test.d.ts +0 -1
- package/dist/params.test.js +0 -83
- package/dist/queue.d.ts +0 -29
- package/dist/queue.js +0 -84
- package/dist/response.d.ts +0 -16
- package/dist/response.js +0 -56
- package/dist/results.d.ts +0 -20
- package/dist/results.js +0 -32
- package/dist/route-methods.d.ts +0 -25
- package/dist/route-methods.js +0 -60
- package/dist/route-methods.test.d.ts +0 -1
- package/dist/route-methods.test.js +0 -129
- package/dist/swagger-schema.d.ts +0 -37
- package/dist/swagger-schema.js +0 -454
- package/dist/swagger-schema.test.d.ts +0 -1
- package/dist/swagger-schema.test.js +0 -125
- package/dist/types/app-builder.interface.d.ts +0 -15
- package/dist/types/app-builder.interface.js +0 -8
- package/dist/types/application.interface.d.ts +0 -8
- package/dist/types/application.interface.js +0 -2
- package/dist/utils/common-utils.d.ts +0 -17
- package/dist/utils/common-utils.js +0 -108
- package/dist/utils/di-utils.d.ts +0 -1
- package/dist/utils/di-utils.js +0 -22
- package/dist/utils/hash.d.ts +0 -2
- package/dist/utils/hash.js +0 -11
- package/dist/utils/index.d.ts +0 -2
- package/dist/utils/index.js +0 -18
- package/dist/utils/object-utils.d.ts +0 -11
- package/dist/utils/object-utils.js +0 -198
- package/dist/utils/optional-require.d.ts +0 -8
- package/dist/utils/optional-require.js +0 -70
- package/dist/utils/validation-utils.d.ts +0 -13
- package/dist/utils/validation-utils.js +0 -119
- package/dist/validation.d.ts +0 -39
- package/dist/validation.js +0 -108
- package/dist/validation.test.d.ts +0 -1
- package/dist/validation.test.js +0 -61
- package/dist/validator-extend.d.ts +0 -7
- package/dist/validator-extend.js +0 -28
- package/dist/websocket.d.ts +0 -10
- package/dist/websocket.js +0 -21
- package/dist/websocket.test.d.ts +0 -1
- package/dist/websocket.test.js +0 -27
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,3194 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_chunk = require("./chunk-9hOWP6kD.cjs");
|
|
3
|
+
let class_validator = require("class-validator");
|
|
4
|
+
let typedi = require("typedi");
|
|
5
|
+
typedi = require_chunk.__toESM(typedi);
|
|
6
|
+
require("reflect-metadata");
|
|
7
|
+
let node_fs = require("node:fs");
|
|
8
|
+
node_fs = require_chunk.__toESM(node_fs);
|
|
9
|
+
let node_crypto = require("node:crypto");
|
|
10
|
+
node_crypto = require_chunk.__toESM(node_crypto);
|
|
11
|
+
let class_transformer = require("class-transformer");
|
|
12
|
+
let fastify = require("fastify");
|
|
13
|
+
fastify = require_chunk.__toESM(fastify);
|
|
14
|
+
let node_path = require("node:path");
|
|
15
|
+
node_path = require_chunk.__toESM(node_path);
|
|
16
|
+
let mime = require("mime");
|
|
17
|
+
mime = require_chunk.__toESM(mime);
|
|
18
|
+
let node_stream = require("node:stream");
|
|
19
|
+
node_stream = require_chunk.__toESM(node_stream);
|
|
20
|
+
let node_async_hooks = require("node:async_hooks");
|
|
21
|
+
let bull = require("bull");
|
|
22
|
+
bull = require_chunk.__toESM(bull);
|
|
23
|
+
let node_stream_promises = require("node:stream/promises");
|
|
24
|
+
let dotenv = require("dotenv");
|
|
25
|
+
dotenv = require_chunk.__toESM(dotenv);
|
|
26
|
+
let pino = require("pino");
|
|
27
|
+
pino = require_chunk.__toESM(pino);
|
|
28
|
+
//#region src/container.ts
|
|
29
|
+
function registerController(controller) {
|
|
30
|
+
controllerRegistry.add(controller);
|
|
31
|
+
}
|
|
32
|
+
function registerService(service) {
|
|
33
|
+
typedi.Container.set(service, service);
|
|
34
|
+
serviceRegistry.add(service);
|
|
35
|
+
}
|
|
36
|
+
function getRegisteredServices() {
|
|
37
|
+
return Array.from(serviceRegistry);
|
|
38
|
+
}
|
|
39
|
+
function getRegisteredControllers() {
|
|
40
|
+
return Array.from(controllerRegistry);
|
|
41
|
+
}
|
|
42
|
+
function isApiController(target) {
|
|
43
|
+
return Reflect.getMetadata(API_CONTROLLER_METADATA_KEY, target) === true;
|
|
44
|
+
}
|
|
45
|
+
function registerDataSource(dataSource) {
|
|
46
|
+
typedi.Container.set("idatasource", dataSource);
|
|
47
|
+
}
|
|
48
|
+
function registerKnex(dataSource) {
|
|
49
|
+
typedi.Container.set("KnexConnection", dataSource);
|
|
50
|
+
}
|
|
51
|
+
var FEATURE_KEY, ROUTE_META_KEY, CONTROLLER_META_KEY, PARAM_META_KEY, QUERY_META_KEY, REQUEST_BODY_META_KEY, REQUEST_BODY_FILE_KEY, REQUEST_BODY_FILES_KEY, REQUEST_USER_META_KEY, REQUEST_HEADER_META_KEY, DATASOURCE_META_KEY, AUTHORIZATION_META_KEY, controllerRegistry, serviceRegistry, API_CONTROLLER_METADATA_KEY, container_default;
|
|
52
|
+
var init_container = require_chunk.__esmMin((() => {
|
|
53
|
+
FEATURE_KEY = Symbol.for("features");
|
|
54
|
+
ROUTE_META_KEY = Symbol("iroute:options");
|
|
55
|
+
CONTROLLER_META_KEY = Symbol("icontroller:options");
|
|
56
|
+
PARAM_META_KEY = Symbol("iparam:options");
|
|
57
|
+
QUERY_META_KEY = Symbol("iparam:options");
|
|
58
|
+
REQUEST_BODY_META_KEY = Symbol("iparam:options");
|
|
59
|
+
REQUEST_BODY_FILE_KEY = Symbol("iparam:options");
|
|
60
|
+
REQUEST_BODY_FILES_KEY = Symbol("iparam:options");
|
|
61
|
+
REQUEST_USER_META_KEY = Symbol("iparam:options");
|
|
62
|
+
REQUEST_HEADER_META_KEY = Symbol("iheader:options");
|
|
63
|
+
DATASOURCE_META_KEY = Symbol("idatasource:options");
|
|
64
|
+
AUTHORIZATION_META_KEY = Symbol("idatasource:authorization");
|
|
65
|
+
controllerRegistry = /* @__PURE__ */ new Set();
|
|
66
|
+
serviceRegistry = /* @__PURE__ */ new Set();
|
|
67
|
+
API_CONTROLLER_METADATA_KEY = Symbol("apiController");
|
|
68
|
+
typedi.Container.set("appName", "Iqra");
|
|
69
|
+
container_default = typedi.Container;
|
|
70
|
+
}));
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region src/swagger-schema.ts
|
|
73
|
+
/**
|
|
74
|
+
* @copyright 2024
|
|
75
|
+
* @author Tareq Hossain
|
|
76
|
+
* @email xtrinsic96@gmail.com
|
|
77
|
+
* @url https://github.com/xtareq
|
|
78
|
+
*/
|
|
79
|
+
init_container();
|
|
80
|
+
function OpenApiProperty$1(options) {
|
|
81
|
+
return function(target, propertyKey) {
|
|
82
|
+
let meta = options ? { ...options } : {};
|
|
83
|
+
if (meta.format === "binary") if (meta.isArray) meta = {
|
|
84
|
+
...meta,
|
|
85
|
+
type: "array",
|
|
86
|
+
items: meta.items ?? {
|
|
87
|
+
type: "string",
|
|
88
|
+
format: "binary"
|
|
89
|
+
},
|
|
90
|
+
description: meta.description || "Array of files"
|
|
91
|
+
};
|
|
92
|
+
else meta = {
|
|
93
|
+
...meta,
|
|
94
|
+
type: "string",
|
|
95
|
+
format: "binary",
|
|
96
|
+
description: meta.description || "File upload"
|
|
97
|
+
};
|
|
98
|
+
Reflect.defineMetadata("property:openapi", meta, target, propertyKey);
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function extractOpenApiFields(meta) {
|
|
102
|
+
const result = {};
|
|
103
|
+
const jsonSchemaFields = [
|
|
104
|
+
"description",
|
|
105
|
+
"deprecated",
|
|
106
|
+
"example",
|
|
107
|
+
"enum",
|
|
108
|
+
"format",
|
|
109
|
+
"default",
|
|
110
|
+
"minimum",
|
|
111
|
+
"maximum",
|
|
112
|
+
"minLength",
|
|
113
|
+
"maxLength",
|
|
114
|
+
"pattern",
|
|
115
|
+
"oneOf",
|
|
116
|
+
"allOf",
|
|
117
|
+
"anyOf",
|
|
118
|
+
"title",
|
|
119
|
+
"readOnly",
|
|
120
|
+
"writeOnly",
|
|
121
|
+
"nullable"
|
|
122
|
+
];
|
|
123
|
+
const validFormats = [
|
|
124
|
+
"date-time",
|
|
125
|
+
"date",
|
|
126
|
+
"time",
|
|
127
|
+
"duration",
|
|
128
|
+
"email",
|
|
129
|
+
"idn-email",
|
|
130
|
+
"hostname",
|
|
131
|
+
"idn-hostname",
|
|
132
|
+
"ipv4",
|
|
133
|
+
"ipv6",
|
|
134
|
+
"uri",
|
|
135
|
+
"uri-reference",
|
|
136
|
+
"iri",
|
|
137
|
+
"iri-reference",
|
|
138
|
+
"uuid",
|
|
139
|
+
"uri-template",
|
|
140
|
+
"json-pointer",
|
|
141
|
+
"relative-json-pointer",
|
|
142
|
+
"regex",
|
|
143
|
+
"int32",
|
|
144
|
+
"int64",
|
|
145
|
+
"float",
|
|
146
|
+
"double",
|
|
147
|
+
"byte",
|
|
148
|
+
"binary",
|
|
149
|
+
"password"
|
|
150
|
+
];
|
|
151
|
+
jsonSchemaFields.forEach((field) => {
|
|
152
|
+
if (meta[field] !== void 0) if (field === "format") {
|
|
153
|
+
const formatValue = meta[field];
|
|
154
|
+
if (validFormats.includes(formatValue)) result[field] = formatValue;
|
|
155
|
+
} else result[field] = meta[field];
|
|
156
|
+
});
|
|
157
|
+
return result;
|
|
158
|
+
}
|
|
159
|
+
function CreateSwaggerObjectSchema(classType) {
|
|
160
|
+
const validationMetadata = (0, class_validator.getMetadataStorage)().getTargetValidationMetadatas(classType, "", true, false);
|
|
161
|
+
const schema = {
|
|
162
|
+
type: "object",
|
|
163
|
+
properties: {},
|
|
164
|
+
required: []
|
|
165
|
+
};
|
|
166
|
+
const prototype = classType.prototype;
|
|
167
|
+
const propertyKeys = /* @__PURE__ */ new Set();
|
|
168
|
+
Object.getOwnPropertyNames(prototype).forEach((k) => propertyKeys.add(k));
|
|
169
|
+
Object.keys(prototype).forEach((k) => propertyKeys.add(k));
|
|
170
|
+
validationMetadata.forEach((m) => propertyKeys.add(m.propertyName));
|
|
171
|
+
try {
|
|
172
|
+
const instance = new classType();
|
|
173
|
+
Reflect.ownKeys(instance).forEach((k) => {
|
|
174
|
+
if (typeof k === "string") propertyKeys.add(k);
|
|
175
|
+
});
|
|
176
|
+
} catch (_) {}
|
|
177
|
+
propertyKeys.forEach((propertyName) => {
|
|
178
|
+
if (!propertyName || propertyName === "constructor") return;
|
|
179
|
+
const openApiMeta = Reflect.getMetadata("property:openapi", prototype, propertyName);
|
|
180
|
+
if (openApiMeta?.exclude) return;
|
|
181
|
+
const propertyType = Reflect.getMetadata("design:type", prototype, propertyName);
|
|
182
|
+
let swaggerProperty = {};
|
|
183
|
+
switch (propertyType) {
|
|
184
|
+
case String:
|
|
185
|
+
swaggerProperty.type = "string";
|
|
186
|
+
break;
|
|
187
|
+
case Number:
|
|
188
|
+
swaggerProperty.type = "number";
|
|
189
|
+
break;
|
|
190
|
+
case Boolean:
|
|
191
|
+
swaggerProperty.type = "boolean";
|
|
192
|
+
break;
|
|
193
|
+
case Date:
|
|
194
|
+
swaggerProperty.type = "string";
|
|
195
|
+
swaggerProperty.format = "date-time";
|
|
196
|
+
break;
|
|
197
|
+
case Array:
|
|
198
|
+
swaggerProperty.type = "array";
|
|
199
|
+
swaggerProperty.items = { type: "string" };
|
|
200
|
+
break;
|
|
201
|
+
case Object:
|
|
202
|
+
swaggerProperty = CreateSwaggerObjectSchema(propertyType);
|
|
203
|
+
break;
|
|
204
|
+
default: if (propertyType && typeof propertyType === "function") swaggerProperty.$ref = `#/components/schemas/${propertyType.name}`;
|
|
205
|
+
else swaggerProperty.type = propertyType?.name?.toLowerCase() || "string";
|
|
206
|
+
}
|
|
207
|
+
if (openApiMeta) {
|
|
208
|
+
swaggerProperty = {
|
|
209
|
+
...swaggerProperty,
|
|
210
|
+
...openApiMeta,
|
|
211
|
+
...extractOpenApiFields(openApiMeta)
|
|
212
|
+
};
|
|
213
|
+
if (openApiMeta.format === "binary") if (openApiMeta.isArray || propertyType === Array) swaggerProperty = {
|
|
214
|
+
type: "array",
|
|
215
|
+
items: {
|
|
216
|
+
type: "string",
|
|
217
|
+
format: "binary"
|
|
218
|
+
},
|
|
219
|
+
description: openApiMeta.description || "Array of files"
|
|
220
|
+
};
|
|
221
|
+
else swaggerProperty = {
|
|
222
|
+
type: "string",
|
|
223
|
+
format: "binary",
|
|
224
|
+
description: openApiMeta.description || "File upload"
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
schema.properties[propertyName] = swaggerProperty;
|
|
228
|
+
});
|
|
229
|
+
validationMetadata.forEach((meta) => {
|
|
230
|
+
const propertyName = meta.propertyName;
|
|
231
|
+
const property = schema.properties[propertyName];
|
|
232
|
+
if (!property) return;
|
|
233
|
+
switch (meta.name) {
|
|
234
|
+
case "isNotEmpty":
|
|
235
|
+
case "isDefined":
|
|
236
|
+
if (!schema.required.includes(propertyName)) schema.required.push(propertyName);
|
|
237
|
+
break;
|
|
238
|
+
case "isOptional":
|
|
239
|
+
schema.required = schema.required.filter((item) => item !== propertyName);
|
|
240
|
+
break;
|
|
241
|
+
case "minLength":
|
|
242
|
+
property.minLength = meta.constraints[0];
|
|
243
|
+
break;
|
|
244
|
+
case "maxLength":
|
|
245
|
+
property.maxLength = meta.constraints[0];
|
|
246
|
+
break;
|
|
247
|
+
case "min":
|
|
248
|
+
property.minimum = meta.constraints[0];
|
|
249
|
+
break;
|
|
250
|
+
case "max":
|
|
251
|
+
property.maximum = meta.constraints[0];
|
|
252
|
+
break;
|
|
253
|
+
case "isEmail":
|
|
254
|
+
property.format = "email";
|
|
255
|
+
break;
|
|
256
|
+
case "isDate":
|
|
257
|
+
property.format = "date-time";
|
|
258
|
+
break;
|
|
259
|
+
case "isIn":
|
|
260
|
+
property.enum = meta.constraints[0];
|
|
261
|
+
break;
|
|
262
|
+
case "isNumber":
|
|
263
|
+
property.type = "number";
|
|
264
|
+
break;
|
|
265
|
+
case "isInt":
|
|
266
|
+
property.type = "integer";
|
|
267
|
+
break;
|
|
268
|
+
case "isBoolean":
|
|
269
|
+
property.type = "boolean";
|
|
270
|
+
break;
|
|
271
|
+
case "isString":
|
|
272
|
+
property.type = "string";
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
if (schema.required.length === 0) delete schema.required;
|
|
277
|
+
return schema;
|
|
278
|
+
}
|
|
279
|
+
function generateClassSchema(classType) {
|
|
280
|
+
const schema = {
|
|
281
|
+
type: "object",
|
|
282
|
+
properties: {},
|
|
283
|
+
required: []
|
|
284
|
+
};
|
|
285
|
+
if (!classType || !classType.prototype) return schema;
|
|
286
|
+
const validationMetadata = (0, class_validator.getMetadataStorage)().getTargetValidationMetadatas(classType, "", true, false);
|
|
287
|
+
const prototype = classType.prototype;
|
|
288
|
+
const propertyKeys = new Set([...Object.getOwnPropertyNames(prototype), ...validationMetadata.map((m) => m.propertyName)]);
|
|
289
|
+
try {
|
|
290
|
+
const instance = new classType();
|
|
291
|
+
Reflect.ownKeys(instance).forEach((k) => {
|
|
292
|
+
if (typeof k === "string") propertyKeys.add(k);
|
|
293
|
+
});
|
|
294
|
+
} catch (_) {}
|
|
295
|
+
propertyKeys.forEach((propertyName) => {
|
|
296
|
+
if (!propertyName || propertyName === "constructor") return;
|
|
297
|
+
const openApiMeta = Reflect.getMetadata("property:openapi", prototype, propertyName);
|
|
298
|
+
if (openApiMeta?.exclude) return;
|
|
299
|
+
const propertyType = Reflect.getMetadata("design:type", prototype, propertyName);
|
|
300
|
+
let swaggerProperty = {};
|
|
301
|
+
switch (propertyType) {
|
|
302
|
+
case String:
|
|
303
|
+
swaggerProperty.type = "string";
|
|
304
|
+
break;
|
|
305
|
+
case Number:
|
|
306
|
+
swaggerProperty.type = "number";
|
|
307
|
+
break;
|
|
308
|
+
case Boolean:
|
|
309
|
+
swaggerProperty.type = "boolean";
|
|
310
|
+
break;
|
|
311
|
+
case Date:
|
|
312
|
+
swaggerProperty.type = "string";
|
|
313
|
+
swaggerProperty.format = "date-time";
|
|
314
|
+
break;
|
|
315
|
+
case Array:
|
|
316
|
+
swaggerProperty.type = "array";
|
|
317
|
+
swaggerProperty.items = { type: "string" };
|
|
318
|
+
break;
|
|
319
|
+
case Object:
|
|
320
|
+
swaggerProperty = generateClassSchema(propertyType);
|
|
321
|
+
break;
|
|
322
|
+
default: if (propertyType && typeof propertyType === "function") swaggerProperty.$ref = `#/components/schemas/${propertyType.name}`;
|
|
323
|
+
else swaggerProperty.type = propertyType?.name?.toLowerCase() || "string";
|
|
324
|
+
}
|
|
325
|
+
if (openApiMeta) {
|
|
326
|
+
const { required: _required, exclude: _exclude, isArray: _isArray, ...safeOpenApiMeta } = openApiMeta;
|
|
327
|
+
swaggerProperty = {
|
|
328
|
+
...swaggerProperty,
|
|
329
|
+
...extractOpenApiFields(safeOpenApiMeta)
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
schema.properties[propertyName] = swaggerProperty;
|
|
333
|
+
});
|
|
334
|
+
validationMetadata.forEach((meta) => {
|
|
335
|
+
const propertyName = meta.propertyName;
|
|
336
|
+
if (!schema.properties[propertyName]) schema.properties[propertyName] = { type: "string" };
|
|
337
|
+
switch (meta.name) {
|
|
338
|
+
case "isNotEmpty":
|
|
339
|
+
case "isDefined":
|
|
340
|
+
if (!schema.required.includes(propertyName)) schema.required.push(propertyName);
|
|
341
|
+
break;
|
|
342
|
+
case "isOptional":
|
|
343
|
+
schema.required = schema.required.filter((item) => item !== propertyName);
|
|
344
|
+
break;
|
|
345
|
+
case "minLength":
|
|
346
|
+
schema.properties[propertyName].minLength = meta.constraints[0];
|
|
347
|
+
break;
|
|
348
|
+
case "maxLength":
|
|
349
|
+
schema.properties[propertyName].maxLength = meta.constraints[0];
|
|
350
|
+
break;
|
|
351
|
+
case "min":
|
|
352
|
+
schema.properties[propertyName].minimum = meta.constraints[0];
|
|
353
|
+
break;
|
|
354
|
+
case "max":
|
|
355
|
+
schema.properties[propertyName].maximum = meta.constraints[0];
|
|
356
|
+
break;
|
|
357
|
+
case "isEmail":
|
|
358
|
+
schema.properties[propertyName].format = "email";
|
|
359
|
+
break;
|
|
360
|
+
case "isDate":
|
|
361
|
+
schema.properties[propertyName].format = "date-time";
|
|
362
|
+
break;
|
|
363
|
+
case "isIn":
|
|
364
|
+
schema.properties[propertyName].enum = meta.constraints[0];
|
|
365
|
+
break;
|
|
366
|
+
case "isNumber":
|
|
367
|
+
schema.properties[propertyName].type = "number";
|
|
368
|
+
break;
|
|
369
|
+
case "isInt":
|
|
370
|
+
schema.properties[propertyName].type = "integer";
|
|
371
|
+
break;
|
|
372
|
+
case "isBoolean":
|
|
373
|
+
schema.properties[propertyName].type = "boolean";
|
|
374
|
+
break;
|
|
375
|
+
case "isString":
|
|
376
|
+
schema.properties[propertyName].type = "string";
|
|
377
|
+
break;
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
if (schema.required.length === 0) delete schema.required;
|
|
381
|
+
return schema;
|
|
382
|
+
}
|
|
383
|
+
function generateSwaggerSchema(controllers) {
|
|
384
|
+
if (!Array.isArray(controllers)) return generateClassSchema(controllers);
|
|
385
|
+
const components = {};
|
|
386
|
+
for (const controller of controllers) {
|
|
387
|
+
if (!controller || typeof controller !== "function") continue;
|
|
388
|
+
if (!controller.prototype) continue;
|
|
389
|
+
if (Reflect.getMetadata(CONTROLLER_META_KEY, controller)) continue;
|
|
390
|
+
if (!Reflect.getMetadata("openapi:schema", controller)) continue;
|
|
391
|
+
components[controller.name] = generateClassSchema(controller);
|
|
392
|
+
}
|
|
393
|
+
return { components: Object.keys(components).length > 0 ? { schemas: components } : void 0 };
|
|
394
|
+
}
|
|
395
|
+
function OpenApiResponse$1(code = 200, model, description = "Successful response") {
|
|
396
|
+
let dataSchema;
|
|
397
|
+
if (typeof model === "function") dataSchema = generateClassSchema(model);
|
|
398
|
+
else if (model && typeof model === "object") dataSchema = inferSchemaFromExample(model);
|
|
399
|
+
else dataSchema = { type: "string" };
|
|
400
|
+
let message = "OK";
|
|
401
|
+
switch (code) {
|
|
402
|
+
case 400:
|
|
403
|
+
message = "Error";
|
|
404
|
+
description = "Error: Bad Request";
|
|
405
|
+
break;
|
|
406
|
+
case 401:
|
|
407
|
+
message = "Error";
|
|
408
|
+
description = "Error: Unauthorized";
|
|
409
|
+
break;
|
|
410
|
+
case 403:
|
|
411
|
+
message = "Error";
|
|
412
|
+
description = "Error: Forbidden";
|
|
413
|
+
break;
|
|
414
|
+
case 201:
|
|
415
|
+
message = "Created";
|
|
416
|
+
description = "Success: Created";
|
|
417
|
+
break;
|
|
418
|
+
case 500:
|
|
419
|
+
message = "Error";
|
|
420
|
+
description = "Error: InternalError";
|
|
421
|
+
break;
|
|
422
|
+
}
|
|
423
|
+
return {
|
|
424
|
+
description,
|
|
425
|
+
content: { "application/json": { schema: {
|
|
426
|
+
type: "object",
|
|
427
|
+
properties: {
|
|
428
|
+
code: {
|
|
429
|
+
type: "number",
|
|
430
|
+
example: code
|
|
431
|
+
},
|
|
432
|
+
status: {
|
|
433
|
+
type: "string",
|
|
434
|
+
example: message
|
|
435
|
+
},
|
|
436
|
+
data: dataSchema
|
|
437
|
+
}
|
|
438
|
+
} } }
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
function inferSchemaFromExample(obj) {
|
|
442
|
+
if (Array.isArray(obj)) return {
|
|
443
|
+
type: "array",
|
|
444
|
+
items: inferSchemaFromExample(obj[0] ?? {})
|
|
445
|
+
};
|
|
446
|
+
if (obj && typeof obj === "object") {
|
|
447
|
+
const properties = {};
|
|
448
|
+
for (const [key, value] of Object.entries(obj)) properties[key] = inferType(value);
|
|
449
|
+
return {
|
|
450
|
+
type: "object",
|
|
451
|
+
properties
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
return inferType(obj);
|
|
455
|
+
}
|
|
456
|
+
function inferType(value) {
|
|
457
|
+
switch (typeof value) {
|
|
458
|
+
case "string": return {
|
|
459
|
+
type: "string",
|
|
460
|
+
example: value
|
|
461
|
+
};
|
|
462
|
+
case "number": return {
|
|
463
|
+
type: "number",
|
|
464
|
+
example: value
|
|
465
|
+
};
|
|
466
|
+
case "boolean": return {
|
|
467
|
+
type: "boolean",
|
|
468
|
+
example: value
|
|
469
|
+
};
|
|
470
|
+
case "object":
|
|
471
|
+
if (Array.isArray(value)) return inferSchemaFromExample(value);
|
|
472
|
+
if (value === null) return { type: "null" };
|
|
473
|
+
return inferSchemaFromExample(value);
|
|
474
|
+
default: return { type: "string" };
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
//#endregion
|
|
478
|
+
//#region src/controller.ts
|
|
479
|
+
/**
|
|
480
|
+
* @copyright 2024
|
|
481
|
+
* @author Tareq Hossain
|
|
482
|
+
* @email xtrinsic96@gmail.com
|
|
483
|
+
* @url https://github.com/xtareq
|
|
484
|
+
*/
|
|
485
|
+
init_container();
|
|
486
|
+
const REQUEST_METADATA_KEY = Symbol("avleon:request");
|
|
487
|
+
function AvleonRequest() {
|
|
488
|
+
return (target, propertyKey, parameterIndex) => {
|
|
489
|
+
const existingParams = Reflect.getMetadata(REQUEST_METADATA_KEY, target, propertyKey) || [];
|
|
490
|
+
existingParams.push({
|
|
491
|
+
index: parameterIndex,
|
|
492
|
+
type: "request"
|
|
493
|
+
});
|
|
494
|
+
Reflect.defineMetadata(REQUEST_METADATA_KEY, existingParams, target, propertyKey);
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
function createControllerDecorator(type = "web") {
|
|
498
|
+
return function(pathOrOptions, maybeOptions) {
|
|
499
|
+
return function(target) {
|
|
500
|
+
let path = "/";
|
|
501
|
+
let options = {};
|
|
502
|
+
if (typeof pathOrOptions === "string") {
|
|
503
|
+
path = pathOrOptions;
|
|
504
|
+
options = maybeOptions || {};
|
|
505
|
+
} else if (typeof pathOrOptions === "object") {
|
|
506
|
+
options = pathOrOptions;
|
|
507
|
+
path = options.path || "/";
|
|
508
|
+
}
|
|
509
|
+
Reflect.defineMetadata(API_CONTROLLER_METADATA_KEY, true, target);
|
|
510
|
+
if (typeof typedi.Service === "function") {
|
|
511
|
+
registerController(target);
|
|
512
|
+
(0, typedi.Service)()(target);
|
|
513
|
+
Reflect.defineMetadata(CONTROLLER_META_KEY, {
|
|
514
|
+
type,
|
|
515
|
+
path,
|
|
516
|
+
options
|
|
517
|
+
}, target);
|
|
518
|
+
} else throw new Error("Service decorator is not a function");
|
|
519
|
+
};
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
function ApiController(pathOrOptions = "/", mayBeOptions) {
|
|
523
|
+
if (typeof pathOrOptions == "function") {
|
|
524
|
+
Reflect.defineMetadata(API_CONTROLLER_METADATA_KEY, true, pathOrOptions);
|
|
525
|
+
if (typeof typedi.Service === "function") {
|
|
526
|
+
registerController(pathOrOptions);
|
|
527
|
+
(0, typedi.Service)()(pathOrOptions);
|
|
528
|
+
Reflect.defineMetadata(CONTROLLER_META_KEY, {
|
|
529
|
+
type: "api",
|
|
530
|
+
path: "/",
|
|
531
|
+
options: {}
|
|
532
|
+
}, pathOrOptions);
|
|
533
|
+
} else throw new Error("Service decorator is not a function");
|
|
534
|
+
} else {
|
|
535
|
+
if (mayBeOptions) return createControllerDecorator("api")(pathOrOptions, mayBeOptions);
|
|
536
|
+
return createControllerDecorator("api")(pathOrOptions);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
//#endregion
|
|
540
|
+
//#region src/route-methods.ts
|
|
541
|
+
init_container();
|
|
542
|
+
/**
|
|
543
|
+
* Generic Route decorator factory
|
|
544
|
+
*/
|
|
545
|
+
function Route(method, pathOrOptions, maybeOptions) {
|
|
546
|
+
return function(target, propertyKey, descriptor) {
|
|
547
|
+
let path = "/";
|
|
548
|
+
let options = {};
|
|
549
|
+
if (typeof pathOrOptions === "string") {
|
|
550
|
+
path = pathOrOptions || "/";
|
|
551
|
+
options = maybeOptions || {};
|
|
552
|
+
} else if (typeof pathOrOptions === "object" && pathOrOptions !== null) {
|
|
553
|
+
options = pathOrOptions;
|
|
554
|
+
path = options.path || options.name || "/";
|
|
555
|
+
} else {
|
|
556
|
+
options = maybeOptions || {};
|
|
557
|
+
path = "/";
|
|
558
|
+
}
|
|
559
|
+
path = typeof path === "string" ? path : "/";
|
|
560
|
+
Reflect.defineMetadata("route:path", path, target, propertyKey);
|
|
561
|
+
Reflect.defineMetadata("route:method", method, target, propertyKey);
|
|
562
|
+
Reflect.defineMetadata(ROUTE_META_KEY, {
|
|
563
|
+
...options,
|
|
564
|
+
method,
|
|
565
|
+
path,
|
|
566
|
+
controller: target.constructor.name
|
|
567
|
+
}, target, propertyKey);
|
|
568
|
+
if (options) Reflect.defineMetadata("route:options", options, target, propertyKey);
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
const Get = (pathOrOptions, maybeOptions) => Route("GET", pathOrOptions, maybeOptions);
|
|
572
|
+
const Post = (pathOrOptions, maybeOptions) => Route("POST", pathOrOptions, maybeOptions);
|
|
573
|
+
const Put = (pathOrOptions, maybeOptions) => Route("PUT", pathOrOptions, maybeOptions);
|
|
574
|
+
const Delete = (pathOrOptions, maybeOptions) => Route("DELETE", pathOrOptions, maybeOptions);
|
|
575
|
+
const Patch = (pathOrOptions, maybeOptions) => Route("PATCH", pathOrOptions, maybeOptions);
|
|
576
|
+
const Options = (pathOrOptions, maybeOptions) => Route("OPTIONS", pathOrOptions, maybeOptions);
|
|
577
|
+
const All = (pathOrOptions, maybeOptions) => Route("ALL", pathOrOptions, maybeOptions);
|
|
578
|
+
//#endregion
|
|
579
|
+
//#region src/openapi.ts
|
|
580
|
+
/**
|
|
581
|
+
* Normalize a flat ParamsSchemaMap into a valid JSON Schema object
|
|
582
|
+
* that Fastify/AJV can validate against.
|
|
583
|
+
*
|
|
584
|
+
* Input: { id: { type: "string", example: "abc-123" } }
|
|
585
|
+
* Output: { type: "object", properties: { id: { type: "string", example: "abc-123" } } }
|
|
586
|
+
*/
|
|
587
|
+
function normalizeParamsToJsonSchema(params, requiredKeys = []) {
|
|
588
|
+
const properties = {};
|
|
589
|
+
for (const [key, val] of Object.entries(params)) properties[key] = {
|
|
590
|
+
type: "string",
|
|
591
|
+
...val
|
|
592
|
+
};
|
|
593
|
+
const schema = {
|
|
594
|
+
type: "object",
|
|
595
|
+
properties
|
|
596
|
+
};
|
|
597
|
+
if (requiredKeys.length > 0) schema.required = requiredKeys;
|
|
598
|
+
return schema;
|
|
599
|
+
}
|
|
600
|
+
function OpenApiSchema() {
|
|
601
|
+
return function(target) {
|
|
602
|
+
Reflect.defineMetadata("openapi:schema", true, target);
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
function OpenApi(options) {
|
|
606
|
+
return function(target, propertyKey, descriptor) {
|
|
607
|
+
if (typeof target === "function" && !propertyKey) Reflect.defineMetadata("controller:openapi", options, target);
|
|
608
|
+
else if (descriptor) Reflect.defineMetadata("route:openapi", options, target, propertyKey);
|
|
609
|
+
else if (propertyKey) Reflect.defineMetadata("property:openapi", options, target, propertyKey);
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
//#endregion
|
|
613
|
+
//#region src/utils/common-utils.ts
|
|
614
|
+
/**
|
|
615
|
+
* @copyright 2024
|
|
616
|
+
* @author Tareq Hossain
|
|
617
|
+
* @email xtrinsic96@gmail.com
|
|
618
|
+
* @url https://github.com/xtareq
|
|
619
|
+
*/
|
|
620
|
+
function isConstructor(func) {
|
|
621
|
+
if (typeof func !== "function") return false;
|
|
622
|
+
if (func === Function.prototype.bind || func instanceof RegExp) return false;
|
|
623
|
+
if (func.prototype && typeof func.prototype === "object") return true;
|
|
624
|
+
try {
|
|
625
|
+
return typeof new func() === "object";
|
|
626
|
+
} catch (e) {
|
|
627
|
+
return false;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
function formatUrl(path) {
|
|
631
|
+
if (typeof path !== "string") throw new Error("The path must be a string");
|
|
632
|
+
path = path.trim();
|
|
633
|
+
if (!path.startsWith("/")) path = "/" + path;
|
|
634
|
+
path = path.replace(/\/\/+/g, "/");
|
|
635
|
+
if (path.endsWith("/")) path = path.slice(0, -1);
|
|
636
|
+
return path;
|
|
637
|
+
}
|
|
638
|
+
function parsedPath(ipath) {
|
|
639
|
+
return !ipath.startsWith("/") ? "/" + ipath : ipath;
|
|
640
|
+
}
|
|
641
|
+
function normalizePath(base = "/", subPath = "/") {
|
|
642
|
+
return `/${base}/${subPath}`.replace(/\/+/g, "/").replace(/\/$/, "");
|
|
643
|
+
}
|
|
644
|
+
function extrctParamFromUrl(url) {
|
|
645
|
+
return url.split("/").filter((x) => x.startsWith(":") || x.startsWith("?:")).map((f) => ({
|
|
646
|
+
key: f.replace(/(\?|:)/g, ""),
|
|
647
|
+
required: !f.startsWith("?:")
|
|
648
|
+
}));
|
|
649
|
+
}
|
|
650
|
+
function findDuplicates(arr) {
|
|
651
|
+
const seen = /* @__PURE__ */ new Set();
|
|
652
|
+
const duplicates = /* @__PURE__ */ new Set();
|
|
653
|
+
for (const str of arr) if (seen.has(str)) duplicates.add(str);
|
|
654
|
+
else seen.add(str);
|
|
655
|
+
return Array.from(duplicates);
|
|
656
|
+
}
|
|
657
|
+
function sleep(ms) {
|
|
658
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
659
|
+
}
|
|
660
|
+
var uuid, getLineNumber;
|
|
661
|
+
var init_common_utils = require_chunk.__esmMin((() => {
|
|
662
|
+
uuid = node_crypto.default.randomUUID();
|
|
663
|
+
getLineNumber = (filePath, rpath) => {
|
|
664
|
+
let numbers = [];
|
|
665
|
+
try {
|
|
666
|
+
const lines = node_fs.default.readFileSync(filePath, "utf8").split("\n");
|
|
667
|
+
for (let i = 0; i < lines.length; i++) {
|
|
668
|
+
const match = lines[i].match(rpath);
|
|
669
|
+
if (match) {
|
|
670
|
+
console.log(match);
|
|
671
|
+
numbers.push({
|
|
672
|
+
line: i + 1,
|
|
673
|
+
column: match.index ?? 0
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
return numbers;
|
|
678
|
+
} catch (error) {
|
|
679
|
+
return numbers;
|
|
680
|
+
}
|
|
681
|
+
};
|
|
682
|
+
}));
|
|
683
|
+
//#endregion
|
|
684
|
+
//#region src/utils/object-utils.ts
|
|
685
|
+
/**
|
|
686
|
+
* @copyright 2024
|
|
687
|
+
* @author Tareq Hossain
|
|
688
|
+
* @email xtrinsic96@gmail.com
|
|
689
|
+
* @url https://github.com/xtareq
|
|
690
|
+
*/
|
|
691
|
+
function pick(obj, paths) {
|
|
692
|
+
const result = {};
|
|
693
|
+
for (const path of paths) {
|
|
694
|
+
const keys = path.split(".");
|
|
695
|
+
let source = obj;
|
|
696
|
+
let target = result;
|
|
697
|
+
for (let i = 0; i < keys.length; i++) {
|
|
698
|
+
const key = keys[i];
|
|
699
|
+
if (!(key in source)) break;
|
|
700
|
+
if (i === keys.length - 1) target[key] = source[key];
|
|
701
|
+
else {
|
|
702
|
+
source = source[key];
|
|
703
|
+
target[key] = target[key] || {};
|
|
704
|
+
target = target[key];
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
return result;
|
|
709
|
+
}
|
|
710
|
+
function exclude(obj, paths) {
|
|
711
|
+
if (Array.isArray(obj)) return obj.map((item) => exclude(item, paths));
|
|
712
|
+
const clone = structuredClone(obj);
|
|
713
|
+
for (const path of paths) {
|
|
714
|
+
const keys = path.split(".");
|
|
715
|
+
let target = clone;
|
|
716
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
717
|
+
if (!(keys[i] in target)) break;
|
|
718
|
+
target = target[keys[i]];
|
|
719
|
+
}
|
|
720
|
+
delete target?.[keys[keys.length - 1]];
|
|
721
|
+
}
|
|
722
|
+
return clone;
|
|
723
|
+
}
|
|
724
|
+
function autoCast(value, typeHint, schema) {
|
|
725
|
+
if (value === null || value === void 0) return value;
|
|
726
|
+
if (Array.isArray(value)) {
|
|
727
|
+
const elementType = Array.isArray(typeHint) ? typeHint[0] : void 0;
|
|
728
|
+
return value.map((v) => autoCast(v, elementType));
|
|
729
|
+
}
|
|
730
|
+
if (typeof value === "object" && !(value instanceof Date)) {
|
|
731
|
+
const result = {};
|
|
732
|
+
for (const [key, val] of Object.entries(value)) {
|
|
733
|
+
let fieldType = void 0;
|
|
734
|
+
if (schema?.properties?.[key]?.type) {
|
|
735
|
+
const t = schema.properties[key].type;
|
|
736
|
+
fieldType = t === "integer" || t === "number" ? Number : t === "boolean" ? Boolean : t === "array" ? Array : t === "object" ? Object : String;
|
|
737
|
+
}
|
|
738
|
+
result[key] = autoCast(val, fieldType);
|
|
739
|
+
}
|
|
740
|
+
return result;
|
|
741
|
+
}
|
|
742
|
+
if (typeof value !== "string") return value;
|
|
743
|
+
const trimmed = value.trim();
|
|
744
|
+
if (typeHint === Boolean || trimmed.toLowerCase() === "true") return true;
|
|
745
|
+
if (trimmed.toLowerCase() === "false") return false;
|
|
746
|
+
if (typeHint === Number || !isNaN(Number(trimmed)) && trimmed !== "") {
|
|
747
|
+
const n = Number(trimmed);
|
|
748
|
+
if (!isNaN(n)) return n;
|
|
749
|
+
}
|
|
750
|
+
if (trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]")) try {
|
|
751
|
+
return autoCast(JSON.parse(trimmed), typeHint, schema);
|
|
752
|
+
} catch {
|
|
753
|
+
return trimmed;
|
|
754
|
+
}
|
|
755
|
+
if (typeHint === Date || /^\d{4}-\d{2}-\d{2}([Tt]\d{2}:\d{2})?/.test(trimmed)) {
|
|
756
|
+
const d = new Date(trimmed);
|
|
757
|
+
if (!isNaN(d.getTime())) return d;
|
|
758
|
+
}
|
|
759
|
+
return trimmed;
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* Deeply normalizes query strings into nested JS objects.
|
|
763
|
+
*/
|
|
764
|
+
function normalizeQueryDeep(query) {
|
|
765
|
+
const result = {};
|
|
766
|
+
const setDeep = (obj, path, value) => {
|
|
767
|
+
let current = obj;
|
|
768
|
+
for (let i = 0; i < path.length; i++) {
|
|
769
|
+
const key = path[i];
|
|
770
|
+
const nextKey = path[i + 1];
|
|
771
|
+
if (i === path.length - 1) if (key === "") {
|
|
772
|
+
if (!Array.isArray(current)) current = [];
|
|
773
|
+
current.push(value);
|
|
774
|
+
} else if (Array.isArray(current[key])) current[key].push(value);
|
|
775
|
+
else if (current[key] !== void 0) current[key] = [current[key], value];
|
|
776
|
+
else current[key] = value;
|
|
777
|
+
else {
|
|
778
|
+
if (!current[key]) current[key] = nextKey === "" || /^\d+$/.test(nextKey) ? [] : {};
|
|
779
|
+
current = current[key];
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
};
|
|
783
|
+
for (const [rawKey, rawValue] of Object.entries(query)) {
|
|
784
|
+
const path = [];
|
|
785
|
+
const regex = /([^\[\]]+)|(\[\])/g;
|
|
786
|
+
let match;
|
|
787
|
+
while ((match = regex.exec(rawKey)) !== null) if (match[1]) path.push(match[1]);
|
|
788
|
+
else if (match[2]) path.push("");
|
|
789
|
+
if (path.length === 0) if (result[rawKey]) if (Array.isArray(result[rawKey])) result[rawKey].push(rawValue);
|
|
790
|
+
else result[rawKey] = [result[rawKey], rawValue];
|
|
791
|
+
else result[rawKey] = rawValue;
|
|
792
|
+
else setDeep(result, path, rawValue);
|
|
793
|
+
}
|
|
794
|
+
return result;
|
|
795
|
+
}
|
|
796
|
+
function transformObjectByInstanceToObject(instance, value) {
|
|
797
|
+
return (0, class_transformer.instanceToPlain)((0, class_transformer.plainToInstance)(instance, value), {
|
|
798
|
+
excludeExtraneousValues: true,
|
|
799
|
+
exposeUnsetFields: true
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
function jsonToJs(value) {
|
|
803
|
+
try {
|
|
804
|
+
return JSON.parse(value);
|
|
805
|
+
} catch (err) {
|
|
806
|
+
return false;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
function jsonToInstance(value, instance) {
|
|
810
|
+
try {
|
|
811
|
+
return (0, class_transformer.plainToInstance)(instance, JSON.parse(value));
|
|
812
|
+
} catch (err) {
|
|
813
|
+
return false;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
var init_object_utils = require_chunk.__esmMin((() => {}));
|
|
817
|
+
//#endregion
|
|
818
|
+
//#region src/exceptions/http-exceptions.ts
|
|
819
|
+
var BaseHttpException, BadRequestException, ValidationErrorException, InternalErrorException, NotFoundException, UnauthorizedException, ForbiddenException, HttpExceptions;
|
|
820
|
+
var init_http_exceptions = require_chunk.__esmMin((() => {
|
|
821
|
+
BaseHttpException = class extends Error {
|
|
822
|
+
code = 500;
|
|
823
|
+
name = "HttpException";
|
|
824
|
+
payload;
|
|
825
|
+
constructor(message) {
|
|
826
|
+
const stringMessage = typeof message === "string" ? message : JSON.stringify(message);
|
|
827
|
+
super(stringMessage);
|
|
828
|
+
this.payload = typeof message === "string" ? { message } : message;
|
|
829
|
+
}
|
|
830
|
+
isCustomException() {
|
|
831
|
+
return true;
|
|
832
|
+
}
|
|
833
|
+
};
|
|
834
|
+
BadRequestException = class extends BaseHttpException {
|
|
835
|
+
name = "BadRequest";
|
|
836
|
+
code = 400;
|
|
837
|
+
constructor(message) {
|
|
838
|
+
super(message);
|
|
839
|
+
}
|
|
840
|
+
};
|
|
841
|
+
ValidationErrorException = class extends BadRequestException {
|
|
842
|
+
name = "ValidationError";
|
|
843
|
+
};
|
|
844
|
+
InternalErrorException = class extends BaseHttpException {
|
|
845
|
+
name = "InternalError";
|
|
846
|
+
code = 500;
|
|
847
|
+
constructor(message = "Something going wrong") {
|
|
848
|
+
super(message);
|
|
849
|
+
}
|
|
850
|
+
};
|
|
851
|
+
NotFoundException = class extends BaseHttpException {
|
|
852
|
+
name = "NotFound";
|
|
853
|
+
code = 404;
|
|
854
|
+
constructor(message) {
|
|
855
|
+
super(message);
|
|
856
|
+
}
|
|
857
|
+
};
|
|
858
|
+
UnauthorizedException = class extends BaseHttpException {
|
|
859
|
+
name = "Unauthorized";
|
|
860
|
+
code = 401;
|
|
861
|
+
constructor(message) {
|
|
862
|
+
super(message);
|
|
863
|
+
}
|
|
864
|
+
};
|
|
865
|
+
ForbiddenException = class extends BaseHttpException {
|
|
866
|
+
name = "Forbidden";
|
|
867
|
+
code = 403;
|
|
868
|
+
constructor(message) {
|
|
869
|
+
super(message);
|
|
870
|
+
}
|
|
871
|
+
};
|
|
872
|
+
HttpExceptions = {
|
|
873
|
+
notFound: (message = "") => new NotFoundException(message),
|
|
874
|
+
validationError: (message = "") => new ValidationErrorException(message),
|
|
875
|
+
badRequest: (message = "") => new BadRequestException(message),
|
|
876
|
+
unauthorized: (message = "") => new UnauthorizedException(message),
|
|
877
|
+
forbidden: (message = "") => new ForbiddenException(message),
|
|
878
|
+
internalError: (message = "") => new InternalErrorException(message)
|
|
879
|
+
};
|
|
880
|
+
}));
|
|
881
|
+
//#endregion
|
|
882
|
+
//#region src/exceptions/index.ts
|
|
883
|
+
var init_exceptions = require_chunk.__esmMin((() => {
|
|
884
|
+
init_http_exceptions();
|
|
885
|
+
}));
|
|
886
|
+
//#endregion
|
|
887
|
+
//#region src/utils/validation-utils.ts
|
|
888
|
+
/**
|
|
889
|
+
* @copyright 2024
|
|
890
|
+
* @author Tareq Hossain
|
|
891
|
+
* @email xtrinsic96@gmail.com
|
|
892
|
+
* @url https://github.com/xtareq
|
|
893
|
+
*/
|
|
894
|
+
function getDataType(expectedType) {
|
|
895
|
+
switch (expectedType.name) {
|
|
896
|
+
case "Object":
|
|
897
|
+
if (Array.isArray(expectedType)) return "array";
|
|
898
|
+
return "object";
|
|
899
|
+
case "String": return "string";
|
|
900
|
+
case "Number": return "number";
|
|
901
|
+
case "Boolean": return "boolean";
|
|
902
|
+
default: return expectedType;
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
function isValidType(value, expectedType) {
|
|
906
|
+
if (value === void 0 || value === null) return true;
|
|
907
|
+
switch (expectedType.name) {
|
|
908
|
+
case "String": return typeof value === "string";
|
|
909
|
+
case "Number": return typeof value === "number" || !isNaN(Number(value));
|
|
910
|
+
case "Boolean": return typeof value === "boolean";
|
|
911
|
+
default: return value instanceof expectedType;
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
function isValidJsonString(value) {
|
|
915
|
+
try {
|
|
916
|
+
return JSON.parse(value);
|
|
917
|
+
} catch (err) {
|
|
918
|
+
return false;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
async function validateObjectByInstance(target, value = {}, options = "array") {
|
|
922
|
+
try {
|
|
923
|
+
const { validateOrReject } = require("class-validator");
|
|
924
|
+
const { plainToInstance } = require("class-transformer");
|
|
925
|
+
await validateOrReject(plainToInstance(target, value));
|
|
926
|
+
} catch (error) {
|
|
927
|
+
if (typeof error == "object" && Array.isArray(error)) return options == "object" ? error.reduce((acc, x) => {
|
|
928
|
+
acc[x.property] = x.constraints;
|
|
929
|
+
return acc;
|
|
930
|
+
}, {}) : error.map((x) => ({
|
|
931
|
+
path: x.property,
|
|
932
|
+
constraints: x.constraints
|
|
933
|
+
}));
|
|
934
|
+
else throw new InternalErrorException("Can't validate object");
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
function validateRequestBody(target, value, options = "array") {
|
|
938
|
+
if (!isClassValidatorClass(target)) return {
|
|
939
|
+
count: 0,
|
|
940
|
+
errors: {}
|
|
941
|
+
};
|
|
942
|
+
const error = (0, class_validator.validateSync)((0, class_transformer.plainToInstance)(target, value ? value : {}));
|
|
943
|
+
const errors = options == "object" ? error.reduce((acc, x) => {
|
|
944
|
+
acc[x.property] = x.constraints;
|
|
945
|
+
return acc;
|
|
946
|
+
}, {}) : error.map((x) => ({
|
|
947
|
+
path: x.property,
|
|
948
|
+
constraints: x.constraints
|
|
949
|
+
}));
|
|
950
|
+
return {
|
|
951
|
+
count: error.length,
|
|
952
|
+
errors
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
var isClassValidator, isClassValidatorClass;
|
|
956
|
+
var init_validation_utils = require_chunk.__esmMin((() => {
|
|
957
|
+
init_exceptions();
|
|
958
|
+
isClassValidator = (target) => {
|
|
959
|
+
try {
|
|
960
|
+
require("class-validator");
|
|
961
|
+
return (0, class_validator.getMetadataStorage)().getTargetValidationMetadatas(target, "", false, false).length > 0;
|
|
962
|
+
} catch (err) {
|
|
963
|
+
console.log(err);
|
|
964
|
+
return false;
|
|
965
|
+
}
|
|
966
|
+
};
|
|
967
|
+
isClassValidatorClass = (target) => {
|
|
968
|
+
try {
|
|
969
|
+
return require("class-validator").getMetadataStorage().getTargetValidationMetadatas(target, void 0, false, false).length > 0;
|
|
970
|
+
} catch (err) {
|
|
971
|
+
return false;
|
|
972
|
+
}
|
|
973
|
+
};
|
|
974
|
+
}));
|
|
975
|
+
//#endregion
|
|
976
|
+
//#region src/exceptions/system-exception.ts
|
|
977
|
+
var SystemUseError, EnvironmentVariableNotFound;
|
|
978
|
+
var init_system_exception = require_chunk.__esmMin((() => {
|
|
979
|
+
SystemUseError = class extends Error {
|
|
980
|
+
constructor(message) {
|
|
981
|
+
super(message);
|
|
982
|
+
}
|
|
983
|
+
};
|
|
984
|
+
EnvironmentVariableNotFound = class extends Error {
|
|
985
|
+
constructor(key) {
|
|
986
|
+
super(`${key} not found in environment variables.`);
|
|
987
|
+
}
|
|
988
|
+
};
|
|
989
|
+
}));
|
|
990
|
+
//#endregion
|
|
991
|
+
//#region src/utils/di-utils.ts
|
|
992
|
+
function inject(cls) {
|
|
993
|
+
try {
|
|
994
|
+
return container_default.get(cls);
|
|
995
|
+
} catch (error) {
|
|
996
|
+
throw new SystemUseError("Not a project class. Maybe you wanna register it first.");
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
var init_di_utils = require_chunk.__esmMin((() => {
|
|
1000
|
+
init_container();
|
|
1001
|
+
init_system_exception();
|
|
1002
|
+
}));
|
|
1003
|
+
//#endregion
|
|
1004
|
+
//#region src/validation.ts
|
|
1005
|
+
function validateOrThrow(obj, rules, options) {
|
|
1006
|
+
const errors = new Validator(rules, options).validate(obj);
|
|
1007
|
+
if (errors[0].length > 0) throw new BadRequestException(errors[0]);
|
|
1008
|
+
return errors[1];
|
|
1009
|
+
}
|
|
1010
|
+
var PValidationRule, Validator, isBool, parseBoolean;
|
|
1011
|
+
var init_validation = require_chunk.__esmMin((() => {
|
|
1012
|
+
init_exceptions();
|
|
1013
|
+
PValidationRule = class {
|
|
1014
|
+
name;
|
|
1015
|
+
type;
|
|
1016
|
+
message;
|
|
1017
|
+
constructor(name, type, message) {
|
|
1018
|
+
this.name = name;
|
|
1019
|
+
this.type = type;
|
|
1020
|
+
this.message = message;
|
|
1021
|
+
}
|
|
1022
|
+
};
|
|
1023
|
+
Validator = class {
|
|
1024
|
+
rules = [];
|
|
1025
|
+
options = {};
|
|
1026
|
+
constructor(obj, options) {
|
|
1027
|
+
this.init(obj);
|
|
1028
|
+
if (options) this.options = options;
|
|
1029
|
+
}
|
|
1030
|
+
init(obj) {
|
|
1031
|
+
Object.keys(obj).forEach((key) => {
|
|
1032
|
+
const rule = obj[key];
|
|
1033
|
+
this.rules.push(new PValidationRule(key, rule.type, rule.message));
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
validate(obj, options) {
|
|
1037
|
+
const erors = [];
|
|
1038
|
+
this.rules.forEach((k) => {
|
|
1039
|
+
const r = Object.keys(obj).find((key) => key == k.name);
|
|
1040
|
+
let messages = [];
|
|
1041
|
+
if (!r || obj[r] == void 0 || obj[r] == "") messages.push({
|
|
1042
|
+
constraint: "required",
|
|
1043
|
+
message: k.name + " is required"
|
|
1044
|
+
});
|
|
1045
|
+
if (k.type == "string" && typeof obj[k.name] != "string") messages.push({
|
|
1046
|
+
constraint: "type",
|
|
1047
|
+
message: `${k.name} must be type ${k.type}`
|
|
1048
|
+
});
|
|
1049
|
+
if (k.type == "number" && !parseInt(obj[k.name])) messages.push({
|
|
1050
|
+
constraint: "type",
|
|
1051
|
+
message: `${k.name} must be type ${k.type}`
|
|
1052
|
+
});
|
|
1053
|
+
if (k.type == "number") obj[k.name] = parseInt(obj[k.name]);
|
|
1054
|
+
if (k.type == "boolean" && !isBool(obj[k.name])) messages.push({
|
|
1055
|
+
constraint: "type",
|
|
1056
|
+
message: `${k.name} must be type ${k.type}`
|
|
1057
|
+
});
|
|
1058
|
+
if (k.type == "boolean") obj[k.name] = parseBoolean(obj[k.name]);
|
|
1059
|
+
if (messages.length > 0) erors.push({
|
|
1060
|
+
path: k.name,
|
|
1061
|
+
...this.options.location ? { location: this.options.location } : {},
|
|
1062
|
+
constraints: messages
|
|
1063
|
+
});
|
|
1064
|
+
});
|
|
1065
|
+
return [erors, obj];
|
|
1066
|
+
}
|
|
1067
|
+
};
|
|
1068
|
+
isBool = (val) => {
|
|
1069
|
+
if (typeof val == "boolean") return true;
|
|
1070
|
+
if (parseInt(val) == 0 || parseInt(val) == 1) return true;
|
|
1071
|
+
if (val == "true" || val == "false") return true;
|
|
1072
|
+
return false;
|
|
1073
|
+
};
|
|
1074
|
+
parseBoolean = (val) => {
|
|
1075
|
+
if (typeof val === "boolean") return val;
|
|
1076
|
+
if (parseInt(val) == 1) return true;
|
|
1077
|
+
if (typeof val === "string") return val.trim().toLowerCase() === "true";
|
|
1078
|
+
return false;
|
|
1079
|
+
};
|
|
1080
|
+
}));
|
|
1081
|
+
//#endregion
|
|
1082
|
+
//#region src/helpers.ts
|
|
1083
|
+
var init_helpers = require_chunk.__esmMin((() => {
|
|
1084
|
+
init_common_utils();
|
|
1085
|
+
init_object_utils();
|
|
1086
|
+
init_validation_utils();
|
|
1087
|
+
init_di_utils();
|
|
1088
|
+
init_validation();
|
|
1089
|
+
}));
|
|
1090
|
+
//#endregion
|
|
1091
|
+
//#region src/params.ts
|
|
1092
|
+
init_container();
|
|
1093
|
+
init_helpers();
|
|
1094
|
+
function createParamDecorator(type) {
|
|
1095
|
+
return function(key, options = {}) {
|
|
1096
|
+
return function(target, propertyKey, parameterIndex) {
|
|
1097
|
+
let metaKey;
|
|
1098
|
+
switch (type) {
|
|
1099
|
+
case "route:param":
|
|
1100
|
+
metaKey = PARAM_META_KEY;
|
|
1101
|
+
break;
|
|
1102
|
+
case "route:query":
|
|
1103
|
+
metaKey = QUERY_META_KEY;
|
|
1104
|
+
break;
|
|
1105
|
+
case "route:body":
|
|
1106
|
+
metaKey = REQUEST_BODY_META_KEY;
|
|
1107
|
+
break;
|
|
1108
|
+
case "route:user":
|
|
1109
|
+
metaKey = REQUEST_USER_META_KEY;
|
|
1110
|
+
break;
|
|
1111
|
+
case "route:header":
|
|
1112
|
+
metaKey = REQUEST_HEADER_META_KEY;
|
|
1113
|
+
break;
|
|
1114
|
+
default: throw new Error(`Unknown param decorator type: ${String(type)}`);
|
|
1115
|
+
}
|
|
1116
|
+
const existingParams = Reflect.getMetadata(metaKey, target, propertyKey) || [];
|
|
1117
|
+
const paramNames = target[propertyKey].toString().match(/\(([^)]*)\)/)?.[1]?.split(",").map((n) => n.trim()) || [];
|
|
1118
|
+
const paramDataType = (Reflect.getMetadata("design:paramtypes", target, propertyKey) || [])[parameterIndex];
|
|
1119
|
+
existingParams.push({
|
|
1120
|
+
index: parameterIndex,
|
|
1121
|
+
key: typeof key === "string" ? key : "all",
|
|
1122
|
+
name: paramNames[parameterIndex],
|
|
1123
|
+
required: options.required ?? true,
|
|
1124
|
+
validate: options.validate ?? true,
|
|
1125
|
+
dataType: getDataType(paramDataType),
|
|
1126
|
+
validatorClass: isClassValidatorClass(paramDataType),
|
|
1127
|
+
schema: isClassValidatorClass(paramDataType) ? generateClassSchema(paramDataType) : null,
|
|
1128
|
+
type
|
|
1129
|
+
});
|
|
1130
|
+
Reflect.defineMetadata(metaKey, existingParams, target, propertyKey);
|
|
1131
|
+
};
|
|
1132
|
+
};
|
|
1133
|
+
}
|
|
1134
|
+
const Param = createParamDecorator("route:param");
|
|
1135
|
+
const Query = createParamDecorator("route:query");
|
|
1136
|
+
const Body = createParamDecorator("route:body");
|
|
1137
|
+
const Header = createParamDecorator("route:header");
|
|
1138
|
+
const AuthUser = createParamDecorator("route:user");
|
|
1139
|
+
//#endregion
|
|
1140
|
+
//#region src/decorators.ts
|
|
1141
|
+
/**
|
|
1142
|
+
* @copyright 2024
|
|
1143
|
+
* @author Tareq Hossain
|
|
1144
|
+
* @email xtrinsic96@gmail.com
|
|
1145
|
+
* @url https://github.com/xtareq
|
|
1146
|
+
*/
|
|
1147
|
+
function AppService(target) {
|
|
1148
|
+
if (target) (0, typedi.Service)()(target);
|
|
1149
|
+
else return function(tg) {
|
|
1150
|
+
(0, typedi.Service)()(tg);
|
|
1151
|
+
};
|
|
1152
|
+
}
|
|
1153
|
+
const Utility = typedi.Service;
|
|
1154
|
+
const Helper = typedi.Service;
|
|
1155
|
+
//#endregion
|
|
1156
|
+
//#region src/core/router.ts
|
|
1157
|
+
init_helpers();
|
|
1158
|
+
init_container();
|
|
1159
|
+
init_exceptions();
|
|
1160
|
+
/**
|
|
1161
|
+
* Normalize a flat params/query map like:
|
|
1162
|
+
* { id: { type: "string", example: "abc-123" } }
|
|
1163
|
+
* into a valid AJV/JSON Schema object:
|
|
1164
|
+
* { type: "object", properties: { id: { type: "string", example: "abc-123" } } }
|
|
1165
|
+
*/
|
|
1166
|
+
function normalizeParamsToJsonSchema$1(map, requiredKeys = []) {
|
|
1167
|
+
const properties = {};
|
|
1168
|
+
for (const [key, val] of Object.entries(map)) {
|
|
1169
|
+
const { required, ...rest } = val;
|
|
1170
|
+
properties[key] = {
|
|
1171
|
+
type: "string",
|
|
1172
|
+
...rest
|
|
1173
|
+
};
|
|
1174
|
+
}
|
|
1175
|
+
const schema = {
|
|
1176
|
+
type: "object",
|
|
1177
|
+
properties
|
|
1178
|
+
};
|
|
1179
|
+
if (requiredKeys.length > 0) schema.required = requiredKeys;
|
|
1180
|
+
return schema;
|
|
1181
|
+
}
|
|
1182
|
+
/**
|
|
1183
|
+
* Take a raw @OpenApi schema object and return a Fastify-compatible
|
|
1184
|
+
* route schema — normalizing params, query → querystring, and headers.
|
|
1185
|
+
*/
|
|
1186
|
+
function buildRouteSchema(raw) {
|
|
1187
|
+
const schema = { ...raw };
|
|
1188
|
+
if (raw.params && typeof raw.params === "object" && !raw.params.type) {
|
|
1189
|
+
const required = Object.entries(raw.params).filter(([, v]) => v.required !== false).map(([k]) => k);
|
|
1190
|
+
schema.params = normalizeParamsToJsonSchema$1(raw.params, required);
|
|
1191
|
+
}
|
|
1192
|
+
if (raw.query && typeof raw.query === "object" && !raw.query.type) {
|
|
1193
|
+
const required = Object.entries(raw.query).filter(([, v]) => v.required === true).map(([k]) => k);
|
|
1194
|
+
schema.querystring = normalizeParamsToJsonSchema$1(raw.query, required);
|
|
1195
|
+
delete schema.query;
|
|
1196
|
+
}
|
|
1197
|
+
if (raw.headers && typeof raw.headers === "object" && !raw.headers.type) schema.headers = normalizeParamsToJsonSchema$1(raw.headers);
|
|
1198
|
+
return schema;
|
|
1199
|
+
}
|
|
1200
|
+
var AvleonRouter = class {
|
|
1201
|
+
routeSet = /* @__PURE__ */ new Set();
|
|
1202
|
+
metaCache = /* @__PURE__ */ new Map();
|
|
1203
|
+
middlewares = /* @__PURE__ */ new Map();
|
|
1204
|
+
rMap = /* @__PURE__ */ new Map();
|
|
1205
|
+
app;
|
|
1206
|
+
authorizeMiddleware;
|
|
1207
|
+
constructor(app) {
|
|
1208
|
+
this.app = app;
|
|
1209
|
+
}
|
|
1210
|
+
setAuthorizeMiddleware(middleware) {
|
|
1211
|
+
this.authorizeMiddleware = middleware;
|
|
1212
|
+
}
|
|
1213
|
+
registerMiddleware(name, instance) {
|
|
1214
|
+
this.middlewares.set(name, instance);
|
|
1215
|
+
}
|
|
1216
|
+
_processMeta(prototype, method) {
|
|
1217
|
+
const cacheKey = `${prototype.constructor.name}_${method}`;
|
|
1218
|
+
if (this.metaCache.has(cacheKey)) return this.metaCache.get(cacheKey);
|
|
1219
|
+
const meta = {
|
|
1220
|
+
request: Reflect.getMetadata(REQUEST_METADATA_KEY, prototype, method) || [],
|
|
1221
|
+
params: Reflect.getMetadata(PARAM_META_KEY, prototype, method) || [],
|
|
1222
|
+
query: Reflect.getMetadata(QUERY_META_KEY, prototype, method) || [],
|
|
1223
|
+
body: Reflect.getMetadata(REQUEST_BODY_META_KEY, prototype, method) || [],
|
|
1224
|
+
file: Reflect.getMetadata(REQUEST_BODY_FILE_KEY, prototype, method) || [],
|
|
1225
|
+
files: Reflect.getMetadata(REQUEST_BODY_FILES_KEY, prototype, method) || [],
|
|
1226
|
+
headers: Reflect.getMetadata(REQUEST_HEADER_META_KEY, prototype, method) || [],
|
|
1227
|
+
currentUser: Reflect.getMetadata(REQUEST_USER_META_KEY, prototype, method) || []
|
|
1228
|
+
};
|
|
1229
|
+
this.metaCache.set(cacheKey, meta);
|
|
1230
|
+
return meta;
|
|
1231
|
+
}
|
|
1232
|
+
executeMiddlewares(target, propertyKey) {
|
|
1233
|
+
const classMiddlewares = Reflect.getMetadata("controller:middleware", target.constructor) || [];
|
|
1234
|
+
const methodMiddlewares = propertyKey ? Reflect.getMetadata("route:middleware", target, propertyKey) || [] : [];
|
|
1235
|
+
return [...classMiddlewares, ...methodMiddlewares];
|
|
1236
|
+
}
|
|
1237
|
+
async buildController(controller) {
|
|
1238
|
+
const ctrl = typedi.default.get(controller);
|
|
1239
|
+
const controllerMeta = Reflect.getMetadata(CONTROLLER_META_KEY, ctrl.constructor);
|
|
1240
|
+
if (!controllerMeta) return;
|
|
1241
|
+
const prototype = Object.getPrototypeOf(ctrl);
|
|
1242
|
+
const methods = Object.getOwnPropertyNames(prototype).filter((name) => name !== "constructor");
|
|
1243
|
+
const tag = ctrl.constructor.name.replace("Controller", "");
|
|
1244
|
+
const swaggerControllerMeta = Reflect.getMetadata("controller:openapi", ctrl.constructor) || {};
|
|
1245
|
+
const authClsMeata = Reflect.getMetadata(AUTHORIZATION_META_KEY, ctrl.constructor) || {
|
|
1246
|
+
authorize: false,
|
|
1247
|
+
options: void 0
|
|
1248
|
+
};
|
|
1249
|
+
for await (const method of methods) {
|
|
1250
|
+
const methodMeta = Reflect.getMetadata(ROUTE_META_KEY, prototype, method);
|
|
1251
|
+
if (!methodMeta) continue;
|
|
1252
|
+
const methodmetaOptions = {
|
|
1253
|
+
method: methodMeta.method.toLowerCase(),
|
|
1254
|
+
path: formatUrl(controllerMeta.path + methodMeta.path)
|
|
1255
|
+
};
|
|
1256
|
+
const routeKey = `${methodmetaOptions.method}:${methodmetaOptions.path}`;
|
|
1257
|
+
if (!this.routeSet.has(routeKey)) this.routeSet.add(routeKey);
|
|
1258
|
+
const classMiddlewares = this.executeMiddlewares(ctrl, method);
|
|
1259
|
+
const swaggerMeta = Reflect.getMetadata("route:openapi", prototype, method) || {};
|
|
1260
|
+
const authClsMethodMeata = Reflect.getMetadata(AUTHORIZATION_META_KEY, ctrl.constructor, method) || {
|
|
1261
|
+
authorize: false,
|
|
1262
|
+
options: void 0
|
|
1263
|
+
};
|
|
1264
|
+
const allMeta = this._processMeta(prototype, method);
|
|
1265
|
+
let bodySchema = null;
|
|
1266
|
+
allMeta.body.forEach((r) => {
|
|
1267
|
+
if (r.schema) bodySchema = { ...r.schema };
|
|
1268
|
+
});
|
|
1269
|
+
let schema = {
|
|
1270
|
+
...swaggerControllerMeta,
|
|
1271
|
+
...swaggerMeta,
|
|
1272
|
+
tags: [tag]
|
|
1273
|
+
};
|
|
1274
|
+
if (!swaggerMeta.body && bodySchema) schema = {
|
|
1275
|
+
...schema,
|
|
1276
|
+
body: bodySchema
|
|
1277
|
+
};
|
|
1278
|
+
if (!swaggerMeta.query && !schema.querystring) {
|
|
1279
|
+
for (const queryMeta of allMeta.query) if (queryMeta.validatorClass && queryMeta.schema) {
|
|
1280
|
+
schema.querystring = queryMeta.schema;
|
|
1281
|
+
break;
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
if (!swaggerMeta.body && !bodySchema) {
|
|
1285
|
+
for (const bodyMeta of allMeta.body) if (bodyMeta.validatorClass && bodyMeta.schema) {
|
|
1286
|
+
schema = {
|
|
1287
|
+
...schema,
|
|
1288
|
+
body: bodyMeta.schema
|
|
1289
|
+
};
|
|
1290
|
+
break;
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
const routePath = methodmetaOptions.path === "" ? "/" : methodmetaOptions.path;
|
|
1294
|
+
const isMultipart = schema?.consumes?.includes("multipart/form-data") || Object.values(schema?.body?.properties || {}).some((p) => p.format === "binary");
|
|
1295
|
+
if (isMultipart) {
|
|
1296
|
+
schema.consumes = ["multipart/form-data"];
|
|
1297
|
+
if (!schema.body) schema.body = {
|
|
1298
|
+
type: "object",
|
|
1299
|
+
properties: {}
|
|
1300
|
+
};
|
|
1301
|
+
for (const param of allMeta.body) if (param.type === "route:file") schema.body.properties[param.key] = {
|
|
1302
|
+
type: "string",
|
|
1303
|
+
format: "binary"
|
|
1304
|
+
};
|
|
1305
|
+
else schema.body.properties[param.key] = { type: param.dataType };
|
|
1306
|
+
}
|
|
1307
|
+
const routeSchema = buildRouteSchema(schema);
|
|
1308
|
+
this.app.route({
|
|
1309
|
+
url: routePath,
|
|
1310
|
+
method: methodmetaOptions.method.toUpperCase(),
|
|
1311
|
+
schema: routeSchema,
|
|
1312
|
+
attachValidation: isMultipart,
|
|
1313
|
+
handler: async (req, res) => {
|
|
1314
|
+
let reqClone = req;
|
|
1315
|
+
if (authClsMeata.authorize && this.authorizeMiddleware) {
|
|
1316
|
+
await container_default.get(this.authorizeMiddleware).authorize(reqClone, authClsMeata.options);
|
|
1317
|
+
if (res.sent) return;
|
|
1318
|
+
}
|
|
1319
|
+
if (authClsMethodMeata.authorize && this.authorizeMiddleware) {
|
|
1320
|
+
await container_default.get(this.authorizeMiddleware).authorize(reqClone, authClsMethodMeata.options);
|
|
1321
|
+
if (res.sent) return;
|
|
1322
|
+
}
|
|
1323
|
+
if (classMiddlewares.length > 0) for (let m of classMiddlewares) {
|
|
1324
|
+
reqClone = await typedi.default.get(m.constructor).invoke(reqClone, res);
|
|
1325
|
+
if (res.sent) return;
|
|
1326
|
+
}
|
|
1327
|
+
const args = await this._mapArgs(reqClone, allMeta);
|
|
1328
|
+
for (let paramMeta of allMeta.params) if (paramMeta.required) validateOrThrow({ [paramMeta.key]: args[paramMeta.index] }, { [paramMeta.key]: { type: paramMeta.dataType } }, { location: "param" });
|
|
1329
|
+
for (let queryMeta of allMeta.query) {
|
|
1330
|
+
if (queryMeta.validatorClass) {
|
|
1331
|
+
const err = await validateObjectByInstance(queryMeta.dataType, args[queryMeta.index]);
|
|
1332
|
+
if (err) return await res.code(400).send({
|
|
1333
|
+
code: 400,
|
|
1334
|
+
error: "ValidationError",
|
|
1335
|
+
errors: err,
|
|
1336
|
+
message: err.message
|
|
1337
|
+
});
|
|
1338
|
+
}
|
|
1339
|
+
if (queryMeta.required) validateOrThrow({ [queryMeta.key]: args[queryMeta.index] }, { [queryMeta.key]: { type: queryMeta.dataType } }, { location: "queryparam" });
|
|
1340
|
+
}
|
|
1341
|
+
if (!isMultipart) {
|
|
1342
|
+
for (let bodyMeta of allMeta.body) if (bodyMeta.validatorClass) {
|
|
1343
|
+
const err = await validateObjectByInstance(bodyMeta.dataType, args[bodyMeta.index]);
|
|
1344
|
+
if (err) return await res.code(400).send({
|
|
1345
|
+
code: 400,
|
|
1346
|
+
error: "ValidationError",
|
|
1347
|
+
errors: err,
|
|
1348
|
+
message: err.message
|
|
1349
|
+
});
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
const result = await prototype[method].apply(ctrl, args);
|
|
1353
|
+
if (result?.download) {
|
|
1354
|
+
const { stream, filename } = result;
|
|
1355
|
+
if (!stream || typeof stream.pipe !== "function") return res.code(500).send({
|
|
1356
|
+
code: 500,
|
|
1357
|
+
error: "INTERNAL_ERROR",
|
|
1358
|
+
message: "Invalid stream object"
|
|
1359
|
+
});
|
|
1360
|
+
const contentType = result.contentType || mime.default.getType(filename) || "application/octet-stream";
|
|
1361
|
+
res.header("Content-Type", contentType);
|
|
1362
|
+
res.header("Content-Disposition", `attachment; filename="${filename}"`);
|
|
1363
|
+
stream.on("error", (err) => {
|
|
1364
|
+
console.error("Stream error:", err);
|
|
1365
|
+
if (!res.sent) res.code(500).send({
|
|
1366
|
+
code: 500,
|
|
1367
|
+
error: "StreamError",
|
|
1368
|
+
message: "Error while streaming file."
|
|
1369
|
+
});
|
|
1370
|
+
});
|
|
1371
|
+
return res.send(stream);
|
|
1372
|
+
}
|
|
1373
|
+
if (result instanceof node_stream.default || typeof result?.pipe === "function") {
|
|
1374
|
+
result.on("error", (err) => {
|
|
1375
|
+
console.error("Stream error:", err);
|
|
1376
|
+
if (!res.sent) res.code(500).send({
|
|
1377
|
+
code: 500,
|
|
1378
|
+
error: "StreamError",
|
|
1379
|
+
message: "Error while streaming file."
|
|
1380
|
+
});
|
|
1381
|
+
});
|
|
1382
|
+
res.header("Content-Type", "application/octet-stream");
|
|
1383
|
+
return res.send(result);
|
|
1384
|
+
}
|
|
1385
|
+
return res.send(result);
|
|
1386
|
+
}
|
|
1387
|
+
});
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
async _mapArgs(req, meta) {
|
|
1391
|
+
if (!req.hasOwnProperty("_argsCache")) Object.defineProperty(req, "_argsCache", {
|
|
1392
|
+
value: /* @__PURE__ */ new Map(),
|
|
1393
|
+
enumerable: false,
|
|
1394
|
+
writable: false,
|
|
1395
|
+
configurable: false
|
|
1396
|
+
});
|
|
1397
|
+
const cache = req._argsCache;
|
|
1398
|
+
const cacheKey = JSON.stringify(meta);
|
|
1399
|
+
if (cache.has(cacheKey)) return cache.get(cacheKey);
|
|
1400
|
+
const maxIndex = Math.max(...meta.params.map((p) => p.index || 0), ...meta.query.map((q) => q.index), ...meta.body.map((b) => b.index), ...meta.currentUser.map((u) => u.index), ...meta.headers.map((h) => h.index), ...meta.request?.map((r) => r.index) || [], ...meta.file?.map((f) => f.index) || [], ...meta.files?.map((f) => f.index) || [], -1) + 1;
|
|
1401
|
+
const args = new Array(maxIndex).fill(void 0);
|
|
1402
|
+
meta.params.forEach((p) => {
|
|
1403
|
+
const raw = p.key === "all" ? { ...req.params } : req.params[p.key] ?? null;
|
|
1404
|
+
args[p.index] = autoCast(raw, p.dataType, p.schema);
|
|
1405
|
+
});
|
|
1406
|
+
meta.query.forEach((q) => {
|
|
1407
|
+
const raw = q.key === "all" ? normalizeQueryDeep({ ...req.query }) : req.query[q.key];
|
|
1408
|
+
args[q.index] = autoCast(raw, q.dataType, q.schema);
|
|
1409
|
+
});
|
|
1410
|
+
meta.body.forEach((body) => {
|
|
1411
|
+
args[body.index] = {
|
|
1412
|
+
...req.body,
|
|
1413
|
+
...req.formData
|
|
1414
|
+
};
|
|
1415
|
+
});
|
|
1416
|
+
meta.currentUser.forEach((user) => {
|
|
1417
|
+
args[user.index] = req.user;
|
|
1418
|
+
});
|
|
1419
|
+
meta.headers.forEach((header) => {
|
|
1420
|
+
args[header.index] = header.key === "all" ? { ...req.headers } : req.headers[header.key];
|
|
1421
|
+
});
|
|
1422
|
+
if (meta.request && meta.request.length > 0) meta.request.forEach((r) => {
|
|
1423
|
+
args[r.index] = req;
|
|
1424
|
+
});
|
|
1425
|
+
const needsFiles = meta.file && meta.file.length > 0 || meta.files && meta.files.length > 0;
|
|
1426
|
+
if (needsFiles && req.headers["content-type"]?.startsWith("multipart/form-data") && req.saveRequestFiles) {
|
|
1427
|
+
const files = await req.saveRequestFiles();
|
|
1428
|
+
if (!files || files.length === 0) {
|
|
1429
|
+
if (meta.files && meta.files.length > 0) throw new BadRequestException({ error: "No files uploaded" });
|
|
1430
|
+
if (meta.file && meta.file.length > 0) meta.file.forEach((f) => {
|
|
1431
|
+
args[f.index] = null;
|
|
1432
|
+
});
|
|
1433
|
+
} else {
|
|
1434
|
+
const fileInfo = files.map((file) => ({
|
|
1435
|
+
type: file.type,
|
|
1436
|
+
filepath: file.filepath,
|
|
1437
|
+
fieldname: file.fieldname,
|
|
1438
|
+
filename: file.filename,
|
|
1439
|
+
encoding: file.encoding,
|
|
1440
|
+
mimetype: file.mimetype,
|
|
1441
|
+
fields: file.fields,
|
|
1442
|
+
toBuffer: file.toBuffer
|
|
1443
|
+
}));
|
|
1444
|
+
if (meta.file && meta.file.length > 0) meta.file.forEach((f) => {
|
|
1445
|
+
if (f.fieldName === "all") args[f.index] = fileInfo[0] || null;
|
|
1446
|
+
else {
|
|
1447
|
+
const file = fileInfo.find((x) => x.fieldname === f.fieldName);
|
|
1448
|
+
if (!file) throw new BadRequestException(`File field "${f.fieldName}" not found in uploaded files`);
|
|
1449
|
+
args[f.index] = file;
|
|
1450
|
+
}
|
|
1451
|
+
});
|
|
1452
|
+
if (meta.files && meta.files.length > 0) meta.files.forEach((f) => {
|
|
1453
|
+
if (f.fieldName === "all") args[f.index] = fileInfo;
|
|
1454
|
+
else {
|
|
1455
|
+
const matchingFiles = fileInfo.filter((x) => x.fieldname === f.fieldName);
|
|
1456
|
+
if (matchingFiles.length === 0) throw new BadRequestException(`No files found for field "${f.fieldName}"`);
|
|
1457
|
+
args[f.index] = matchingFiles;
|
|
1458
|
+
}
|
|
1459
|
+
});
|
|
1460
|
+
}
|
|
1461
|
+
} else if (needsFiles) throw new BadRequestException({ error: "Invalid content type. Expected multipart/form-data for file uploads" });
|
|
1462
|
+
cache.set(cacheKey, args);
|
|
1463
|
+
return args;
|
|
1464
|
+
}
|
|
1465
|
+
_routeHandler(routePath, method, fn) {
|
|
1466
|
+
const routeKey = method + ":" + routePath;
|
|
1467
|
+
this.rMap.set(routeKey, {
|
|
1468
|
+
handler: fn,
|
|
1469
|
+
middlewares: [],
|
|
1470
|
+
schema: {}
|
|
1471
|
+
});
|
|
1472
|
+
const route = {
|
|
1473
|
+
useMiddleware: (middlewares) => {
|
|
1474
|
+
const ms = (Array.isArray(middlewares) ? middlewares : [middlewares]).map((mclass) => {
|
|
1475
|
+
const cls = typedi.default.get(mclass);
|
|
1476
|
+
this.middlewares.set(mclass.name, cls);
|
|
1477
|
+
return cls.invoke;
|
|
1478
|
+
});
|
|
1479
|
+
const r = this.rMap.get(routeKey);
|
|
1480
|
+
if (r) r.middlewares = ms;
|
|
1481
|
+
return route;
|
|
1482
|
+
},
|
|
1483
|
+
useOpenApi: (options) => {
|
|
1484
|
+
const r = this.rMap.get(routeKey);
|
|
1485
|
+
if (r) r.schema = options;
|
|
1486
|
+
return route;
|
|
1487
|
+
}
|
|
1488
|
+
};
|
|
1489
|
+
return route;
|
|
1490
|
+
}
|
|
1491
|
+
mapGet(path = "", fn) {
|
|
1492
|
+
return this._routeHandler(path, "GET", fn);
|
|
1493
|
+
}
|
|
1494
|
+
mapPost(path = "", fn) {
|
|
1495
|
+
return this._routeHandler(path, "POST", fn);
|
|
1496
|
+
}
|
|
1497
|
+
mapPut(path = "", fn) {
|
|
1498
|
+
return this._routeHandler(path, "PUT", fn);
|
|
1499
|
+
}
|
|
1500
|
+
mapDelete(path = "", fn) {
|
|
1501
|
+
return this._routeHandler(path, "DELETE", fn);
|
|
1502
|
+
}
|
|
1503
|
+
async mapRoute(method, path = "", fn) {
|
|
1504
|
+
this.app[method](path, async (req, res) => {
|
|
1505
|
+
try {
|
|
1506
|
+
const result = await fn(req, res);
|
|
1507
|
+
if (typeof result === "object" && result !== null) res.json(result);
|
|
1508
|
+
else res.send(result);
|
|
1509
|
+
} catch (error) {
|
|
1510
|
+
console.error(`Error in ${method} route handler:`, error);
|
|
1511
|
+
res.status(500).send({ error: "Internal Server Error" });
|
|
1512
|
+
}
|
|
1513
|
+
});
|
|
1514
|
+
}
|
|
1515
|
+
processFunctionalRoutes() {
|
|
1516
|
+
this.rMap.forEach((value, key) => {
|
|
1517
|
+
const colonIdx = key.indexOf(":");
|
|
1518
|
+
const m = key.slice(0, colonIdx);
|
|
1519
|
+
const r = key.slice(colonIdx + 1);
|
|
1520
|
+
const routeSchema = buildRouteSchema(value.schema || {});
|
|
1521
|
+
this.app.route({
|
|
1522
|
+
method: m.toUpperCase(),
|
|
1523
|
+
url: r,
|
|
1524
|
+
schema: routeSchema,
|
|
1525
|
+
preHandler: value.middlewares ?? [],
|
|
1526
|
+
handler: async (req, res) => {
|
|
1527
|
+
return value.handler(req, res);
|
|
1528
|
+
}
|
|
1529
|
+
});
|
|
1530
|
+
});
|
|
1531
|
+
}
|
|
1532
|
+
};
|
|
1533
|
+
//#endregion
|
|
1534
|
+
//#region \0@oxc-project+runtime@0.121.0/helpers/decorateMetadata.js
|
|
1535
|
+
function __decorateMetadata(k, v) {
|
|
1536
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1537
|
+
}
|
|
1538
|
+
var init_decorateMetadata = require_chunk.__esmMin((() => {}));
|
|
1539
|
+
//#endregion
|
|
1540
|
+
//#region \0@oxc-project+runtime@0.121.0/helpers/decorate.js
|
|
1541
|
+
function __decorate(decorators, target, key, desc) {
|
|
1542
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1543
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1544
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1545
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1546
|
+
}
|
|
1547
|
+
var init_decorate = require_chunk.__esmMin((() => {}));
|
|
1548
|
+
//#endregion
|
|
1549
|
+
//#region src/kenx-provider.ts
|
|
1550
|
+
var kenx_provider_exports = /* @__PURE__ */ require_chunk.__exportAll({ DB: () => DB });
|
|
1551
|
+
var DB;
|
|
1552
|
+
var init_kenx_provider = require_chunk.__esmMin((() => {
|
|
1553
|
+
init_decorateMetadata();
|
|
1554
|
+
init_decorate();
|
|
1555
|
+
DB = class DB {
|
|
1556
|
+
connection;
|
|
1557
|
+
constructor() {
|
|
1558
|
+
const existing = typedi.Container.has("KnexConnection") ? typedi.Container.get("KnexConnection") : null;
|
|
1559
|
+
if (existing) this.connection = existing;
|
|
1560
|
+
}
|
|
1561
|
+
init(config) {
|
|
1562
|
+
if (!this.connection) {
|
|
1563
|
+
this.connection = require("knex")(config);
|
|
1564
|
+
typedi.Container.set("KnexConnection", this.connection);
|
|
1565
|
+
}
|
|
1566
|
+
return this.connection;
|
|
1567
|
+
}
|
|
1568
|
+
get client() {
|
|
1569
|
+
if (!this.connection) throw new Error("Knex is not initialized. Call DB.init(config) first.");
|
|
1570
|
+
return this.connection;
|
|
1571
|
+
}
|
|
1572
|
+
};
|
|
1573
|
+
DB = __decorate([(0, typedi.Service)(), __decorateMetadata("design:paramtypes", [])], DB);
|
|
1574
|
+
}));
|
|
1575
|
+
//#endregion
|
|
1576
|
+
//#region src/websocket.ts
|
|
1577
|
+
var websocket_exports = /* @__PURE__ */ require_chunk.__exportAll({
|
|
1578
|
+
AvleonSocketIo: () => AvleonSocketIo,
|
|
1579
|
+
SocketIoServer: () => SocketIoServer
|
|
1580
|
+
});
|
|
1581
|
+
var SocketIoServer, AvleonSocketIo;
|
|
1582
|
+
var init_websocket = require_chunk.__esmMin((() => {
|
|
1583
|
+
init_decorate();
|
|
1584
|
+
SocketIoServer = new typedi.Token("SocketIoServer");
|
|
1585
|
+
AvleonSocketIo = class AvleonSocketIo {
|
|
1586
|
+
io;
|
|
1587
|
+
sendToAll() {}
|
|
1588
|
+
sendOnly() {}
|
|
1589
|
+
sendRoom() {}
|
|
1590
|
+
receive(channel) {}
|
|
1591
|
+
};
|
|
1592
|
+
AvleonSocketIo = __decorate([(0, typedi.Service)()], AvleonSocketIo);
|
|
1593
|
+
}));
|
|
1594
|
+
//#endregion
|
|
1595
|
+
//#region src/event-dispatcher.ts
|
|
1596
|
+
var event_dispatcher_exports = /* @__PURE__ */ require_chunk.__exportAll({
|
|
1597
|
+
Dispatch: () => Dispatch,
|
|
1598
|
+
EventDispatcher: () => EventDispatcher,
|
|
1599
|
+
SocketContextService: () => SocketContextService
|
|
1600
|
+
});
|
|
1601
|
+
function Dispatch(event, options) {
|
|
1602
|
+
return function(target, propertyKey, descriptor) {
|
|
1603
|
+
const original = descriptor.value;
|
|
1604
|
+
descriptor.value = async function(...args) {
|
|
1605
|
+
const result = await original.apply(this, args);
|
|
1606
|
+
await typedi.Container.get(EventDispatcher).dispatch(event, result, options);
|
|
1607
|
+
return result;
|
|
1608
|
+
};
|
|
1609
|
+
};
|
|
1610
|
+
}
|
|
1611
|
+
var _ref$1, SocketContextService, EventDispatcher;
|
|
1612
|
+
var init_event_dispatcher = require_chunk.__esmMin((() => {
|
|
1613
|
+
init_websocket();
|
|
1614
|
+
init_helpers();
|
|
1615
|
+
init_decorate();
|
|
1616
|
+
init_decorateMetadata();
|
|
1617
|
+
SocketContextService = class SocketContextService {
|
|
1618
|
+
storage = new node_async_hooks.AsyncLocalStorage();
|
|
1619
|
+
run(socket, fn) {
|
|
1620
|
+
this.storage.run({ socket }, fn);
|
|
1621
|
+
}
|
|
1622
|
+
getSocket() {
|
|
1623
|
+
return this.storage.getStore()?.socket;
|
|
1624
|
+
}
|
|
1625
|
+
};
|
|
1626
|
+
SocketContextService = __decorate([(0, typedi.Service)()], SocketContextService);
|
|
1627
|
+
EventDispatcher = class EventDispatcher {
|
|
1628
|
+
constructor(_context) {
|
|
1629
|
+
this._context = _context;
|
|
1630
|
+
}
|
|
1631
|
+
async dispatch(event, data, options = {}) {
|
|
1632
|
+
const retryCount = options.retry ?? 0;
|
|
1633
|
+
const delay = options.retryDelay ?? 300;
|
|
1634
|
+
for (let attempt = 0; attempt <= retryCount; attempt++) try {
|
|
1635
|
+
await this.dispatchToTransports(event, data, options);
|
|
1636
|
+
break;
|
|
1637
|
+
} catch (err) {
|
|
1638
|
+
if (attempt === retryCount) throw err;
|
|
1639
|
+
await sleep(delay * (attempt + 1));
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
async dispatchToTransports(event, data, options) {
|
|
1643
|
+
const transports = options.transports ?? ["socket"];
|
|
1644
|
+
for (const transport of transports) if (transport === "socket") {
|
|
1645
|
+
const io = typedi.Container.get(SocketIoServer);
|
|
1646
|
+
const socket = typedi.Container.get(SocketContextService).getSocket();
|
|
1647
|
+
if (options.broadcast && socket) if (options.room) socket.broadcast.to(options.room).emit(event, data);
|
|
1648
|
+
else socket.broadcast.emit(event, data);
|
|
1649
|
+
else if (options.room) io.to(options.room).emit(event, data);
|
|
1650
|
+
else io.emit(event, data);
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
};
|
|
1654
|
+
EventDispatcher = __decorate([(0, typedi.Service)(), __decorateMetadata("design:paramtypes", [typeof (_ref$1 = typeof SocketContextService !== "undefined" && SocketContextService) === "function" ? _ref$1 : Object])], EventDispatcher);
|
|
1655
|
+
}));
|
|
1656
|
+
//#endregion
|
|
1657
|
+
//#region src/event-subscriber.ts
|
|
1658
|
+
var event_subscriber_exports = /* @__PURE__ */ require_chunk.__exportAll({
|
|
1659
|
+
EventSubscriberRegistry: () => EventSubscriberRegistry,
|
|
1660
|
+
Private: () => Private,
|
|
1661
|
+
Subscribe: () => Subscribe,
|
|
1662
|
+
getPrivateChannelResolver: () => getPrivateChannelResolver,
|
|
1663
|
+
getSocketSubscribers: () => getSocketSubscribers,
|
|
1664
|
+
isPrivate: () => isPrivate,
|
|
1665
|
+
registerSocketSubscriber: () => registerSocketSubscriber
|
|
1666
|
+
});
|
|
1667
|
+
function Private(channelResolver) {
|
|
1668
|
+
return function(target, propertyKey) {
|
|
1669
|
+
Reflect.defineMetadata(PRIVATE_META_KEY, true, target, propertyKey);
|
|
1670
|
+
Reflect.defineMetadata(`private:channel:${propertyKey}`, channelResolver, target);
|
|
1671
|
+
};
|
|
1672
|
+
}
|
|
1673
|
+
function isPrivate(target, propertyKey) {
|
|
1674
|
+
return Reflect.getMetadata(PRIVATE_META_KEY, target, propertyKey) || false;
|
|
1675
|
+
}
|
|
1676
|
+
function getPrivateChannelResolver(target, propertyKey) {
|
|
1677
|
+
return Reflect.getMetadata(`private:channel:${propertyKey}`, target);
|
|
1678
|
+
}
|
|
1679
|
+
function registerSocketSubscriber(target) {
|
|
1680
|
+
socketSubscriberClasses.add(target);
|
|
1681
|
+
}
|
|
1682
|
+
function getSocketSubscribers() {
|
|
1683
|
+
return Array.from(socketSubscriberClasses);
|
|
1684
|
+
}
|
|
1685
|
+
function Subscribe(event) {
|
|
1686
|
+
return (target, propertyKey) => {
|
|
1687
|
+
Reflect.defineMetadata("socket:event", event, target, propertyKey);
|
|
1688
|
+
registerSocketSubscriber(target.constructor);
|
|
1689
|
+
};
|
|
1690
|
+
}
|
|
1691
|
+
var _ref, PRIVATE_META_KEY, socketSubscriberClasses, EventSubscriberRegistry;
|
|
1692
|
+
var init_event_subscriber = require_chunk.__esmMin((() => {
|
|
1693
|
+
init_event_dispatcher();
|
|
1694
|
+
init_decorateMetadata();
|
|
1695
|
+
init_decorate();
|
|
1696
|
+
PRIVATE_META_KEY = "avleon:private";
|
|
1697
|
+
socketSubscriberClasses = /* @__PURE__ */ new Set();
|
|
1698
|
+
EventSubscriberRegistry = class EventSubscriberRegistry {
|
|
1699
|
+
constructor(socketContext) {
|
|
1700
|
+
this.socketContext = socketContext;
|
|
1701
|
+
}
|
|
1702
|
+
register(socket) {
|
|
1703
|
+
const subscriberClasses = getSocketSubscribers();
|
|
1704
|
+
for (const SubscriberClass of subscriberClasses) {
|
|
1705
|
+
const instance = typedi.Container.get(SubscriberClass);
|
|
1706
|
+
const prototype = Object.getPrototypeOf(instance);
|
|
1707
|
+
const methodNames = Object.getOwnPropertyNames(prototype).filter((name) => typeof prototype[name] === "function");
|
|
1708
|
+
for (const methodName of methodNames) {
|
|
1709
|
+
const event = Reflect.getMetadata("socket:event", prototype, methodName);
|
|
1710
|
+
const isPrivateListener = isPrivate(instance, methodName);
|
|
1711
|
+
const channelResolver = getPrivateChannelResolver(instance, methodName);
|
|
1712
|
+
if (event) {
|
|
1713
|
+
const channel = isPrivateListener && channelResolver ? channelResolver(socket) : event;
|
|
1714
|
+
console.log("Channel", channel);
|
|
1715
|
+
socket.on(channel, (payload) => {
|
|
1716
|
+
this.socketContext.run(socket, async () => {
|
|
1717
|
+
if (isPrivateListener) {
|
|
1718
|
+
if (!socket.data.user) return;
|
|
1719
|
+
}
|
|
1720
|
+
await instance[methodName](payload, socket.data);
|
|
1721
|
+
});
|
|
1722
|
+
});
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
};
|
|
1728
|
+
EventSubscriberRegistry = __decorate([(0, typedi.Service)(), __decorateMetadata("design:paramtypes", [typeof (_ref = typeof SocketContextService !== "undefined" && SocketContextService) === "function" ? _ref : Object])], EventSubscriberRegistry);
|
|
1729
|
+
}));
|
|
1730
|
+
//#endregion
|
|
1731
|
+
//#region src/core/application.ts
|
|
1732
|
+
/**
|
|
1733
|
+
* @copyright 2024
|
|
1734
|
+
* @author Tareq Hossain
|
|
1735
|
+
* @email xtrinsic96@gmail.com
|
|
1736
|
+
* @url https://github.com/xtareq
|
|
1737
|
+
*/
|
|
1738
|
+
init_exceptions();
|
|
1739
|
+
init_system_exception();
|
|
1740
|
+
init_container();
|
|
1741
|
+
function requireTypeorm() {
|
|
1742
|
+
try {
|
|
1743
|
+
return require("typeorm");
|
|
1744
|
+
} catch {
|
|
1745
|
+
throw new Error("[Avleon] typeorm is not installed.\nRun: npm install typeorm\nThen install a driver: npm install pg (or mysql2, sqlite3, etc.)");
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
function requireSocketIo() {
|
|
1749
|
+
try {
|
|
1750
|
+
return require("fastify-socket.io");
|
|
1751
|
+
} catch {
|
|
1752
|
+
throw new Error("[Avleon] fastify-socket.io is not installed.\nRun: npm install fastify-socket.io socket.io");
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
function requireSwagger() {
|
|
1756
|
+
try {
|
|
1757
|
+
return require("@fastify/swagger");
|
|
1758
|
+
} catch {
|
|
1759
|
+
throw new Error("[Avleon] @fastify/swagger is not installed.\nRun: npm install @fastify/swagger @fastify/swagger-ui");
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
function requireSwaggerUi() {
|
|
1763
|
+
try {
|
|
1764
|
+
return require("@fastify/swagger-ui");
|
|
1765
|
+
} catch {
|
|
1766
|
+
throw new Error("[Avleon] @fastify/swagger-ui is not installed.\nRun: npm install @fastify/swagger-ui");
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
function requireScalar() {
|
|
1770
|
+
try {
|
|
1771
|
+
return require("@scalar/fastify-api-reference");
|
|
1772
|
+
} catch {
|
|
1773
|
+
throw new Error("[Avleon] @scalar/fastify-api-reference is not installed.\nRun: npm install @scalar/fastify-api-reference");
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
function requireCors() {
|
|
1777
|
+
try {
|
|
1778
|
+
return require("@fastify/cors");
|
|
1779
|
+
} catch {
|
|
1780
|
+
throw new Error("[Avleon] @fastify/cors is not installed.\nRun: npm install @fastify/cors");
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
function requireMultipart() {
|
|
1784
|
+
try {
|
|
1785
|
+
return require("@fastify/multipart");
|
|
1786
|
+
} catch {
|
|
1787
|
+
throw new Error("[Avleon] @fastify/multipart is not installed.\nRun: npm install @fastify/multipart");
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
function requireStatic() {
|
|
1791
|
+
try {
|
|
1792
|
+
return require("@fastify/static");
|
|
1793
|
+
} catch {
|
|
1794
|
+
throw new Error("[Avleon] @fastify/static is not installed.\nRun: npm install @fastify/static");
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
var AvleonApplication = class AvleonApplication {
|
|
1798
|
+
app;
|
|
1799
|
+
router;
|
|
1800
|
+
static instance;
|
|
1801
|
+
alreadyRun = false;
|
|
1802
|
+
hasSwagger = false;
|
|
1803
|
+
globalSwaggerOptions;
|
|
1804
|
+
dataSourceOptions = null;
|
|
1805
|
+
dataSource = null;
|
|
1806
|
+
isMapFeatures = false;
|
|
1807
|
+
registerControllerAuto = false;
|
|
1808
|
+
registerControllerPath = "src/controllers";
|
|
1809
|
+
controllers = [];
|
|
1810
|
+
_hasWebsocket = false;
|
|
1811
|
+
io;
|
|
1812
|
+
constructor(options) {
|
|
1813
|
+
this.app = (0, fastify.default)({
|
|
1814
|
+
...options?.server,
|
|
1815
|
+
ajv: { customOptions: {
|
|
1816
|
+
strict: false,
|
|
1817
|
+
...options?.server?.ajv?.customOptions
|
|
1818
|
+
} }
|
|
1819
|
+
});
|
|
1820
|
+
this.router = new AvleonRouter(this.app);
|
|
1821
|
+
if (options?.dataSourceOptions) {
|
|
1822
|
+
this.dataSourceOptions = options.dataSourceOptions;
|
|
1823
|
+
if (this.dataSourceOptions) {
|
|
1824
|
+
const { DataSource } = requireTypeorm();
|
|
1825
|
+
this.dataSource = new DataSource(this.dataSourceOptions);
|
|
1826
|
+
typedi.default.set(DataSource, this.dataSource);
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
static getApp(options) {
|
|
1831
|
+
if (!AvleonApplication.instance) AvleonApplication.instance = new AvleonApplication(options);
|
|
1832
|
+
return AvleonApplication.instance;
|
|
1833
|
+
}
|
|
1834
|
+
/** @deprecated Use `getApp` instead. This is internal. */
|
|
1835
|
+
static getInternalApp(options) {
|
|
1836
|
+
return new AvleonApplication(options);
|
|
1837
|
+
}
|
|
1838
|
+
useCors(options) {
|
|
1839
|
+
this.app.register(requireCors(), options);
|
|
1840
|
+
return this;
|
|
1841
|
+
}
|
|
1842
|
+
useDatasource(dataSource) {
|
|
1843
|
+
const { DataSource } = requireTypeorm();
|
|
1844
|
+
this.dataSource = dataSource;
|
|
1845
|
+
typedi.default.set(DataSource, this.dataSource);
|
|
1846
|
+
return this;
|
|
1847
|
+
}
|
|
1848
|
+
useMultipart(options) {
|
|
1849
|
+
this.app.register(requireMultipart(), {
|
|
1850
|
+
attachFieldsToBody: true,
|
|
1851
|
+
limits: { fileSize: 10 * 1024 * 1024 },
|
|
1852
|
+
...options
|
|
1853
|
+
});
|
|
1854
|
+
return this;
|
|
1855
|
+
}
|
|
1856
|
+
useOpenApi(options) {
|
|
1857
|
+
this.hasSwagger = true;
|
|
1858
|
+
this.globalSwaggerOptions = options;
|
|
1859
|
+
return this;
|
|
1860
|
+
}
|
|
1861
|
+
async initSwagger(options) {
|
|
1862
|
+
const baseSchema = generateSwaggerSchema((this.controllers ?? []).filter((c) => c != null && typeof c === "function"));
|
|
1863
|
+
await this.app.register(requireSwagger(), { openapi: {
|
|
1864
|
+
...baseSchema,
|
|
1865
|
+
info: options?.info || {
|
|
1866
|
+
title: "API",
|
|
1867
|
+
version: "1.0.0"
|
|
1868
|
+
},
|
|
1869
|
+
servers: options?.servers,
|
|
1870
|
+
tags: options?.tags,
|
|
1871
|
+
components: {
|
|
1872
|
+
...baseSchema?.components,
|
|
1873
|
+
...options?.components,
|
|
1874
|
+
schemas: {
|
|
1875
|
+
...baseSchema?.components?.schemas,
|
|
1876
|
+
...options?.components?.schemas
|
|
1877
|
+
}
|
|
1878
|
+
},
|
|
1879
|
+
security: options?.security,
|
|
1880
|
+
externalDocs: options?.externalDocs
|
|
1881
|
+
} });
|
|
1882
|
+
const routePrefix = options?.routePrefix || "/swagger";
|
|
1883
|
+
if (options?.provider === "scalar") await this.app.register(requireScalar(), {
|
|
1884
|
+
routePrefix,
|
|
1885
|
+
configuration: {
|
|
1886
|
+
spec: { content: () => requireSwagger() },
|
|
1887
|
+
...options?.uiConfig
|
|
1888
|
+
}
|
|
1889
|
+
});
|
|
1890
|
+
else await this.app.register(requireSwaggerUi(), {
|
|
1891
|
+
routePrefix,
|
|
1892
|
+
uiConfig: {
|
|
1893
|
+
docExpansion: "full",
|
|
1894
|
+
deepLinking: false,
|
|
1895
|
+
...options?.uiConfig
|
|
1896
|
+
},
|
|
1897
|
+
uiHooks: {
|
|
1898
|
+
onRequest: function(request, reply, next) {
|
|
1899
|
+
next();
|
|
1900
|
+
},
|
|
1901
|
+
preHandler: function(request, reply, next) {
|
|
1902
|
+
next();
|
|
1903
|
+
}
|
|
1904
|
+
},
|
|
1905
|
+
staticCSP: true,
|
|
1906
|
+
transformSpecificationClone: true
|
|
1907
|
+
});
|
|
1908
|
+
}
|
|
1909
|
+
useMiddlewares(middlewares) {
|
|
1910
|
+
middlewares.forEach((m) => {
|
|
1911
|
+
const cls = typedi.default.get(m);
|
|
1912
|
+
this.app.addHook("preHandler", async (req, res) => {
|
|
1913
|
+
await cls.invoke(req, res);
|
|
1914
|
+
});
|
|
1915
|
+
});
|
|
1916
|
+
return this;
|
|
1917
|
+
}
|
|
1918
|
+
useAuthorization(authorization) {
|
|
1919
|
+
this.router.setAuthorizeMiddleware(authorization);
|
|
1920
|
+
return this;
|
|
1921
|
+
}
|
|
1922
|
+
useSerialization() {
|
|
1923
|
+
return this;
|
|
1924
|
+
}
|
|
1925
|
+
useControllers(controllers) {
|
|
1926
|
+
if (Array.isArray(controllers)) {
|
|
1927
|
+
this.controllers = controllers;
|
|
1928
|
+
controllers.forEach((controller) => {
|
|
1929
|
+
if (!this.controllers.includes(controller)) this.controllers.push(controller);
|
|
1930
|
+
});
|
|
1931
|
+
} else {
|
|
1932
|
+
this.registerControllerAuto = true;
|
|
1933
|
+
if (controllers.path) this.registerControllerPath = controllers.path;
|
|
1934
|
+
}
|
|
1935
|
+
return this;
|
|
1936
|
+
}
|
|
1937
|
+
useStaticFiles(options) {
|
|
1938
|
+
this.app.register(requireStatic(), options);
|
|
1939
|
+
return this;
|
|
1940
|
+
}
|
|
1941
|
+
useHttps(options) {
|
|
1942
|
+
return this;
|
|
1943
|
+
}
|
|
1944
|
+
useGlobal(options) {
|
|
1945
|
+
if (options.cors) this.useCors(options.cors);
|
|
1946
|
+
if (options.openApi) this.useOpenApi(options.openApi);
|
|
1947
|
+
if (options.controllers) this.useControllers(options.controllers);
|
|
1948
|
+
if (options.middlewares) this.useMiddlewares(options.middlewares);
|
|
1949
|
+
if (options.authorization) this.useAuthorization(options.authorization);
|
|
1950
|
+
if (options.multipart) this.useMultipart(options.multipart);
|
|
1951
|
+
if (options.staticFiles) this.useStaticFiles(options.staticFiles);
|
|
1952
|
+
return this;
|
|
1953
|
+
}
|
|
1954
|
+
useSocketIo(options) {
|
|
1955
|
+
this._hasWebsocket = true;
|
|
1956
|
+
this.app.register(requireSocketIo(), options);
|
|
1957
|
+
return this;
|
|
1958
|
+
}
|
|
1959
|
+
useKnex(options) {
|
|
1960
|
+
try {
|
|
1961
|
+
const { DB } = (init_kenx_provider(), require_chunk.__toCommonJS(kenx_provider_exports));
|
|
1962
|
+
typedi.default.get(DB).init(options.config ?? options);
|
|
1963
|
+
} catch (e) {
|
|
1964
|
+
if (e.message?.includes("knex")) throw e;
|
|
1965
|
+
throw new Error("[Avleon] Failed to initialize Knex. Make sure knex and a database driver are installed.\nRun: npm install knex pg (or mysql2, sqlite3, etc.)");
|
|
1966
|
+
}
|
|
1967
|
+
return this;
|
|
1968
|
+
}
|
|
1969
|
+
mapFeatures() {
|
|
1970
|
+
this.isMapFeatures = true;
|
|
1971
|
+
return this;
|
|
1972
|
+
}
|
|
1973
|
+
isDevelopment() {
|
|
1974
|
+
return process.env.NODE_ENV === "development" || !process.env.NODE_ENV;
|
|
1975
|
+
}
|
|
1976
|
+
async _mapControllers() {
|
|
1977
|
+
const safeControllers = (this.controllers ?? []).filter((c) => c != null && typeof c === "function");
|
|
1978
|
+
if (safeControllers.length > 0) for (const controller of safeControllers) if (isApiController(controller)) await this.router.buildController(controller);
|
|
1979
|
+
else throw new SystemUseError("Not an API controller.");
|
|
1980
|
+
}
|
|
1981
|
+
_resolveControllerDir(dir) {
|
|
1982
|
+
const isTsNode = process.env.TS_NODE_DEV || process.env.TS_NODE_PROJECT || process[Symbol.for("ts-node.register.instance")];
|
|
1983
|
+
const controllerDir = node_path.default.join(process.cwd(), this.registerControllerPath);
|
|
1984
|
+
return isTsNode ? controllerDir : controllerDir.replace("src", "dist");
|
|
1985
|
+
}
|
|
1986
|
+
async autoControllers(controllersPath) {
|
|
1987
|
+
const conDir = this._resolveControllerDir(controllersPath);
|
|
1988
|
+
const isTsNode = process.env.TS_NODE_DEV || process.env.TS_NODE_PROJECT || process[Symbol.for("ts-node.register.instance")];
|
|
1989
|
+
try {
|
|
1990
|
+
const files = node_fs.default.readdirSync(conDir, {
|
|
1991
|
+
recursive: true,
|
|
1992
|
+
encoding: "utf-8"
|
|
1993
|
+
});
|
|
1994
|
+
for (const file of files) {
|
|
1995
|
+
if (/\.(test|spec|e2e-spec)\.(ts|js)$/.test(file)) continue;
|
|
1996
|
+
if (isTsNode ? file.endsWith(".ts") : file.endsWith(".js")) {
|
|
1997
|
+
const module = await import(node_path.default.join(conDir, file));
|
|
1998
|
+
for (const exported of Object.values(module)) if (typeof exported === "function" && isApiController(exported)) {
|
|
1999
|
+
if (!this.controllers.some((con) => exported.name === con.name)) this.controllers.push(exported);
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
} catch (e) {
|
|
2004
|
+
console.warn("Could not auto-register controllers from " + conDir, e);
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
_mapFeatures() {
|
|
2008
|
+
try {
|
|
2009
|
+
typedi.default.get("features");
|
|
2010
|
+
} catch {}
|
|
2011
|
+
}
|
|
2012
|
+
async initializeDatabase() {
|
|
2013
|
+
if (this.dataSourceOptions && this.dataSource) await this.dataSource.initialize();
|
|
2014
|
+
}
|
|
2015
|
+
handleSocket(socket) {
|
|
2016
|
+
try {
|
|
2017
|
+
const { SocketContextService } = (init_event_dispatcher(), require_chunk.__toCommonJS(event_dispatcher_exports));
|
|
2018
|
+
const { EventSubscriberRegistry } = (init_event_subscriber(), require_chunk.__toCommonJS(event_subscriber_exports));
|
|
2019
|
+
const { SocketIoServer } = (init_websocket(), require_chunk.__toCommonJS(websocket_exports));
|
|
2020
|
+
const contextService = typedi.default.get(SocketContextService);
|
|
2021
|
+
typedi.default.get(EventSubscriberRegistry).register(socket);
|
|
2022
|
+
const originalOn = socket.on.bind(socket);
|
|
2023
|
+
socket.on = (event, handler) => {
|
|
2024
|
+
return originalOn(event, (...args) => {
|
|
2025
|
+
contextService.run(socket, () => handler(...args));
|
|
2026
|
+
});
|
|
2027
|
+
};
|
|
2028
|
+
} catch (e) {
|
|
2029
|
+
console.warn("[Avleon] Socket handler error:", e);
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
mapGet(path = "", fn) {
|
|
2033
|
+
return this.router.mapGet(path, fn);
|
|
2034
|
+
}
|
|
2035
|
+
mapPost(path = "", fn) {
|
|
2036
|
+
return this.router.mapPost(path, fn);
|
|
2037
|
+
}
|
|
2038
|
+
mapPut(path = "", fn) {
|
|
2039
|
+
return this.router.mapPut(path, fn);
|
|
2040
|
+
}
|
|
2041
|
+
mapDelete(path = "", fn) {
|
|
2042
|
+
return this.router.mapDelete(path, fn);
|
|
2043
|
+
}
|
|
2044
|
+
async run(port = 4e3, fn) {
|
|
2045
|
+
if (this.alreadyRun) throw new SystemUseError("App already running");
|
|
2046
|
+
this.alreadyRun = true;
|
|
2047
|
+
if (this.hasSwagger) await this.initSwagger(this.globalSwaggerOptions);
|
|
2048
|
+
await this.initializeDatabase();
|
|
2049
|
+
if (this.isMapFeatures) this._mapFeatures();
|
|
2050
|
+
if (this.registerControllerAuto) await this.autoControllers();
|
|
2051
|
+
await this._mapControllers();
|
|
2052
|
+
this.router.processFunctionalRoutes();
|
|
2053
|
+
this.app.setErrorHandler((error, request, reply) => {
|
|
2054
|
+
const isDev = process.env.NODE_ENV === "development";
|
|
2055
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
2056
|
+
const requestInfo = `${request.method} ${request.url}`;
|
|
2057
|
+
if (error.statusCode === 400 || error.validation) {
|
|
2058
|
+
console.warn(`[${timestamp}] HTTP 400 ${requestInfo} — ${error.message}`);
|
|
2059
|
+
return reply.status(400).send({
|
|
2060
|
+
code: 400,
|
|
2061
|
+
status: "Error",
|
|
2062
|
+
message: error.message,
|
|
2063
|
+
errors: error.validation ?? void 0
|
|
2064
|
+
});
|
|
2065
|
+
}
|
|
2066
|
+
if (error instanceof BaseHttpException) {
|
|
2067
|
+
console.warn(`[${timestamp}] HTTP ${error.code} ${requestInfo} — ${error.message}`);
|
|
2068
|
+
return reply.status(error.code || 500).type("application/json").serializer((payload) => JSON.stringify(payload)).send({
|
|
2069
|
+
code: error.code,
|
|
2070
|
+
status: "Error",
|
|
2071
|
+
message: error.message,
|
|
2072
|
+
data: error.payload
|
|
2073
|
+
});
|
|
2074
|
+
}
|
|
2075
|
+
console.error(`\n❌ UNHANDLED ERROR @ ${timestamp}`);
|
|
2076
|
+
console.error(` ${requestInfo}`);
|
|
2077
|
+
console.error(` ${error.name}: ${error.message}`);
|
|
2078
|
+
if (error.stack) console.error(` Stack:\n${error.stack.split("\n").slice(1).map((l) => ` ${l.trim()}`).join("\n")}`);
|
|
2079
|
+
if (isDev) {
|
|
2080
|
+
console.error(` Params:`, JSON.stringify(request.params));
|
|
2081
|
+
console.error(` Query: `, JSON.stringify(request.query));
|
|
2082
|
+
console.error(` Body: `, JSON.stringify(request.body));
|
|
2083
|
+
}
|
|
2084
|
+
console.error("");
|
|
2085
|
+
return reply.status(500).send({
|
|
2086
|
+
code: 500,
|
|
2087
|
+
status: "Error",
|
|
2088
|
+
message: isDev ? error.message : "Internal Server Error",
|
|
2089
|
+
...isDev && {
|
|
2090
|
+
error: error.name,
|
|
2091
|
+
stack: error.stack?.split("\n").slice(0, 5)
|
|
2092
|
+
}
|
|
2093
|
+
});
|
|
2094
|
+
});
|
|
2095
|
+
await this.app.ready();
|
|
2096
|
+
if (this._hasWebsocket) {
|
|
2097
|
+
if (!this.app.io) throw new Error("Socket.IO not initialized. Make sure fastify-socket.io is registered correctly.");
|
|
2098
|
+
try {
|
|
2099
|
+
const { SocketIoServer } = (init_websocket(), require_chunk.__toCommonJS(websocket_exports));
|
|
2100
|
+
typedi.default.set(SocketIoServer, this.app.io);
|
|
2101
|
+
await this.app.io.on("connection", this.handleSocket.bind(this));
|
|
2102
|
+
} catch (e) {
|
|
2103
|
+
console.warn("[Avleon] WebSocket setup error:", e);
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
await this.app.listen({ port });
|
|
2107
|
+
console.log(`Application running on http://127.0.0.1:${port}`);
|
|
2108
|
+
if (fn) fn();
|
|
2109
|
+
}
|
|
2110
|
+
};
|
|
2111
|
+
//#endregion
|
|
2112
|
+
//#region src/core/testing.ts
|
|
2113
|
+
/**
|
|
2114
|
+
* @copyright 2024
|
|
2115
|
+
* @author Tareq Hossain
|
|
2116
|
+
* @email xtrinsic96@gmail.com
|
|
2117
|
+
* @url https://github.com/xtareq
|
|
2118
|
+
*/
|
|
2119
|
+
init_exceptions();
|
|
2120
|
+
init_system_exception();
|
|
2121
|
+
var AvleonTest = class AvleonTest {
|
|
2122
|
+
constructor() {
|
|
2123
|
+
process.env.NODE_ENV = "test";
|
|
2124
|
+
}
|
|
2125
|
+
static getController(controller, deps = []) {
|
|
2126
|
+
const paramTypes = Reflect.getMetadata("design:paramtypes", controller) || [];
|
|
2127
|
+
deps.forEach((dep, i) => {
|
|
2128
|
+
typedi.default.set(paramTypes[i], dep);
|
|
2129
|
+
});
|
|
2130
|
+
return typedi.default.get(controller);
|
|
2131
|
+
}
|
|
2132
|
+
static getProvider(service, deps = []) {
|
|
2133
|
+
const paramTypes = Reflect.getMetadata("design:paramtypes", service) || [];
|
|
2134
|
+
deps.forEach((dep, i) => {
|
|
2135
|
+
typedi.default.set(paramTypes[i], dep);
|
|
2136
|
+
});
|
|
2137
|
+
return typedi.default.get(service);
|
|
2138
|
+
}
|
|
2139
|
+
static createTestApplication(options) {
|
|
2140
|
+
const app = AvleonApplication.getInternalApp({ dataSourceOptions: options.dataSource ? options.dataSource : void 0 });
|
|
2141
|
+
if (options.controllers) app.useControllers(options.controllers);
|
|
2142
|
+
return AvleonTest.from(app);
|
|
2143
|
+
}
|
|
2144
|
+
static from(app) {
|
|
2145
|
+
try {
|
|
2146
|
+
app._mapControllers().catch((e) => console.error(e));
|
|
2147
|
+
app.app.setErrorHandler(async (error, req, res) => {
|
|
2148
|
+
if (error instanceof ValidationErrorException) return res.status(400).send({
|
|
2149
|
+
code: 400,
|
|
2150
|
+
error: "ValidationError",
|
|
2151
|
+
errors: error.message
|
|
2152
|
+
});
|
|
2153
|
+
return res.status(500).send(error);
|
|
2154
|
+
});
|
|
2155
|
+
return {
|
|
2156
|
+
get: async (url, options) => app.app.inject({
|
|
2157
|
+
method: "GET",
|
|
2158
|
+
url,
|
|
2159
|
+
...options
|
|
2160
|
+
}),
|
|
2161
|
+
post: async (url, options) => app.app.inject({
|
|
2162
|
+
method: "POST",
|
|
2163
|
+
url,
|
|
2164
|
+
...options
|
|
2165
|
+
}),
|
|
2166
|
+
put: async (url, options) => app.app.inject({
|
|
2167
|
+
method: "PUT",
|
|
2168
|
+
url,
|
|
2169
|
+
...options
|
|
2170
|
+
}),
|
|
2171
|
+
patch: async (url, options) => app.app.inject({
|
|
2172
|
+
method: "PATCH",
|
|
2173
|
+
url,
|
|
2174
|
+
...options
|
|
2175
|
+
}),
|
|
2176
|
+
delete: async (url, options) => app.app.inject({
|
|
2177
|
+
method: "DELETE",
|
|
2178
|
+
url,
|
|
2179
|
+
...options
|
|
2180
|
+
}),
|
|
2181
|
+
options: async (url, options) => app.app.inject({
|
|
2182
|
+
method: "OPTIONS",
|
|
2183
|
+
url,
|
|
2184
|
+
...options
|
|
2185
|
+
}),
|
|
2186
|
+
getController: (controller, deps = []) => {
|
|
2187
|
+
return AvleonTest.getController(controller, deps);
|
|
2188
|
+
}
|
|
2189
|
+
};
|
|
2190
|
+
} catch (error) {
|
|
2191
|
+
throw new SystemUseError("Can't get test appliction");
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
static clean() {
|
|
2195
|
+
typedi.default.reset();
|
|
2196
|
+
}
|
|
2197
|
+
};
|
|
2198
|
+
var Avleon = class {
|
|
2199
|
+
static createApplication() {
|
|
2200
|
+
return AvleonApplication.getApp();
|
|
2201
|
+
}
|
|
2202
|
+
static createTestApplication(options) {
|
|
2203
|
+
return AvleonTest.createTestApplication(options);
|
|
2204
|
+
}
|
|
2205
|
+
};
|
|
2206
|
+
//#endregion
|
|
2207
|
+
//#region src/response.ts
|
|
2208
|
+
/**
|
|
2209
|
+
* @copyright 2024
|
|
2210
|
+
* @author Tareq Hossain
|
|
2211
|
+
* @email xtrinsic96@gmail.com
|
|
2212
|
+
* @url https://github.com/xtareq
|
|
2213
|
+
*/
|
|
2214
|
+
var HttpResponse = class {
|
|
2215
|
+
static Ok(obj, s) {
|
|
2216
|
+
if (s) {
|
|
2217
|
+
const isPaginated = obj?.hasOwnProperty("total");
|
|
2218
|
+
const transformedData = (0, class_transformer.plainToInstance)(s, isPaginated ? obj.data : obj, {
|
|
2219
|
+
enableImplicitConversion: true,
|
|
2220
|
+
excludeExtraneousValues: true
|
|
2221
|
+
});
|
|
2222
|
+
return {
|
|
2223
|
+
message: "success",
|
|
2224
|
+
...isPaginated ? {
|
|
2225
|
+
...obj,
|
|
2226
|
+
data: (0, class_transformer.instanceToPlain)(transformedData)
|
|
2227
|
+
} : { data: (0, class_transformer.instanceToPlain)(transformedData) }
|
|
2228
|
+
};
|
|
2229
|
+
}
|
|
2230
|
+
return {
|
|
2231
|
+
message: "success",
|
|
2232
|
+
data: obj
|
|
2233
|
+
};
|
|
2234
|
+
}
|
|
2235
|
+
static Created(obj, s) {
|
|
2236
|
+
if (s) return {
|
|
2237
|
+
message: "created",
|
|
2238
|
+
data: (0, class_transformer.instanceToPlain)((0, class_transformer.plainToInstance)(s, obj, {
|
|
2239
|
+
enableImplicitConversion: true,
|
|
2240
|
+
excludeExtraneousValues: true
|
|
2241
|
+
}))
|
|
2242
|
+
};
|
|
2243
|
+
return {
|
|
2244
|
+
message: "created",
|
|
2245
|
+
data: obj
|
|
2246
|
+
};
|
|
2247
|
+
}
|
|
2248
|
+
static NoContent() {
|
|
2249
|
+
return {
|
|
2250
|
+
message: "no content",
|
|
2251
|
+
data: null
|
|
2252
|
+
};
|
|
2253
|
+
}
|
|
2254
|
+
};
|
|
2255
|
+
//#endregion
|
|
2256
|
+
//#region src/middleware.ts
|
|
2257
|
+
/**
|
|
2258
|
+
* @copyright 2024
|
|
2259
|
+
* @author Tareq Hossain
|
|
2260
|
+
* @email xtrinsic96@gmail.com
|
|
2261
|
+
* @url https://github.com/xtareq
|
|
2262
|
+
*/
|
|
2263
|
+
init_container();
|
|
2264
|
+
var AvleonMiddleware = class {};
|
|
2265
|
+
var AuthorizeMiddleware = class {};
|
|
2266
|
+
function CanAuthorize(target) {
|
|
2267
|
+
if (typeof target.prototype.authorize !== "function") throw new Error(`Class "${target.name}" must implement an "authorize" method.`);
|
|
2268
|
+
(0, typedi.Service)()(target);
|
|
2269
|
+
}
|
|
2270
|
+
function AppAuthorization(target) {
|
|
2271
|
+
if (typeof target.prototype.authorize !== "function") throw new Error(`Class "${target.name}" must implement an "authorize" method.`);
|
|
2272
|
+
(0, typedi.Service)()(target);
|
|
2273
|
+
}
|
|
2274
|
+
function Authorized(options = {}) {
|
|
2275
|
+
return function(target, propertyKey, descriptor) {
|
|
2276
|
+
if (propertyKey && descriptor) Reflect.defineMetadata(AUTHORIZATION_META_KEY, {
|
|
2277
|
+
authorize: true,
|
|
2278
|
+
options
|
|
2279
|
+
}, target.constructor, propertyKey);
|
|
2280
|
+
else Reflect.defineMetadata(AUTHORIZATION_META_KEY, {
|
|
2281
|
+
authorize: true,
|
|
2282
|
+
options
|
|
2283
|
+
}, target);
|
|
2284
|
+
};
|
|
2285
|
+
}
|
|
2286
|
+
function AppMiddleware(target) {
|
|
2287
|
+
if (typeof target.prototype.invoke !== "function") throw new Error(`Class "${target.name}" must implement an "invoke" method.`);
|
|
2288
|
+
(0, typedi.Service)()(target);
|
|
2289
|
+
}
|
|
2290
|
+
/**
|
|
2291
|
+
* A decorator function that applies one or more middleware to a class or a method.
|
|
2292
|
+
*
|
|
2293
|
+
* When applied to a class, the middleware are registered for the entire controller.
|
|
2294
|
+
* When applied to a method, the middleware are registered for that specific route.
|
|
2295
|
+
*
|
|
2296
|
+
* @param options - A single middleware instance/class or an array of middleware instances/classes to be applied.
|
|
2297
|
+
* @returns A decorator that registers the middleware metadata.
|
|
2298
|
+
*/
|
|
2299
|
+
function UseMiddleware(options) {
|
|
2300
|
+
return function(target, propertyKey, descriptor) {
|
|
2301
|
+
const normalizeMiddleware = (middleware) => typeof middleware === "function" ? new middleware() : middleware;
|
|
2302
|
+
const middlewareList = (Array.isArray(options) ? options : [options]).map(normalizeMiddleware);
|
|
2303
|
+
if (typeof target === "function" && !propertyKey) {
|
|
2304
|
+
const existingMiddlewares = Reflect.getMetadata("controller:middleware", target) || [];
|
|
2305
|
+
Reflect.defineMetadata("controller:middleware", [...existingMiddlewares, ...middlewareList], target);
|
|
2306
|
+
} else if (descriptor) {
|
|
2307
|
+
const existingMiddlewares = Reflect.getMetadata("route:middleware", target, propertyKey) || [];
|
|
2308
|
+
Reflect.defineMetadata("route:middleware", [...existingMiddlewares, ...middlewareList], target, propertyKey);
|
|
2309
|
+
}
|
|
2310
|
+
};
|
|
2311
|
+
}
|
|
2312
|
+
//#endregion
|
|
2313
|
+
//#region src/collection.ts
|
|
2314
|
+
/**
|
|
2315
|
+
* @copyright 2024
|
|
2316
|
+
* @author Tareq Hossain
|
|
2317
|
+
* @email xtrinsic96@gmail.com
|
|
2318
|
+
* @url https://github.com/xtareq
|
|
2319
|
+
*/
|
|
2320
|
+
init_exceptions();
|
|
2321
|
+
var BasicCollectionImpl = class BasicCollectionImpl {
|
|
2322
|
+
items;
|
|
2323
|
+
constructor(items) {
|
|
2324
|
+
this.items = items;
|
|
2325
|
+
}
|
|
2326
|
+
static from(items) {
|
|
2327
|
+
return new BasicCollectionImpl(items);
|
|
2328
|
+
}
|
|
2329
|
+
clear() {
|
|
2330
|
+
this.items = [];
|
|
2331
|
+
}
|
|
2332
|
+
find(predicate) {
|
|
2333
|
+
if (this.isFunction(predicate)) return this.items.filter(predicate);
|
|
2334
|
+
return Array.from(this.items);
|
|
2335
|
+
}
|
|
2336
|
+
async findAsync(predicate) {
|
|
2337
|
+
return Array.from(this.items);
|
|
2338
|
+
}
|
|
2339
|
+
_matches(item, where) {
|
|
2340
|
+
if ("$or" in where) return where.$or.some((cond) => this._matches(item, cond));
|
|
2341
|
+
if ("$and" in where) return where.$and.every((cond) => this._matches(item, cond));
|
|
2342
|
+
if ("$not" in where) return !this._matches(item, where.$not);
|
|
2343
|
+
return Object.entries(where).every(([key, condition]) => {
|
|
2344
|
+
const itemValue = item[key];
|
|
2345
|
+
if (condition && typeof condition === "object" && !Array.isArray(condition)) {
|
|
2346
|
+
const op = condition;
|
|
2347
|
+
if ("$in" in op && Array.isArray(op.$in)) return op.$in.includes(itemValue);
|
|
2348
|
+
}
|
|
2349
|
+
return itemValue === condition;
|
|
2350
|
+
});
|
|
2351
|
+
}
|
|
2352
|
+
findOne(predicate) {
|
|
2353
|
+
if (this.isFunction(predicate)) return this.items.find(predicate);
|
|
2354
|
+
const result = this.items.filter((item) => this._matches(item, predicate.where));
|
|
2355
|
+
if (result.length > 0) return result[0];
|
|
2356
|
+
}
|
|
2357
|
+
async findOneAsync(predicate) {
|
|
2358
|
+
if (this.isFunction(predicate)) return this.items.find(predicate);
|
|
2359
|
+
return this.items.find((item) => this._matches(item, predicate.where));
|
|
2360
|
+
}
|
|
2361
|
+
isFunction(value) {
|
|
2362
|
+
return typeof value === "function";
|
|
2363
|
+
}
|
|
2364
|
+
add(item) {
|
|
2365
|
+
this.items.push(item);
|
|
2366
|
+
return this.items[this.items.length - 1];
|
|
2367
|
+
}
|
|
2368
|
+
addAll(items) {
|
|
2369
|
+
this.items.push(...items);
|
|
2370
|
+
}
|
|
2371
|
+
update(predicate, updater) {
|
|
2372
|
+
const item = this.items.find(predicate);
|
|
2373
|
+
if (item) {
|
|
2374
|
+
const index = this.items.indexOf(item);
|
|
2375
|
+
this.items[index] = {
|
|
2376
|
+
...item,
|
|
2377
|
+
...updater
|
|
2378
|
+
};
|
|
2379
|
+
} else throw new NotFoundException("Item not found");
|
|
2380
|
+
}
|
|
2381
|
+
updateAll(predicate, updater) {
|
|
2382
|
+
for (let i = 0; i < this.items.length; i++) if (predicate(this.items[i])) this.items[i] = updater(this.items[i]);
|
|
2383
|
+
}
|
|
2384
|
+
delete(predicate) {
|
|
2385
|
+
const index = this.items.findIndex(predicate);
|
|
2386
|
+
if (index !== -1) this.items.splice(index, 1);
|
|
2387
|
+
}
|
|
2388
|
+
deleteAll(predicate) {
|
|
2389
|
+
this.items = this.items.filter((item) => !predicate(item));
|
|
2390
|
+
}
|
|
2391
|
+
max(key) {
|
|
2392
|
+
return Math.max(...this.items.map((item) => item[key]));
|
|
2393
|
+
}
|
|
2394
|
+
min(key) {
|
|
2395
|
+
return Math.max(...this.items.map((item) => item[key]));
|
|
2396
|
+
}
|
|
2397
|
+
sum(key) {
|
|
2398
|
+
return this.items.flatMap((x) => x[key]).reduce((sum, num) => sum + num, 0);
|
|
2399
|
+
}
|
|
2400
|
+
avg(key) {
|
|
2401
|
+
const nums = this.items.flatMap((x) => x[key]);
|
|
2402
|
+
return nums.reduce((sum, num) => sum + num, 0) / nums.length;
|
|
2403
|
+
}
|
|
2404
|
+
paginate(options) {
|
|
2405
|
+
const take = options?.take || 10;
|
|
2406
|
+
const skip = options?.skip || 0;
|
|
2407
|
+
const total = this.items.length;
|
|
2408
|
+
const data = this.items.slice(skip, take);
|
|
2409
|
+
return {
|
|
2410
|
+
total,
|
|
2411
|
+
totalPage: Math.ceil(total / take),
|
|
2412
|
+
next: skip + take < total ? skip + take : null,
|
|
2413
|
+
data
|
|
2414
|
+
};
|
|
2415
|
+
}
|
|
2416
|
+
getDeepValue(item, path) {
|
|
2417
|
+
if (typeof path !== "string") return item[path];
|
|
2418
|
+
return path.split(".").reduce((acc, key) => acc?.[key], item);
|
|
2419
|
+
}
|
|
2420
|
+
};
|
|
2421
|
+
var AsynchronousCollection = class AsynchronousCollection {
|
|
2422
|
+
model;
|
|
2423
|
+
repo;
|
|
2424
|
+
constructor(model) {
|
|
2425
|
+
this.model = model;
|
|
2426
|
+
}
|
|
2427
|
+
static fromRepository(model) {
|
|
2428
|
+
return new AsynchronousCollection(model);
|
|
2429
|
+
}
|
|
2430
|
+
getRepository() {
|
|
2431
|
+
if (!this.repo) {
|
|
2432
|
+
const dataSource = typedi.default.get("idatasource");
|
|
2433
|
+
console.log("datasource", dataSource);
|
|
2434
|
+
const repository = dataSource.getRepository(this.model);
|
|
2435
|
+
this.repo = repository;
|
|
2436
|
+
return repository;
|
|
2437
|
+
}
|
|
2438
|
+
return this.repo;
|
|
2439
|
+
}
|
|
2440
|
+
async paginate(options) {
|
|
2441
|
+
const take = options?.take || 10;
|
|
2442
|
+
const skip = options?.skip || 0;
|
|
2443
|
+
const [data, total] = await this.getRepository().findAndCount({
|
|
2444
|
+
take,
|
|
2445
|
+
skip
|
|
2446
|
+
});
|
|
2447
|
+
return {
|
|
2448
|
+
total,
|
|
2449
|
+
totalPage: Math.ceil(total / take),
|
|
2450
|
+
next: skip + take < total ? skip + take : null,
|
|
2451
|
+
prev: skip + take < total ? skip + take : null,
|
|
2452
|
+
data
|
|
2453
|
+
};
|
|
2454
|
+
}
|
|
2455
|
+
};
|
|
2456
|
+
var Collection = class {
|
|
2457
|
+
constructor() {}
|
|
2458
|
+
static from(items) {
|
|
2459
|
+
return BasicCollectionImpl.from(items);
|
|
2460
|
+
}
|
|
2461
|
+
static fromRepository(entity) {
|
|
2462
|
+
return AsynchronousCollection.fromRepository(entity).getRepository();
|
|
2463
|
+
}
|
|
2464
|
+
};
|
|
2465
|
+
function InjectRepository(model) {
|
|
2466
|
+
return function(object, propertyName, index) {
|
|
2467
|
+
let repo;
|
|
2468
|
+
try {
|
|
2469
|
+
typedi.default.registerHandler({
|
|
2470
|
+
object,
|
|
2471
|
+
propertyName,
|
|
2472
|
+
index,
|
|
2473
|
+
value: (containerInstance) => {
|
|
2474
|
+
repo = containerInstance.get("idatasource").getRepository(model).extend({ paginate: () => {} });
|
|
2475
|
+
repo.paginate = async function(options = {
|
|
2476
|
+
take: 10,
|
|
2477
|
+
skip: 0
|
|
2478
|
+
}) {
|
|
2479
|
+
const [data, total] = await this.findAndCount({
|
|
2480
|
+
take: options.take || 10,
|
|
2481
|
+
skip: options.skip || 0
|
|
2482
|
+
});
|
|
2483
|
+
return {
|
|
2484
|
+
total,
|
|
2485
|
+
totalPage: Math.ceil(total / (options.take || 10)),
|
|
2486
|
+
next: options.skip + options.take < total ? options.skip + options.take : null,
|
|
2487
|
+
data
|
|
2488
|
+
};
|
|
2489
|
+
};
|
|
2490
|
+
return repo;
|
|
2491
|
+
}
|
|
2492
|
+
});
|
|
2493
|
+
} catch (error) {
|
|
2494
|
+
if (error.name && error.name == "ServiceNotFoundError") console.log("Database didn't initialized.");
|
|
2495
|
+
}
|
|
2496
|
+
};
|
|
2497
|
+
}
|
|
2498
|
+
//#endregion
|
|
2499
|
+
//#region src/queue.ts
|
|
2500
|
+
var AvleonQueue = class {
|
|
2501
|
+
queue;
|
|
2502
|
+
handlerFn;
|
|
2503
|
+
constructor(name, adapter, handler) {
|
|
2504
|
+
this.name = name;
|
|
2505
|
+
this.adapter = adapter;
|
|
2506
|
+
this.queue = new bull.default(name || "default", adapter);
|
|
2507
|
+
this.handlerFn = handler;
|
|
2508
|
+
if (typeof this.handler === "function" && !this.handlerFn) this.handlerFn = (job) => this.handler(job);
|
|
2509
|
+
if (this.handlerFn) this.queue.process(this.handlerFn);
|
|
2510
|
+
}
|
|
2511
|
+
add(data, options) {
|
|
2512
|
+
return this.queue.add(data, options);
|
|
2513
|
+
}
|
|
2514
|
+
delay(data, delayMs, options) {
|
|
2515
|
+
return this.queue.add(data, {
|
|
2516
|
+
...options,
|
|
2517
|
+
delay: delayMs
|
|
2518
|
+
});
|
|
2519
|
+
}
|
|
2520
|
+
process(handler) {
|
|
2521
|
+
this.handlerFn = handler;
|
|
2522
|
+
this.queue.process(handler);
|
|
2523
|
+
}
|
|
2524
|
+
processConcurrent(concurrency, handler) {
|
|
2525
|
+
this.handlerFn = handler;
|
|
2526
|
+
this.queue.process(concurrency, handler);
|
|
2527
|
+
}
|
|
2528
|
+
getQueue() {
|
|
2529
|
+
return this.queue;
|
|
2530
|
+
}
|
|
2531
|
+
async clean(grace, status) {
|
|
2532
|
+
return this.queue.clean(grace, status);
|
|
2533
|
+
}
|
|
2534
|
+
async close() {
|
|
2535
|
+
await this.queue.close();
|
|
2536
|
+
}
|
|
2537
|
+
async pause() {
|
|
2538
|
+
await this.queue.pause();
|
|
2539
|
+
}
|
|
2540
|
+
async resume() {
|
|
2541
|
+
await this.queue.resume();
|
|
2542
|
+
}
|
|
2543
|
+
async getJob(jobId) {
|
|
2544
|
+
return this.queue.getJob(jobId);
|
|
2545
|
+
}
|
|
2546
|
+
async getJobs(types, start, end) {
|
|
2547
|
+
return this.queue.getJobs(types, start, end);
|
|
2548
|
+
}
|
|
2549
|
+
};
|
|
2550
|
+
function Queue(config) {
|
|
2551
|
+
return function(target) {
|
|
2552
|
+
const DecoratedClass = class extends target {
|
|
2553
|
+
constructor(...args) {
|
|
2554
|
+
super(config.name, config.adapter, config.handler);
|
|
2555
|
+
}
|
|
2556
|
+
};
|
|
2557
|
+
Object.defineProperty(DecoratedClass, "name", {
|
|
2558
|
+
value: target.name,
|
|
2559
|
+
writable: false
|
|
2560
|
+
});
|
|
2561
|
+
(0, typedi.Service)()(DecoratedClass);
|
|
2562
|
+
return DecoratedClass;
|
|
2563
|
+
};
|
|
2564
|
+
}
|
|
2565
|
+
//#endregion
|
|
2566
|
+
//#region src/file-storage.ts
|
|
2567
|
+
init_system_exception();
|
|
2568
|
+
init_http_exceptions();
|
|
2569
|
+
init_decorateMetadata();
|
|
2570
|
+
init_decorate();
|
|
2571
|
+
let FileStorage = class FileStorage {
|
|
2572
|
+
transformOptions = null;
|
|
2573
|
+
baseDir;
|
|
2574
|
+
maxFileSize = 50 * 1024 * 1024;
|
|
2575
|
+
constructor() {
|
|
2576
|
+
this.baseDir = node_path.default.join(process.cwd(), "public");
|
|
2577
|
+
this.ensureDirectoryExists(this.baseDir);
|
|
2578
|
+
}
|
|
2579
|
+
/**
|
|
2580
|
+
* Set transformation options for the next save operation
|
|
2581
|
+
*/
|
|
2582
|
+
transform(options) {
|
|
2583
|
+
this.transformOptions = options;
|
|
2584
|
+
return this;
|
|
2585
|
+
}
|
|
2586
|
+
async getUploadFile(fliePath) {
|
|
2587
|
+
return await node_fs.default.promises.readFile(node_path.default.join(this.baseDir, fliePath));
|
|
2588
|
+
}
|
|
2589
|
+
async download(filepath) {
|
|
2590
|
+
const filename = filepath.toString().split(/[\/\\]/).pop() || "file";
|
|
2591
|
+
return {
|
|
2592
|
+
download: true,
|
|
2593
|
+
stream: (0, node_fs.createReadStream)(filepath),
|
|
2594
|
+
filename
|
|
2595
|
+
};
|
|
2596
|
+
}
|
|
2597
|
+
async downloadAs(filepath, filename) {
|
|
2598
|
+
return {
|
|
2599
|
+
download: true,
|
|
2600
|
+
stream: (0, node_fs.createReadStream)(filepath),
|
|
2601
|
+
filename
|
|
2602
|
+
};
|
|
2603
|
+
}
|
|
2604
|
+
/**
|
|
2605
|
+
* Save a single file with optional transformations
|
|
2606
|
+
*/
|
|
2607
|
+
async save(f, options) {
|
|
2608
|
+
const opts = {
|
|
2609
|
+
overwrite: options?.overwrite ?? true,
|
|
2610
|
+
to: options?.to,
|
|
2611
|
+
saveAs: options?.saveAs
|
|
2612
|
+
};
|
|
2613
|
+
if (f.type !== "file") throw new SystemUseError("Invalid file type");
|
|
2614
|
+
try {
|
|
2615
|
+
const filename = opts.saveAs || f.filename;
|
|
2616
|
+
this.validateFilename(filename);
|
|
2617
|
+
const uploadDir = opts.to ? node_path.default.join(this.baseDir, opts.to) : this.baseDir;
|
|
2618
|
+
const fullPath = node_path.default.join(uploadDir, filename);
|
|
2619
|
+
if (!fullPath.startsWith(this.baseDir)) throw new SystemUseError("Invalid file path");
|
|
2620
|
+
if (!opts.overwrite && this.isFileExists(fullPath)) throw new SystemUseError("File already exists");
|
|
2621
|
+
await this.ensureDirectoryExists(uploadDir);
|
|
2622
|
+
let sourceStream;
|
|
2623
|
+
const savedFile = f;
|
|
2624
|
+
if (savedFile.filepath && !f.file) sourceStream = node_fs.default.createReadStream(savedFile.filepath);
|
|
2625
|
+
else if (f.file) sourceStream = f.file;
|
|
2626
|
+
else throw new SystemUseError("No file stream or filepath available");
|
|
2627
|
+
if (this.transformOptions && this.isImageFile(filename)) await this.processImage(sourceStream, fullPath);
|
|
2628
|
+
else await (0, node_stream_promises.pipeline)(sourceStream, node_fs.default.createWriteStream(fullPath));
|
|
2629
|
+
if (savedFile.filepath && node_fs.default.existsSync(savedFile.filepath)) this.removeFileSync(savedFile.filepath);
|
|
2630
|
+
if (opts.saveAs) f.filename = opts.saveAs;
|
|
2631
|
+
return {
|
|
2632
|
+
uploadPath: options?.to ? "/uploads/" + options.to + "/" + f.filename : "/uploads/" + f.filename,
|
|
2633
|
+
staticPath: options?.to ? "/static/" + options.to + "/" + f.filename : "/static/" + f.filename
|
|
2634
|
+
};
|
|
2635
|
+
} catch (err) {
|
|
2636
|
+
if (err instanceof SystemUseError || err instanceof InternalErrorException) throw err;
|
|
2637
|
+
console.error("File save error:", err);
|
|
2638
|
+
throw new SystemUseError("Failed to upload file");
|
|
2639
|
+
}
|
|
2640
|
+
}
|
|
2641
|
+
/**
|
|
2642
|
+
* Save multiple files
|
|
2643
|
+
*/
|
|
2644
|
+
async saveAll(files, options) {
|
|
2645
|
+
if (!files || files.length === 0) return [];
|
|
2646
|
+
const opts = {
|
|
2647
|
+
overwrite: options?.overwrite ?? true,
|
|
2648
|
+
to: options?.to
|
|
2649
|
+
};
|
|
2650
|
+
const uploadDir = opts.to ? node_path.default.join(this.baseDir, opts.to) : this.baseDir;
|
|
2651
|
+
await this.ensureDirectoryExists(uploadDir);
|
|
2652
|
+
const results = [];
|
|
2653
|
+
for (const f of files) try {
|
|
2654
|
+
this.validateFilename(f.filename);
|
|
2655
|
+
const fullPath = node_path.default.join(uploadDir, f.filename);
|
|
2656
|
+
if (!fullPath.startsWith(this.baseDir)) throw new SystemUseError(`Invalid file path for ${f.filename}`);
|
|
2657
|
+
if (!opts.overwrite && this.isFileExists(fullPath)) throw new SystemUseError(`File ${f.filename} already exists`);
|
|
2658
|
+
if (f.file) if (this.transformOptions && this.isImageFile(f.filename)) await this.processImage(f.file, fullPath);
|
|
2659
|
+
else await (0, node_stream_promises.pipeline)(f.file, node_fs.default.createWriteStream(fullPath));
|
|
2660
|
+
else {
|
|
2661
|
+
const fp = f;
|
|
2662
|
+
if (!fp.filepath) throw new SystemUseError(`No filepath for ${f.filename}`);
|
|
2663
|
+
if (this.transformOptions && this.isImageFile(f.filename)) await this.processImage(node_fs.default.createReadStream(fp.filepath), fullPath);
|
|
2664
|
+
else await (0, node_stream_promises.pipeline)(node_fs.default.createReadStream(fp.filepath), node_fs.default.createWriteStream(fullPath));
|
|
2665
|
+
this.removeFileSync(fp.filepath);
|
|
2666
|
+
}
|
|
2667
|
+
results.push({
|
|
2668
|
+
uploadPath: options?.to ? "/uploads/" + options.to + "/" + f.filename : "/uploads/" + f.filename,
|
|
2669
|
+
staticPath: options?.to ? "/static/" + options.to + "/" + f.filename : "/static/" + f.filename
|
|
2670
|
+
});
|
|
2671
|
+
} catch (error) {
|
|
2672
|
+
console.error(`Failed to save file ${f.filename}:`, error);
|
|
2673
|
+
throw new SystemUseError(`Failed to upload file ${f.filename}`);
|
|
2674
|
+
}
|
|
2675
|
+
return results;
|
|
2676
|
+
}
|
|
2677
|
+
/**
|
|
2678
|
+
* Remove a file from storage
|
|
2679
|
+
*/
|
|
2680
|
+
async remove(filepath) {
|
|
2681
|
+
const fullPath = node_path.default.join(this.baseDir, filepath);
|
|
2682
|
+
if (!fullPath.startsWith(this.baseDir)) throw new SystemUseError("Invalid file path");
|
|
2683
|
+
if (!this.isFileExists(fullPath)) throw new SystemUseError("File doesn't exist");
|
|
2684
|
+
try {
|
|
2685
|
+
node_fs.default.unlinkSync(fullPath);
|
|
2686
|
+
} catch (error) {
|
|
2687
|
+
console.error("File removal error:", error);
|
|
2688
|
+
throw new SystemUseError("Failed to remove file");
|
|
2689
|
+
}
|
|
2690
|
+
}
|
|
2691
|
+
/**
|
|
2692
|
+
* Process image with transformations using sharp
|
|
2693
|
+
*/
|
|
2694
|
+
async processImage(fileStream, outputPath) {
|
|
2695
|
+
try {
|
|
2696
|
+
let sharpPipeline = (await Promise.resolve().then(() => /* @__PURE__ */ require_chunk.__toESM(require("./lib-Bk8hUm06.cjs").default, 1))).default();
|
|
2697
|
+
if (this.transformOptions?.resize) sharpPipeline = sharpPipeline.resize(this.transformOptions.resize.width, this.transformOptions.resize.height, {
|
|
2698
|
+
fit: "inside",
|
|
2699
|
+
withoutEnlargement: true
|
|
2700
|
+
});
|
|
2701
|
+
if (this.transformOptions?.format) {
|
|
2702
|
+
const quality = this.transformOptions.quality || 80;
|
|
2703
|
+
switch (this.transformOptions.format) {
|
|
2704
|
+
case "jpeg":
|
|
2705
|
+
sharpPipeline = sharpPipeline.jpeg({ quality });
|
|
2706
|
+
break;
|
|
2707
|
+
case "png":
|
|
2708
|
+
sharpPipeline = sharpPipeline.png({ quality });
|
|
2709
|
+
break;
|
|
2710
|
+
case "webp":
|
|
2711
|
+
sharpPipeline = sharpPipeline.webp({ quality });
|
|
2712
|
+
break;
|
|
2713
|
+
case "avif":
|
|
2714
|
+
sharpPipeline = sharpPipeline.avif({ quality });
|
|
2715
|
+
break;
|
|
2716
|
+
}
|
|
2717
|
+
}
|
|
2718
|
+
await (0, node_stream_promises.pipeline)(fileStream, sharpPipeline, node_fs.default.createWriteStream(outputPath));
|
|
2719
|
+
} catch (error) {
|
|
2720
|
+
if (error.code === "MODULE_NOT_FOUND" && error.message.includes("sharp")) throw new InternalErrorException("sharp module not found. Please install sharp to use image transformations.");
|
|
2721
|
+
console.error("Image processing failed:", error);
|
|
2722
|
+
throw new InternalErrorException("Image processing failed");
|
|
2723
|
+
} finally {
|
|
2724
|
+
this.transformOptions = null;
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2727
|
+
/**
|
|
2728
|
+
* Helper methods
|
|
2729
|
+
*/
|
|
2730
|
+
isFileExists(fpath) {
|
|
2731
|
+
return node_fs.default.existsSync(fpath);
|
|
2732
|
+
}
|
|
2733
|
+
async ensureDirectoryExists(dirPath) {
|
|
2734
|
+
if (!node_fs.default.existsSync(dirPath)) node_fs.default.mkdirSync(dirPath, { recursive: true });
|
|
2735
|
+
}
|
|
2736
|
+
removeFileSync(filepath) {
|
|
2737
|
+
try {
|
|
2738
|
+
if (node_fs.default.existsSync(filepath)) node_fs.default.unlinkSync(filepath);
|
|
2739
|
+
} catch (error) {
|
|
2740
|
+
console.error("Failed to remove temp file:", error);
|
|
2741
|
+
}
|
|
2742
|
+
}
|
|
2743
|
+
isImageFile(filename) {
|
|
2744
|
+
const ext = node_path.default.extname(filename).toLowerCase();
|
|
2745
|
+
return [
|
|
2746
|
+
".jpg",
|
|
2747
|
+
".jpeg",
|
|
2748
|
+
".png",
|
|
2749
|
+
".webp",
|
|
2750
|
+
".avif",
|
|
2751
|
+
".gif",
|
|
2752
|
+
".bmp"
|
|
2753
|
+
].includes(ext);
|
|
2754
|
+
}
|
|
2755
|
+
validateFilename(filename) {
|
|
2756
|
+
if (!filename || filename.trim() === "") throw new SystemUseError("Invalid filename");
|
|
2757
|
+
if (filename.includes("..") || filename.includes("/") || filename.includes("\\")) throw new SystemUseError("Invalid filename: path traversal detected");
|
|
2758
|
+
if (filename.includes("\0")) throw new SystemUseError("Invalid filename: null byte detected");
|
|
2759
|
+
}
|
|
2760
|
+
};
|
|
2761
|
+
FileStorage = __decorate([AppService, __decorateMetadata("design:paramtypes", [])], FileStorage);
|
|
2762
|
+
//#endregion
|
|
2763
|
+
//#region src/environment-variables.ts
|
|
2764
|
+
/**
|
|
2765
|
+
* @copyright 2024
|
|
2766
|
+
* @author Tareq Hossain
|
|
2767
|
+
* @email xtrinsic96@gmail.com
|
|
2768
|
+
* @url https://github.com/xtareq
|
|
2769
|
+
*/
|
|
2770
|
+
init_system_exception();
|
|
2771
|
+
init_decorate();
|
|
2772
|
+
dotenv.default.config({
|
|
2773
|
+
path: node_path.default.join(process.cwd(), ".env"),
|
|
2774
|
+
quiet: true
|
|
2775
|
+
});
|
|
2776
|
+
let Environment = class Environment {
|
|
2777
|
+
/**
|
|
2778
|
+
* Parses the given `.env` file and merges it with `process.env`.
|
|
2779
|
+
* Values from `process.env` take precedence.
|
|
2780
|
+
*
|
|
2781
|
+
* @private
|
|
2782
|
+
* @param filePath - Absolute path to the `.env` file.
|
|
2783
|
+
* @returns A dictionary of merged environment variables.
|
|
2784
|
+
*/
|
|
2785
|
+
parseEnvFile(filePath) {
|
|
2786
|
+
try {
|
|
2787
|
+
if (!(0, node_fs.existsSync)(filePath)) return { ...process.env };
|
|
2788
|
+
const fileContent = node_fs.default.readFileSync(filePath, "utf8");
|
|
2789
|
+
return {
|
|
2790
|
+
...dotenv.default.parse(fileContent),
|
|
2791
|
+
...process.env
|
|
2792
|
+
};
|
|
2793
|
+
} catch (error) {
|
|
2794
|
+
console.error(`Error parsing .env file: ${error}`);
|
|
2795
|
+
return {};
|
|
2796
|
+
}
|
|
2797
|
+
}
|
|
2798
|
+
/**
|
|
2799
|
+
* Retrieves the value of the specified environment variable.
|
|
2800
|
+
*
|
|
2801
|
+
* @template T
|
|
2802
|
+
* @param key - The name of the environment variable.
|
|
2803
|
+
* @returns The value of the variable, or `undefined` if not found.
|
|
2804
|
+
*/
|
|
2805
|
+
get(key) {
|
|
2806
|
+
return this.parseEnvFile(node_path.default.join(process.cwd(), ".env"))[key];
|
|
2807
|
+
}
|
|
2808
|
+
/**
|
|
2809
|
+
* Retrieves the value of the specified environment variable.
|
|
2810
|
+
* Throws an error if the variable is not found.
|
|
2811
|
+
*
|
|
2812
|
+
* @template T
|
|
2813
|
+
* @param key - The name of the environment variable.
|
|
2814
|
+
* @throws {EnvironmentVariableNotFound} If the variable does not exist.
|
|
2815
|
+
* @returns The value of the variable.
|
|
2816
|
+
*/
|
|
2817
|
+
getOrThrow(key) {
|
|
2818
|
+
const parsedEnv = this.parseEnvFile(node_path.default.join(process.cwd(), ".env"));
|
|
2819
|
+
if (!Object(parsedEnv).hasOwnProperty(key)) throw new EnvironmentVariableNotFound(key);
|
|
2820
|
+
return parsedEnv[key];
|
|
2821
|
+
}
|
|
2822
|
+
/**
|
|
2823
|
+
* Retrieves all available environment variables,
|
|
2824
|
+
* with `process.env` values taking precedence over `.env` values.
|
|
2825
|
+
*
|
|
2826
|
+
* @template T
|
|
2827
|
+
* @returns An object containing all environment variables.
|
|
2828
|
+
*/
|
|
2829
|
+
getAll() {
|
|
2830
|
+
return this.parseEnvFile(node_path.default.join(process.cwd(), ".env"));
|
|
2831
|
+
}
|
|
2832
|
+
};
|
|
2833
|
+
Environment = __decorate([(0, typedi.Service)()], Environment);
|
|
2834
|
+
//#endregion
|
|
2835
|
+
//#region src/config.ts
|
|
2836
|
+
/**
|
|
2837
|
+
* @copyright 2024
|
|
2838
|
+
* @author Tareq Hossain
|
|
2839
|
+
* @email xtrinsic96@gmail.com
|
|
2840
|
+
* @url https://github.com/xtareq
|
|
2841
|
+
*/
|
|
2842
|
+
init_helpers();
|
|
2843
|
+
function AppConfig(target) {
|
|
2844
|
+
typedi.Container.set({
|
|
2845
|
+
id: target,
|
|
2846
|
+
type: target
|
|
2847
|
+
});
|
|
2848
|
+
}
|
|
2849
|
+
var AvleonConfig = class {
|
|
2850
|
+
get(configClass) {
|
|
2851
|
+
const instance = typedi.Container.get(configClass);
|
|
2852
|
+
if (!instance) throw new Error(`Configuration for ${configClass.name} not found.`);
|
|
2853
|
+
return instance.config(new Environment());
|
|
2854
|
+
}
|
|
2855
|
+
};
|
|
2856
|
+
function GetConfig(token) {
|
|
2857
|
+
if (typeof token === "function" && token.prototype != null && typeof token.prototype.config === "function") {
|
|
2858
|
+
const instance = typedi.Container.get(token);
|
|
2859
|
+
if (!instance) throw new Error(`Class "${token.name}" is not registered as a config.`);
|
|
2860
|
+
return instance.config(inject(Environment));
|
|
2861
|
+
}
|
|
2862
|
+
const stored = typedi.Container.get(token);
|
|
2863
|
+
if (!stored) throw new Error("Config object is not registered.");
|
|
2864
|
+
return stored;
|
|
2865
|
+
}
|
|
2866
|
+
function CreateConfig(token, callback) {
|
|
2867
|
+
let env;
|
|
2868
|
+
try {
|
|
2869
|
+
env = typedi.Container.get(Environment);
|
|
2870
|
+
} catch (error) {
|
|
2871
|
+
env = new Environment();
|
|
2872
|
+
}
|
|
2873
|
+
let config = callback(env);
|
|
2874
|
+
typedi.Container.set(token, config);
|
|
2875
|
+
}
|
|
2876
|
+
//#endregion
|
|
2877
|
+
//#region src/logger.ts
|
|
2878
|
+
init_decorateMetadata();
|
|
2879
|
+
init_decorate();
|
|
2880
|
+
let LoggerService = class LoggerService {
|
|
2881
|
+
logger;
|
|
2882
|
+
constructor() {
|
|
2883
|
+
this.logger = (0, pino.default)({
|
|
2884
|
+
level: process.env.LOG_LEVEL || "info",
|
|
2885
|
+
transport: {
|
|
2886
|
+
target: "pino-pretty",
|
|
2887
|
+
options: {
|
|
2888
|
+
translateTime: "SYS:standard",
|
|
2889
|
+
ignore: "pid,hostname"
|
|
2890
|
+
}
|
|
2891
|
+
}
|
|
2892
|
+
});
|
|
2893
|
+
}
|
|
2894
|
+
getLogger() {
|
|
2895
|
+
return this.logger;
|
|
2896
|
+
}
|
|
2897
|
+
info(message, obj) {
|
|
2898
|
+
if (obj) this.logger.info(obj, message);
|
|
2899
|
+
else this.logger.info(message);
|
|
2900
|
+
}
|
|
2901
|
+
error(message, obj) {
|
|
2902
|
+
if (obj) this.logger.error(obj, message);
|
|
2903
|
+
else this.logger.error(message);
|
|
2904
|
+
}
|
|
2905
|
+
warn(message, obj) {
|
|
2906
|
+
if (obj) this.logger.warn(obj, message);
|
|
2907
|
+
else this.logger.warn(message);
|
|
2908
|
+
}
|
|
2909
|
+
debug(message, obj) {
|
|
2910
|
+
if (obj) this.logger.debug(obj, message);
|
|
2911
|
+
else this.logger.debug(message);
|
|
2912
|
+
}
|
|
2913
|
+
fatal(message, obj) {
|
|
2914
|
+
if (obj) this.logger.fatal(obj, message);
|
|
2915
|
+
else this.logger.fatal(message);
|
|
2916
|
+
}
|
|
2917
|
+
trace(message, obj) {
|
|
2918
|
+
if (obj) this.logger.trace(obj, message);
|
|
2919
|
+
else this.logger.trace(message);
|
|
2920
|
+
}
|
|
2921
|
+
};
|
|
2922
|
+
LoggerService = __decorate([AppService, __decorateMetadata("design:paramtypes", [])], LoggerService);
|
|
2923
|
+
//#endregion
|
|
2924
|
+
//#region src/cache.ts
|
|
2925
|
+
var CacheManager = class {
|
|
2926
|
+
store = /* @__PURE__ */ new Map();
|
|
2927
|
+
tagsMap = /* @__PURE__ */ new Map();
|
|
2928
|
+
redis = null;
|
|
2929
|
+
constructor(redisInstance) {
|
|
2930
|
+
this.redis = redisInstance || null;
|
|
2931
|
+
}
|
|
2932
|
+
redisTagKey(tag) {
|
|
2933
|
+
return `cache-tags:${tag}`;
|
|
2934
|
+
}
|
|
2935
|
+
async get(key) {
|
|
2936
|
+
if (this.redis) {
|
|
2937
|
+
const val = await this.redis.get(key);
|
|
2938
|
+
return val ? JSON.parse(val) : null;
|
|
2939
|
+
}
|
|
2940
|
+
const cached = this.store.get(key);
|
|
2941
|
+
return cached ? cached.data : null;
|
|
2942
|
+
}
|
|
2943
|
+
async set(key, value, tags = [], ttl = 3600) {
|
|
2944
|
+
const entry = {
|
|
2945
|
+
data: value,
|
|
2946
|
+
timestamp: Date.now()
|
|
2947
|
+
};
|
|
2948
|
+
if (this.redis) {
|
|
2949
|
+
await this.redis.set(key, JSON.stringify(entry.data), "EX", ttl);
|
|
2950
|
+
for (const tag of tags) await this.redis.sadd(this.redisTagKey(tag), key);
|
|
2951
|
+
} else {
|
|
2952
|
+
this.store.set(key, entry);
|
|
2953
|
+
for (const tag of tags) {
|
|
2954
|
+
if (!this.tagsMap.has(tag)) this.tagsMap.set(tag, /* @__PURE__ */ new Set());
|
|
2955
|
+
this.tagsMap.get(tag).add(key);
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2958
|
+
}
|
|
2959
|
+
async delete(key) {
|
|
2960
|
+
if (this.redis) {
|
|
2961
|
+
await this.redis.del(key);
|
|
2962
|
+
const tagKeys = await this.redis.keys("cache-tags:*");
|
|
2963
|
+
for (const tagKey of tagKeys) await this.redis.srem(tagKey, key);
|
|
2964
|
+
} else {
|
|
2965
|
+
this.store.delete(key);
|
|
2966
|
+
for (const keys of this.tagsMap.values()) keys.delete(key);
|
|
2967
|
+
}
|
|
2968
|
+
}
|
|
2969
|
+
async invalidateTag(tag) {
|
|
2970
|
+
if (this.redis) {
|
|
2971
|
+
const tagKey = this.redisTagKey(tag);
|
|
2972
|
+
const keys = await this.redis.smembers(tagKey);
|
|
2973
|
+
if (keys.length) {
|
|
2974
|
+
await this.redis.del(...keys);
|
|
2975
|
+
await this.redis.del(tagKey);
|
|
2976
|
+
}
|
|
2977
|
+
} else {
|
|
2978
|
+
const keys = this.tagsMap.get(tag);
|
|
2979
|
+
if (keys) {
|
|
2980
|
+
for (const key of keys) this.store.delete(key);
|
|
2981
|
+
this.tagsMap.delete(tag);
|
|
2982
|
+
}
|
|
2983
|
+
}
|
|
2984
|
+
}
|
|
2985
|
+
};
|
|
2986
|
+
//#endregion
|
|
2987
|
+
//#region src/results.ts
|
|
2988
|
+
var Results = class {
|
|
2989
|
+
static code = 500;
|
|
2990
|
+
message = "Something going wrong";
|
|
2991
|
+
static Ok(data) {
|
|
2992
|
+
return new Ok(data);
|
|
2993
|
+
}
|
|
2994
|
+
static NoContent() {
|
|
2995
|
+
this.code = 204;
|
|
2996
|
+
}
|
|
2997
|
+
static OkStream() {}
|
|
2998
|
+
static NotFound(message) {
|
|
2999
|
+
return new NotFound(message);
|
|
3000
|
+
}
|
|
3001
|
+
};
|
|
3002
|
+
var Ok = class {
|
|
3003
|
+
constructor(data) {
|
|
3004
|
+
this.data = data;
|
|
3005
|
+
}
|
|
3006
|
+
};
|
|
3007
|
+
var NotFound = class {
|
|
3008
|
+
constructor(message) {
|
|
3009
|
+
this.message = message;
|
|
3010
|
+
}
|
|
3011
|
+
};
|
|
3012
|
+
//#endregion
|
|
3013
|
+
//#region src/index.ts
|
|
3014
|
+
/**
|
|
3015
|
+
* @copyright 2024
|
|
3016
|
+
* @author Tareq Hossain
|
|
3017
|
+
* @email xtrinsic96@gmail.com
|
|
3018
|
+
* @url https://github.com/xtareq
|
|
3019
|
+
*/
|
|
3020
|
+
init_helpers();
|
|
3021
|
+
init_exceptions();
|
|
3022
|
+
init_container();
|
|
3023
|
+
init_kenx_provider();
|
|
3024
|
+
init_event_dispatcher();
|
|
3025
|
+
init_event_subscriber();
|
|
3026
|
+
init_container();
|
|
3027
|
+
const GetSchema = generateSwaggerSchema;
|
|
3028
|
+
const GetObjectSchema = CreateSwaggerObjectSchema;
|
|
3029
|
+
const OpenApiOk = (args1) => {
|
|
3030
|
+
return OpenApiResponse$1(200, args1, "Success");
|
|
3031
|
+
};
|
|
3032
|
+
const OpenApiResponse = OpenApiResponse$1;
|
|
3033
|
+
const OpenApiProperty = OpenApiProperty$1;
|
|
3034
|
+
//#endregion
|
|
3035
|
+
exports.API_CONTROLLER_METADATA_KEY = API_CONTROLLER_METADATA_KEY;
|
|
3036
|
+
exports.AUTHORIZATION_META_KEY = AUTHORIZATION_META_KEY;
|
|
3037
|
+
exports.All = All;
|
|
3038
|
+
exports.ApiController = ApiController;
|
|
3039
|
+
exports.AppAuthorization = AppAuthorization;
|
|
3040
|
+
exports.AppConfig = AppConfig;
|
|
3041
|
+
exports.AppMiddleware = AppMiddleware;
|
|
3042
|
+
exports.AppService = AppService;
|
|
3043
|
+
exports.AuthUser = AuthUser;
|
|
3044
|
+
exports.AuthorizeMiddleware = AuthorizeMiddleware;
|
|
3045
|
+
exports.Authorized = Authorized;
|
|
3046
|
+
exports.Avleon = Avleon;
|
|
3047
|
+
exports.AvleonApplication = AvleonApplication;
|
|
3048
|
+
exports.AvleonConfig = AvleonConfig;
|
|
3049
|
+
exports.AvleonContainer = container_default;
|
|
3050
|
+
exports.AvleonMiddleware = AvleonMiddleware;
|
|
3051
|
+
exports.AvleonQueue = AvleonQueue;
|
|
3052
|
+
exports.AvleonRequest = AvleonRequest;
|
|
3053
|
+
exports.AvleonTest = AvleonTest;
|
|
3054
|
+
exports.BadRequestException = BadRequestException;
|
|
3055
|
+
exports.BaseHttpException = BaseHttpException;
|
|
3056
|
+
exports.Body = Body;
|
|
3057
|
+
exports.CONTROLLER_META_KEY = CONTROLLER_META_KEY;
|
|
3058
|
+
exports.CacheManager = CacheManager;
|
|
3059
|
+
exports.CanAuthorize = CanAuthorize;
|
|
3060
|
+
exports.Collection = Collection;
|
|
3061
|
+
exports.CreateConfig = CreateConfig;
|
|
3062
|
+
exports.CreateSwaggerObjectSchema = CreateSwaggerObjectSchema;
|
|
3063
|
+
exports.DATASOURCE_META_KEY = DATASOURCE_META_KEY;
|
|
3064
|
+
Object.defineProperty(exports, "DB", {
|
|
3065
|
+
enumerable: true,
|
|
3066
|
+
get: function() {
|
|
3067
|
+
return DB;
|
|
3068
|
+
}
|
|
3069
|
+
});
|
|
3070
|
+
exports.Delete = Delete;
|
|
3071
|
+
exports.Dispatch = Dispatch;
|
|
3072
|
+
Object.defineProperty(exports, "Environment", {
|
|
3073
|
+
enumerable: true,
|
|
3074
|
+
get: function() {
|
|
3075
|
+
return Environment;
|
|
3076
|
+
}
|
|
3077
|
+
});
|
|
3078
|
+
Object.defineProperty(exports, "EventDispatcher", {
|
|
3079
|
+
enumerable: true,
|
|
3080
|
+
get: function() {
|
|
3081
|
+
return EventDispatcher;
|
|
3082
|
+
}
|
|
3083
|
+
});
|
|
3084
|
+
Object.defineProperty(exports, "EventSubscriberRegistry", {
|
|
3085
|
+
enumerable: true,
|
|
3086
|
+
get: function() {
|
|
3087
|
+
return EventSubscriberRegistry;
|
|
3088
|
+
}
|
|
3089
|
+
});
|
|
3090
|
+
exports.FEATURE_KEY = FEATURE_KEY;
|
|
3091
|
+
Object.defineProperty(exports, "FileStorage", {
|
|
3092
|
+
enumerable: true,
|
|
3093
|
+
get: function() {
|
|
3094
|
+
return FileStorage;
|
|
3095
|
+
}
|
|
3096
|
+
});
|
|
3097
|
+
exports.ForbiddenException = ForbiddenException;
|
|
3098
|
+
exports.Get = Get;
|
|
3099
|
+
exports.GetConfig = GetConfig;
|
|
3100
|
+
exports.GetObjectSchema = GetObjectSchema;
|
|
3101
|
+
exports.GetSchema = GetSchema;
|
|
3102
|
+
exports.Header = Header;
|
|
3103
|
+
exports.Helper = Helper;
|
|
3104
|
+
exports.HttpExceptions = HttpExceptions;
|
|
3105
|
+
exports.HttpResponse = HttpResponse;
|
|
3106
|
+
exports.InjectRepository = InjectRepository;
|
|
3107
|
+
exports.InternalErrorException = InternalErrorException;
|
|
3108
|
+
Object.defineProperty(exports, "LoggerService", {
|
|
3109
|
+
enumerable: true,
|
|
3110
|
+
get: function() {
|
|
3111
|
+
return LoggerService;
|
|
3112
|
+
}
|
|
3113
|
+
});
|
|
3114
|
+
exports.NotFound = NotFound;
|
|
3115
|
+
exports.NotFoundException = NotFoundException;
|
|
3116
|
+
exports.Ok = Ok;
|
|
3117
|
+
exports.OpenApi = OpenApi;
|
|
3118
|
+
exports.OpenApiOk = OpenApiOk;
|
|
3119
|
+
exports.OpenApiProperty = OpenApiProperty;
|
|
3120
|
+
exports.OpenApiResponse = OpenApiResponse;
|
|
3121
|
+
exports.OpenApiSchema = OpenApiSchema;
|
|
3122
|
+
exports.Options = Options;
|
|
3123
|
+
exports.PARAM_META_KEY = PARAM_META_KEY;
|
|
3124
|
+
exports.Param = Param;
|
|
3125
|
+
exports.Patch = Patch;
|
|
3126
|
+
exports.Post = Post;
|
|
3127
|
+
exports.Private = Private;
|
|
3128
|
+
exports.Put = Put;
|
|
3129
|
+
exports.QUERY_META_KEY = QUERY_META_KEY;
|
|
3130
|
+
exports.Query = Query;
|
|
3131
|
+
exports.Queue = Queue;
|
|
3132
|
+
exports.REQUEST_BODY_FILES_KEY = REQUEST_BODY_FILES_KEY;
|
|
3133
|
+
exports.REQUEST_BODY_FILE_KEY = REQUEST_BODY_FILE_KEY;
|
|
3134
|
+
exports.REQUEST_BODY_META_KEY = REQUEST_BODY_META_KEY;
|
|
3135
|
+
exports.REQUEST_HEADER_META_KEY = REQUEST_HEADER_META_KEY;
|
|
3136
|
+
exports.REQUEST_METADATA_KEY = REQUEST_METADATA_KEY;
|
|
3137
|
+
exports.REQUEST_USER_META_KEY = REQUEST_USER_META_KEY;
|
|
3138
|
+
exports.ROUTE_META_KEY = ROUTE_META_KEY;
|
|
3139
|
+
exports.Results = Results;
|
|
3140
|
+
exports.Route = Route;
|
|
3141
|
+
Object.defineProperty(exports, "SocketContextService", {
|
|
3142
|
+
enumerable: true,
|
|
3143
|
+
get: function() {
|
|
3144
|
+
return SocketContextService;
|
|
3145
|
+
}
|
|
3146
|
+
});
|
|
3147
|
+
exports.Subscribe = Subscribe;
|
|
3148
|
+
exports.UnauthorizedException = UnauthorizedException;
|
|
3149
|
+
exports.UseMiddleware = UseMiddleware;
|
|
3150
|
+
exports.Utility = Utility;
|
|
3151
|
+
exports.ValidationErrorException = ValidationErrorException;
|
|
3152
|
+
exports.Validator = Validator;
|
|
3153
|
+
exports.autoCast = autoCast;
|
|
3154
|
+
exports.createControllerDecorator = createControllerDecorator;
|
|
3155
|
+
exports.exclude = exclude;
|
|
3156
|
+
exports.extrctParamFromUrl = extrctParamFromUrl;
|
|
3157
|
+
exports.findDuplicates = findDuplicates;
|
|
3158
|
+
exports.formatUrl = formatUrl;
|
|
3159
|
+
exports.generateClassSchema = generateClassSchema;
|
|
3160
|
+
exports.generateSwaggerSchema = generateSwaggerSchema;
|
|
3161
|
+
exports.getDataType = getDataType;
|
|
3162
|
+
exports.getLineNumber = getLineNumber;
|
|
3163
|
+
exports.getPrivateChannelResolver = getPrivateChannelResolver;
|
|
3164
|
+
exports.getRegisteredControllers = getRegisteredControllers;
|
|
3165
|
+
exports.getRegisteredServices = getRegisteredServices;
|
|
3166
|
+
exports.getSocketSubscribers = getSocketSubscribers;
|
|
3167
|
+
exports.inject = inject;
|
|
3168
|
+
exports.isApiController = isApiController;
|
|
3169
|
+
exports.isClassValidator = isClassValidator;
|
|
3170
|
+
exports.isClassValidatorClass = isClassValidatorClass;
|
|
3171
|
+
exports.isConstructor = isConstructor;
|
|
3172
|
+
exports.isPrivate = isPrivate;
|
|
3173
|
+
exports.isValidJsonString = isValidJsonString;
|
|
3174
|
+
exports.isValidType = isValidType;
|
|
3175
|
+
exports.jsonToInstance = jsonToInstance;
|
|
3176
|
+
exports.jsonToJs = jsonToJs;
|
|
3177
|
+
exports.normalizeParamsToJsonSchema = normalizeParamsToJsonSchema;
|
|
3178
|
+
exports.normalizePath = normalizePath;
|
|
3179
|
+
exports.normalizeQueryDeep = normalizeQueryDeep;
|
|
3180
|
+
exports.parsedPath = parsedPath;
|
|
3181
|
+
exports.pick = pick;
|
|
3182
|
+
exports.registerController = registerController;
|
|
3183
|
+
exports.registerDataSource = registerDataSource;
|
|
3184
|
+
exports.registerKnex = registerKnex;
|
|
3185
|
+
exports.registerService = registerService;
|
|
3186
|
+
exports.registerSocketSubscriber = registerSocketSubscriber;
|
|
3187
|
+
exports.sleep = sleep;
|
|
3188
|
+
exports.transformObjectByInstanceToObject = transformObjectByInstanceToObject;
|
|
3189
|
+
exports.uuid = uuid;
|
|
3190
|
+
exports.validateObjectByInstance = validateObjectByInstance;
|
|
3191
|
+
exports.validateOrThrow = validateOrThrow;
|
|
3192
|
+
exports.validateRequestBody = validateRequestBody;
|
|
3193
|
+
|
|
3194
|
+
//# sourceMappingURL=index.cjs.map
|