@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/core/router.js
DELETED
|
@@ -1,520 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.AvleonRouter = void 0;
|
|
40
|
-
/**
|
|
41
|
-
* @copyright 2024
|
|
42
|
-
* @author Tareq Hossain
|
|
43
|
-
* @email xtrinsic96@gmail.com
|
|
44
|
-
* @url https://github.com/xtareq
|
|
45
|
-
*/
|
|
46
|
-
const helpers_1 = require("../helpers");
|
|
47
|
-
const typedi_1 = __importDefault(require("typedi"));
|
|
48
|
-
const container_1 = __importStar(require("../container"));
|
|
49
|
-
const exceptions_1 = require("../exceptions");
|
|
50
|
-
const controller_1 = require("../controller");
|
|
51
|
-
const mime_1 = __importDefault(require("mime"));
|
|
52
|
-
const stream_1 = __importDefault(require("stream"));
|
|
53
|
-
// ---------------------------------------------------------------------------
|
|
54
|
-
// Helpers
|
|
55
|
-
// ---------------------------------------------------------------------------
|
|
56
|
-
/**
|
|
57
|
-
* Normalize a flat params/query map like:
|
|
58
|
-
* { id: { type: "string", example: "abc-123" } }
|
|
59
|
-
* into a valid AJV/JSON Schema object:
|
|
60
|
-
* { type: "object", properties: { id: { type: "string", example: "abc-123" } } }
|
|
61
|
-
*/
|
|
62
|
-
function normalizeParamsToJsonSchema(map, requiredKeys = []) {
|
|
63
|
-
const properties = {};
|
|
64
|
-
for (const [key, val] of Object.entries(map)) {
|
|
65
|
-
const { required, ...rest } = val;
|
|
66
|
-
properties[key] = { type: "string", ...rest };
|
|
67
|
-
}
|
|
68
|
-
const schema = { type: "object", properties };
|
|
69
|
-
if (requiredKeys.length > 0)
|
|
70
|
-
schema.required = requiredKeys;
|
|
71
|
-
return schema;
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Take a raw @OpenApi schema object and return a Fastify-compatible
|
|
75
|
-
* route schema — normalizing params, query → querystring, and headers.
|
|
76
|
-
*/
|
|
77
|
-
function buildRouteSchema(raw) {
|
|
78
|
-
const schema = { ...raw };
|
|
79
|
-
// ✅ Normalize path params
|
|
80
|
-
if (raw.params && typeof raw.params === "object" && !raw.params.type) {
|
|
81
|
-
const required = Object.entries(raw.params)
|
|
82
|
-
.filter(([, v]) => v.required !== false)
|
|
83
|
-
.map(([k]) => k);
|
|
84
|
-
schema.params = normalizeParamsToJsonSchema(raw.params, required);
|
|
85
|
-
}
|
|
86
|
-
// ✅ Normalize query → Fastify uses "querystring", not "query"
|
|
87
|
-
if (raw.query && typeof raw.query === "object" && !raw.query.type) {
|
|
88
|
-
const required = Object.entries(raw.query)
|
|
89
|
-
.filter(([, v]) => v.required === true)
|
|
90
|
-
.map(([k]) => k);
|
|
91
|
-
schema.querystring = normalizeParamsToJsonSchema(raw.query, required);
|
|
92
|
-
delete schema.query;
|
|
93
|
-
}
|
|
94
|
-
// ✅ Normalize headers
|
|
95
|
-
if (raw.headers && typeof raw.headers === "object" && !raw.headers.type) {
|
|
96
|
-
schema.headers = normalizeParamsToJsonSchema(raw.headers);
|
|
97
|
-
}
|
|
98
|
-
return schema;
|
|
99
|
-
}
|
|
100
|
-
// ---------------------------------------------------------------------------
|
|
101
|
-
// Router
|
|
102
|
-
// ---------------------------------------------------------------------------
|
|
103
|
-
class AvleonRouter {
|
|
104
|
-
constructor(app) {
|
|
105
|
-
this.routeSet = new Set();
|
|
106
|
-
this.metaCache = new Map();
|
|
107
|
-
this.middlewares = new Map();
|
|
108
|
-
this.rMap = new Map();
|
|
109
|
-
this.app = app;
|
|
110
|
-
}
|
|
111
|
-
setAuthorizeMiddleware(middleware) {
|
|
112
|
-
this.authorizeMiddleware = middleware;
|
|
113
|
-
}
|
|
114
|
-
registerMiddleware(name, instance) {
|
|
115
|
-
this.middlewares.set(name, instance);
|
|
116
|
-
}
|
|
117
|
-
_processMeta(prototype, method) {
|
|
118
|
-
const cacheKey = `${prototype.constructor.name}_${method}`;
|
|
119
|
-
if (this.metaCache.has(cacheKey)) {
|
|
120
|
-
return this.metaCache.get(cacheKey);
|
|
121
|
-
}
|
|
122
|
-
const meta = {
|
|
123
|
-
request: Reflect.getMetadata(controller_1.REQUEST_METADATA_KEY, prototype, method) || [],
|
|
124
|
-
params: Reflect.getMetadata(container_1.PARAM_META_KEY, prototype, method) || [],
|
|
125
|
-
query: Reflect.getMetadata(container_1.QUERY_META_KEY, prototype, method) || [],
|
|
126
|
-
body: Reflect.getMetadata(container_1.REQUEST_BODY_META_KEY, prototype, method) || [],
|
|
127
|
-
file: Reflect.getMetadata(container_1.REQUEST_BODY_FILE_KEY, prototype, method) || [],
|
|
128
|
-
files: Reflect.getMetadata(container_1.REQUEST_BODY_FILES_KEY, prototype, method) || [],
|
|
129
|
-
headers: Reflect.getMetadata(container_1.REQUEST_HEADER_META_KEY, prototype, method) || [],
|
|
130
|
-
currentUser: Reflect.getMetadata(container_1.REQUEST_USER_META_KEY, prototype, method) || [],
|
|
131
|
-
};
|
|
132
|
-
this.metaCache.set(cacheKey, meta);
|
|
133
|
-
return meta;
|
|
134
|
-
}
|
|
135
|
-
executeMiddlewares(target, propertyKey) {
|
|
136
|
-
const classMiddlewares = Reflect.getMetadata("controller:middleware", target.constructor) || [];
|
|
137
|
-
const methodMiddlewares = propertyKey
|
|
138
|
-
? Reflect.getMetadata("route:middleware", target, propertyKey) || []
|
|
139
|
-
: [];
|
|
140
|
-
return [...classMiddlewares, ...methodMiddlewares];
|
|
141
|
-
}
|
|
142
|
-
async buildController(controller) {
|
|
143
|
-
var _a, _b;
|
|
144
|
-
const ctrl = typedi_1.default.get(controller);
|
|
145
|
-
const controllerMeta = Reflect.getMetadata(container_1.CONTROLLER_META_KEY, ctrl.constructor);
|
|
146
|
-
if (!controllerMeta)
|
|
147
|
-
return;
|
|
148
|
-
const prototype = Object.getPrototypeOf(ctrl);
|
|
149
|
-
const methods = Object.getOwnPropertyNames(prototype).filter((name) => name !== "constructor");
|
|
150
|
-
const tag = ctrl.constructor.name.replace("Controller", "");
|
|
151
|
-
const swaggerControllerMeta = Reflect.getMetadata("controller:openapi", ctrl.constructor) || {};
|
|
152
|
-
const authClsMeata = Reflect.getMetadata(container_1.AUTHORIZATION_META_KEY, ctrl.constructor) || { authorize: false, options: undefined };
|
|
153
|
-
for await (const method of methods) {
|
|
154
|
-
const methodMeta = Reflect.getMetadata(container_1.ROUTE_META_KEY, prototype, method);
|
|
155
|
-
if (!methodMeta)
|
|
156
|
-
continue;
|
|
157
|
-
const methodmetaOptions = {
|
|
158
|
-
method: methodMeta.method.toLowerCase(),
|
|
159
|
-
path: (0, helpers_1.formatUrl)(controllerMeta.path + methodMeta.path),
|
|
160
|
-
};
|
|
161
|
-
const routeKey = `${methodmetaOptions.method}:${methodmetaOptions.path}`;
|
|
162
|
-
if (!this.routeSet.has(routeKey)) {
|
|
163
|
-
this.routeSet.add(routeKey);
|
|
164
|
-
}
|
|
165
|
-
const classMiddlewares = this.executeMiddlewares(ctrl, method);
|
|
166
|
-
const swaggerMeta = Reflect.getMetadata("route:openapi", prototype, method) || {};
|
|
167
|
-
const authClsMethodMeata = Reflect.getMetadata(container_1.AUTHORIZATION_META_KEY, ctrl.constructor, method) || { authorize: false, options: undefined };
|
|
168
|
-
const allMeta = this._processMeta(prototype, method);
|
|
169
|
-
// --- Build body schema from @Body() decorator ---
|
|
170
|
-
let bodySchema = null;
|
|
171
|
-
allMeta.body.forEach((r) => {
|
|
172
|
-
if (r.schema) {
|
|
173
|
-
bodySchema = { ...r.schema };
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
// --- Build base schema (single declaration) ---
|
|
177
|
-
let schema = { ...swaggerControllerMeta, ...swaggerMeta, tags: [tag] };
|
|
178
|
-
// Apply body schema if not manually set in @OpenApi
|
|
179
|
-
if (!swaggerMeta.body && bodySchema) {
|
|
180
|
-
schema = { ...schema, body: bodySchema };
|
|
181
|
-
}
|
|
182
|
-
// ✅ Auto-detect querystring from @Query() DTO if not manually set in @OpenApi
|
|
183
|
-
if (!swaggerMeta.query && !schema.querystring) {
|
|
184
|
-
for (const queryMeta of allMeta.query) {
|
|
185
|
-
if (queryMeta.validatorClass && queryMeta.schema) {
|
|
186
|
-
schema.querystring = queryMeta.schema;
|
|
187
|
-
break;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
// ✅ Auto-detect body from @Body() DTO if still not set
|
|
192
|
-
if (!swaggerMeta.body && !bodySchema) {
|
|
193
|
-
for (const bodyMeta of allMeta.body) {
|
|
194
|
-
if (bodyMeta.validatorClass && bodyMeta.schema) {
|
|
195
|
-
schema = { ...schema, body: bodyMeta.schema };
|
|
196
|
-
break;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
const routePath = methodmetaOptions.path === "" ? "/" : methodmetaOptions.path;
|
|
201
|
-
// --- Handle multipart ---
|
|
202
|
-
const isMultipart = ((_a = schema === null || schema === void 0 ? void 0 : schema.consumes) === null || _a === void 0 ? void 0 : _a.includes("multipart/form-data")) ||
|
|
203
|
-
Object.values(((_b = schema === null || schema === void 0 ? void 0 : schema.body) === null || _b === void 0 ? void 0 : _b.properties) || {}).some((p) => p.format === "binary");
|
|
204
|
-
if (isMultipart) {
|
|
205
|
-
schema.consumes = ["multipart/form-data"];
|
|
206
|
-
if (!schema.body) {
|
|
207
|
-
schema.body = { type: "object", properties: {} };
|
|
208
|
-
}
|
|
209
|
-
for (const param of allMeta.body) {
|
|
210
|
-
if (param.type === "route:file") {
|
|
211
|
-
schema.body.properties[param.key] = {
|
|
212
|
-
type: "string",
|
|
213
|
-
format: "binary",
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
else {
|
|
217
|
-
schema.body.properties[param.key] = { type: param.dataType };
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
// ✅ Normalize params/query/headers into valid JSON Schema
|
|
222
|
-
const routeSchema = buildRouteSchema(schema);
|
|
223
|
-
this.app.route({
|
|
224
|
-
url: routePath,
|
|
225
|
-
method: methodmetaOptions.method.toUpperCase(),
|
|
226
|
-
schema: routeSchema,
|
|
227
|
-
attachValidation: isMultipart,
|
|
228
|
-
handler: async (req, res) => {
|
|
229
|
-
let reqClone = req;
|
|
230
|
-
if (authClsMeata.authorize && this.authorizeMiddleware) {
|
|
231
|
-
const cls = container_1.default.get(this.authorizeMiddleware);
|
|
232
|
-
await cls.authorize(reqClone, authClsMeata.options);
|
|
233
|
-
if (res.sent)
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
236
|
-
if (authClsMethodMeata.authorize && this.authorizeMiddleware) {
|
|
237
|
-
const cls = container_1.default.get(this.authorizeMiddleware);
|
|
238
|
-
await cls.authorize(reqClone, authClsMethodMeata.options);
|
|
239
|
-
if (res.sent)
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
if (classMiddlewares.length > 0) {
|
|
243
|
-
for (let m of classMiddlewares) {
|
|
244
|
-
const cls = typedi_1.default.get(m.constructor);
|
|
245
|
-
reqClone = (await cls.invoke(reqClone, res));
|
|
246
|
-
if (res.sent)
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
const args = await this._mapArgs(reqClone, allMeta);
|
|
251
|
-
for (let paramMeta of allMeta.params) {
|
|
252
|
-
if (paramMeta.required) {
|
|
253
|
-
(0, helpers_1.validateOrThrow)({ [paramMeta.key]: args[paramMeta.index] }, { [paramMeta.key]: { type: paramMeta.dataType } }, { location: "param" });
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
for (let queryMeta of allMeta.query) {
|
|
257
|
-
if (queryMeta.validatorClass) {
|
|
258
|
-
const err = await (0, helpers_1.validateObjectByInstance)(queryMeta.dataType, args[queryMeta.index]);
|
|
259
|
-
if (err) {
|
|
260
|
-
return await res.code(400).send({
|
|
261
|
-
code: 400,
|
|
262
|
-
error: "ValidationError",
|
|
263
|
-
errors: err,
|
|
264
|
-
message: err.message,
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
if (queryMeta.required) {
|
|
269
|
-
(0, helpers_1.validateOrThrow)({ [queryMeta.key]: args[queryMeta.index] }, { [queryMeta.key]: { type: queryMeta.dataType } }, { location: "queryparam" });
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
if (!isMultipart) {
|
|
273
|
-
for (let bodyMeta of allMeta.body) {
|
|
274
|
-
if (bodyMeta.validatorClass) {
|
|
275
|
-
const err = await (0, helpers_1.validateObjectByInstance)(bodyMeta.dataType, args[bodyMeta.index]);
|
|
276
|
-
if (err) {
|
|
277
|
-
return await res.code(400).send({
|
|
278
|
-
code: 400,
|
|
279
|
-
error: "ValidationError",
|
|
280
|
-
errors: err,
|
|
281
|
-
message: err.message,
|
|
282
|
-
});
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
const result = await prototype[method].apply(ctrl, args);
|
|
288
|
-
if (result === null || result === void 0 ? void 0 : result.download) {
|
|
289
|
-
const { stream, filename } = result;
|
|
290
|
-
if (!stream || typeof stream.pipe !== "function") {
|
|
291
|
-
return res.code(500).send({
|
|
292
|
-
code: 500,
|
|
293
|
-
error: "INTERNAL_ERROR",
|
|
294
|
-
message: "Invalid stream object",
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
const contentType = result.contentType ||
|
|
298
|
-
mime_1.default.getType(filename) ||
|
|
299
|
-
"application/octet-stream";
|
|
300
|
-
res.header("Content-Type", contentType);
|
|
301
|
-
res.header("Content-Disposition", `attachment; filename="${filename}"`);
|
|
302
|
-
stream.on("error", (err) => {
|
|
303
|
-
console.error("Stream error:", err);
|
|
304
|
-
if (!res.sent) {
|
|
305
|
-
res.code(500).send({
|
|
306
|
-
code: 500,
|
|
307
|
-
error: "StreamError",
|
|
308
|
-
message: "Error while streaming file.",
|
|
309
|
-
});
|
|
310
|
-
}
|
|
311
|
-
});
|
|
312
|
-
return res.send(stream);
|
|
313
|
-
}
|
|
314
|
-
if (result instanceof stream_1.default || typeof (result === null || result === void 0 ? void 0 : result.pipe) === "function") {
|
|
315
|
-
result.on("error", (err) => {
|
|
316
|
-
console.error("Stream error:", err);
|
|
317
|
-
if (!res.sent) {
|
|
318
|
-
res.code(500).send({
|
|
319
|
-
code: 500,
|
|
320
|
-
error: "StreamError",
|
|
321
|
-
message: "Error while streaming file.",
|
|
322
|
-
});
|
|
323
|
-
}
|
|
324
|
-
});
|
|
325
|
-
res.header("Content-Type", "application/octet-stream");
|
|
326
|
-
return res.send(result);
|
|
327
|
-
}
|
|
328
|
-
return res.send(result);
|
|
329
|
-
},
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
async _mapArgs(req, meta) {
|
|
334
|
-
var _a, _b, _c, _d;
|
|
335
|
-
if (!req.hasOwnProperty("_argsCache")) {
|
|
336
|
-
Object.defineProperty(req, "_argsCache", {
|
|
337
|
-
value: new Map(),
|
|
338
|
-
enumerable: false,
|
|
339
|
-
writable: false,
|
|
340
|
-
configurable: false,
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
const cache = req._argsCache;
|
|
344
|
-
const cacheKey = JSON.stringify(meta);
|
|
345
|
-
if (cache.has(cacheKey)) {
|
|
346
|
-
return cache.get(cacheKey);
|
|
347
|
-
}
|
|
348
|
-
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), ...(((_a = meta.request) === null || _a === void 0 ? void 0 : _a.map((r) => r.index)) || []), ...(((_b = meta.file) === null || _b === void 0 ? void 0 : _b.map((f) => f.index)) || []), ...(((_c = meta.files) === null || _c === void 0 ? void 0 : _c.map((f) => f.index)) || []), -1) + 1;
|
|
349
|
-
const args = new Array(maxIndex).fill(undefined);
|
|
350
|
-
meta.params.forEach((p) => {
|
|
351
|
-
var _a;
|
|
352
|
-
const raw = p.key === "all" ? { ...req.params } : ((_a = req.params[p.key]) !== null && _a !== void 0 ? _a : null);
|
|
353
|
-
args[p.index] = (0, helpers_1.autoCast)(raw, p.dataType, p.schema);
|
|
354
|
-
});
|
|
355
|
-
meta.query.forEach((q) => {
|
|
356
|
-
const raw = q.key === "all"
|
|
357
|
-
? (0, helpers_1.normalizeQueryDeep)({ ...req.query })
|
|
358
|
-
: req.query[q.key];
|
|
359
|
-
args[q.index] = (0, helpers_1.autoCast)(raw, q.dataType, q.schema);
|
|
360
|
-
});
|
|
361
|
-
meta.body.forEach((body) => {
|
|
362
|
-
args[body.index] = { ...req.body, ...req.formData };
|
|
363
|
-
});
|
|
364
|
-
meta.currentUser.forEach((user) => {
|
|
365
|
-
args[user.index] = req.user;
|
|
366
|
-
});
|
|
367
|
-
meta.headers.forEach((header) => {
|
|
368
|
-
args[header.index] =
|
|
369
|
-
header.key === "all"
|
|
370
|
-
? { ...req.headers }
|
|
371
|
-
: req.headers[header.key];
|
|
372
|
-
});
|
|
373
|
-
if (meta.request && meta.request.length > 0) {
|
|
374
|
-
meta.request.forEach((r) => {
|
|
375
|
-
args[r.index] = req;
|
|
376
|
-
});
|
|
377
|
-
}
|
|
378
|
-
const needsFiles = (meta.file && meta.file.length > 0) ||
|
|
379
|
-
(meta.files && meta.files.length > 0);
|
|
380
|
-
if (needsFiles &&
|
|
381
|
-
((_d = req.headers["content-type"]) === null || _d === void 0 ? void 0 : _d.startsWith("multipart/form-data")) &&
|
|
382
|
-
req.saveRequestFiles) {
|
|
383
|
-
const files = await req.saveRequestFiles();
|
|
384
|
-
if (!files || files.length === 0) {
|
|
385
|
-
if (meta.files && meta.files.length > 0) {
|
|
386
|
-
throw new exceptions_1.BadRequestException({ error: "No files uploaded" });
|
|
387
|
-
}
|
|
388
|
-
if (meta.file && meta.file.length > 0) {
|
|
389
|
-
meta.file.forEach((f) => {
|
|
390
|
-
args[f.index] = null;
|
|
391
|
-
});
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
else {
|
|
395
|
-
const fileInfo = files.map((file) => ({
|
|
396
|
-
type: file.type,
|
|
397
|
-
filepath: file.filepath,
|
|
398
|
-
fieldname: file.fieldname,
|
|
399
|
-
filename: file.filename,
|
|
400
|
-
encoding: file.encoding,
|
|
401
|
-
mimetype: file.mimetype,
|
|
402
|
-
fields: file.fields,
|
|
403
|
-
toBuffer: file.toBuffer,
|
|
404
|
-
}));
|
|
405
|
-
if (meta.file && meta.file.length > 0) {
|
|
406
|
-
meta.file.forEach((f) => {
|
|
407
|
-
if (f.fieldName === "all") {
|
|
408
|
-
args[f.index] = fileInfo[0] || null;
|
|
409
|
-
}
|
|
410
|
-
else {
|
|
411
|
-
const file = fileInfo.find((x) => x.fieldname === f.fieldName);
|
|
412
|
-
if (!file) {
|
|
413
|
-
throw new exceptions_1.BadRequestException(`File field "${f.fieldName}" not found in uploaded files`);
|
|
414
|
-
}
|
|
415
|
-
args[f.index] = file;
|
|
416
|
-
}
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
|
-
if (meta.files && meta.files.length > 0) {
|
|
420
|
-
meta.files.forEach((f) => {
|
|
421
|
-
if (f.fieldName === "all") {
|
|
422
|
-
args[f.index] = fileInfo;
|
|
423
|
-
}
|
|
424
|
-
else {
|
|
425
|
-
const matchingFiles = fileInfo.filter((x) => x.fieldname === f.fieldName);
|
|
426
|
-
if (matchingFiles.length === 0) {
|
|
427
|
-
throw new exceptions_1.BadRequestException(`No files found for field "${f.fieldName}"`);
|
|
428
|
-
}
|
|
429
|
-
args[f.index] = matchingFiles;
|
|
430
|
-
}
|
|
431
|
-
});
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
else if (needsFiles) {
|
|
436
|
-
throw new exceptions_1.BadRequestException({
|
|
437
|
-
error: "Invalid content type. Expected multipart/form-data for file uploads",
|
|
438
|
-
});
|
|
439
|
-
}
|
|
440
|
-
cache.set(cacheKey, args);
|
|
441
|
-
return args;
|
|
442
|
-
}
|
|
443
|
-
_routeHandler(routePath, method, fn) {
|
|
444
|
-
const routeKey = method + ":" + routePath;
|
|
445
|
-
this.rMap.set(routeKey, {
|
|
446
|
-
handler: fn,
|
|
447
|
-
middlewares: [],
|
|
448
|
-
schema: {},
|
|
449
|
-
});
|
|
450
|
-
const route = {
|
|
451
|
-
useMiddleware: (middlewares) => {
|
|
452
|
-
const midds = Array.isArray(middlewares) ? middlewares : [middlewares];
|
|
453
|
-
const ms = midds.map((mclass) => {
|
|
454
|
-
const cls = typedi_1.default.get(mclass);
|
|
455
|
-
this.middlewares.set(mclass.name, cls);
|
|
456
|
-
return cls.invoke;
|
|
457
|
-
});
|
|
458
|
-
const r = this.rMap.get(routeKey);
|
|
459
|
-
if (r)
|
|
460
|
-
r.middlewares = ms;
|
|
461
|
-
return route;
|
|
462
|
-
},
|
|
463
|
-
useOpenApi: (options) => {
|
|
464
|
-
const r = this.rMap.get(routeKey);
|
|
465
|
-
if (r)
|
|
466
|
-
r.schema = options;
|
|
467
|
-
return route;
|
|
468
|
-
},
|
|
469
|
-
};
|
|
470
|
-
return route;
|
|
471
|
-
}
|
|
472
|
-
mapGet(path = "", fn) {
|
|
473
|
-
return this._routeHandler(path, "GET", fn);
|
|
474
|
-
}
|
|
475
|
-
mapPost(path = "", fn) {
|
|
476
|
-
return this._routeHandler(path, "POST", fn);
|
|
477
|
-
}
|
|
478
|
-
mapPut(path = "", fn) {
|
|
479
|
-
return this._routeHandler(path, "PUT", fn);
|
|
480
|
-
}
|
|
481
|
-
mapDelete(path = "", fn) {
|
|
482
|
-
return this._routeHandler(path, "DELETE", fn);
|
|
483
|
-
}
|
|
484
|
-
async mapRoute(method, path = "", fn) {
|
|
485
|
-
this.app[method](path, async (req, res) => {
|
|
486
|
-
try {
|
|
487
|
-
const result = await fn(req, res);
|
|
488
|
-
if (typeof result === "object" && result !== null) {
|
|
489
|
-
res.json(result);
|
|
490
|
-
}
|
|
491
|
-
else {
|
|
492
|
-
res.send(result);
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
catch (error) {
|
|
496
|
-
console.error(`Error in ${method} route handler:`, error);
|
|
497
|
-
res.status(500).send({ error: "Internal Server Error" });
|
|
498
|
-
}
|
|
499
|
-
});
|
|
500
|
-
}
|
|
501
|
-
processFunctionalRoutes() {
|
|
502
|
-
this.rMap.forEach((value, key) => {
|
|
503
|
-
var _a;
|
|
504
|
-
const colonIdx = key.indexOf(":");
|
|
505
|
-
const m = key.slice(0, colonIdx);
|
|
506
|
-
const r = key.slice(colonIdx + 1); // ✅ handles paths containing ":"
|
|
507
|
-
const routeSchema = buildRouteSchema(value.schema || {});
|
|
508
|
-
this.app.route({
|
|
509
|
-
method: m.toUpperCase(),
|
|
510
|
-
url: r,
|
|
511
|
-
schema: routeSchema,
|
|
512
|
-
preHandler: (_a = value.middlewares) !== null && _a !== void 0 ? _a : [],
|
|
513
|
-
handler: async (req, res) => {
|
|
514
|
-
return value.handler(req, res);
|
|
515
|
-
},
|
|
516
|
-
});
|
|
517
|
-
});
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
exports.AvleonRouter = AvleonRouter;
|
package/dist/core/testing.d.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @copyright 2024
|
|
3
|
-
* @author Tareq Hossain
|
|
4
|
-
* @email xtrinsic96@gmail.com
|
|
5
|
-
* @url https://github.com/xtareq
|
|
6
|
-
*/
|
|
7
|
-
import { AvleonApplication } from "./application";
|
|
8
|
-
import { Constructor } from "../helpers";
|
|
9
|
-
import { TestApplication, TestAppOptions } from "../interfaces/avleon-application";
|
|
10
|
-
export declare class AvleonTest {
|
|
11
|
-
private constructor();
|
|
12
|
-
static getController<T>(controller: Constructor<T>, deps?: any[]): T;
|
|
13
|
-
static getProvider<T>(service: Constructor<T>, deps?: any[]): T;
|
|
14
|
-
static createTestApplication(options: TestAppOptions): TestApplication;
|
|
15
|
-
static from(app: AvleonApplication): TestApplication;
|
|
16
|
-
static clean(): void;
|
|
17
|
-
}
|
|
18
|
-
export declare class Avleon {
|
|
19
|
-
static createApplication(): AvleonApplication;
|
|
20
|
-
static createTestApplication(options: TestAppOptions): TestApplication;
|
|
21
|
-
}
|
package/dist/core/testing.js
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.Avleon = exports.AvleonTest = void 0;
|
|
7
|
-
/**
|
|
8
|
-
* @copyright 2024
|
|
9
|
-
* @author Tareq Hossain
|
|
10
|
-
* @email xtrinsic96@gmail.com
|
|
11
|
-
* @url https://github.com/xtareq
|
|
12
|
-
*/
|
|
13
|
-
const application_1 = require("./application");
|
|
14
|
-
const typedi_1 = __importDefault(require("typedi"));
|
|
15
|
-
const exceptions_1 = require("../exceptions");
|
|
16
|
-
const system_exception_1 = require("../exceptions/system-exception");
|
|
17
|
-
class AvleonTest {
|
|
18
|
-
constructor() {
|
|
19
|
-
process.env.NODE_ENV = "test";
|
|
20
|
-
}
|
|
21
|
-
static getController(controller, deps = []) {
|
|
22
|
-
const paramTypes = Reflect.getMetadata("design:paramtypes", controller) || [];
|
|
23
|
-
deps.forEach((dep, i) => {
|
|
24
|
-
typedi_1.default.set(paramTypes[i], dep);
|
|
25
|
-
});
|
|
26
|
-
return typedi_1.default.get(controller);
|
|
27
|
-
}
|
|
28
|
-
static getProvider(service, deps = []) {
|
|
29
|
-
const paramTypes = Reflect.getMetadata("design:paramtypes", service) || [];
|
|
30
|
-
deps.forEach((dep, i) => {
|
|
31
|
-
typedi_1.default.set(paramTypes[i], dep);
|
|
32
|
-
});
|
|
33
|
-
return typedi_1.default.get(service);
|
|
34
|
-
}
|
|
35
|
-
static createTestApplication(options) {
|
|
36
|
-
const app = application_1.AvleonApplication.getInternalApp({
|
|
37
|
-
dataSourceOptions: options.dataSource ? options.dataSource : undefined,
|
|
38
|
-
});
|
|
39
|
-
// We need to cast options.controllers to any or Constructor[] for compatibility
|
|
40
|
-
if (options.controllers) {
|
|
41
|
-
app.useControllers(options.controllers);
|
|
42
|
-
}
|
|
43
|
-
return AvleonTest.from(app);
|
|
44
|
-
}
|
|
45
|
-
static from(app) {
|
|
46
|
-
// Logic to return a TestApplication wrapper around the running app
|
|
47
|
-
// This effectively manually builds the routes map if it wasn't run?
|
|
48
|
-
// In icore.ts getTestApp() logic was doing a lot of things manually.
|
|
49
|
-
// We need to replicate that behavior via the `app` instance.
|
|
50
|
-
// BUT, since we split, logic might need access to internals.
|
|
51
|
-
// For now, let's implement the wrapper using app.app (fastify instance)
|
|
52
|
-
try {
|
|
53
|
-
// Note: In original code, getTestApp did _mapControllers().
|
|
54
|
-
// We should access private methods or expose public init method.
|
|
55
|
-
// Since we are in separate file, we can't access private easily.
|
|
56
|
-
// Best approach: add `initForTest()` on AvleonApplication
|
|
57
|
-
// Assuming we added public method or we just cast to any to call private methods (naughty but works)
|
|
58
|
-
app._mapControllers().catch((e) => console.error(e));
|
|
59
|
-
// In icore.ts, it was mapping routes to app.route explicitly.
|
|
60
|
-
// In our refactor, _mapControllers calls router.buildController -> which adds routes to app.
|
|
61
|
-
// So fastify app should be ready.
|
|
62
|
-
app.app.setErrorHandler(async (error, req, res) => {
|
|
63
|
-
// Reuse error handler logic or simplified version
|
|
64
|
-
if (error instanceof exceptions_1.ValidationErrorException) {
|
|
65
|
-
return res.status(400).send({
|
|
66
|
-
code: 400,
|
|
67
|
-
error: "ValidationError",
|
|
68
|
-
errors: error.message,
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
return res.status(500).send(error);
|
|
72
|
-
});
|
|
73
|
-
return {
|
|
74
|
-
get: async (url, options) => app.app.inject({ method: "GET", url, ...options }),
|
|
75
|
-
post: async (url, options) => app.app.inject({ method: "POST", url, ...options }),
|
|
76
|
-
put: async (url, options) => app.app.inject({ method: "PUT", url, ...options }),
|
|
77
|
-
patch: async (url, options) => app.app.inject({ method: "PATCH", url, ...options }),
|
|
78
|
-
delete: async (url, options) => app.app.inject({ method: "DELETE", url, ...options }),
|
|
79
|
-
options: async (url, options) => app.app.inject({ method: "OPTIONS", url, ...options }),
|
|
80
|
-
getController: (controller, deps = []) => {
|
|
81
|
-
return AvleonTest.getController(controller, deps);
|
|
82
|
-
},
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
catch (error) {
|
|
86
|
-
throw new system_exception_1.SystemUseError("Can't get test appliction");
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
static clean() {
|
|
90
|
-
typedi_1.default.reset();
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
exports.AvleonTest = AvleonTest;
|
|
94
|
-
class Avleon {
|
|
95
|
-
static createApplication() {
|
|
96
|
-
const app = application_1.AvleonApplication.getApp();
|
|
97
|
-
return app;
|
|
98
|
-
}
|
|
99
|
-
static createTestApplication(options) {
|
|
100
|
-
const app = AvleonTest.createTestApplication(options);
|
|
101
|
-
return app;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
exports.Avleon = Avleon;
|