@zenofolio/hyper-decor 1.0.69 → 1.0.72

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.
Files changed (32) hide show
  1. package/README.md +136 -254
  2. package/dist/__internals/constants.d.ts +2 -2
  3. package/dist/__internals/constants.js +2 -2
  4. package/dist/__internals/creators/request.creator.d.ts +1 -10
  5. package/dist/__internals/creators/request.creator.js +141 -21
  6. package/dist/__internals/decorator-base.js +1 -1
  7. package/dist/__internals/helpers/prepare.helper.js +71 -51
  8. package/dist/decorators/File.d.ts +4 -32
  9. package/dist/decorators/File.js +62 -75
  10. package/dist/decorators/Http.d.ts +9 -42
  11. package/dist/decorators/Http.js +24 -72
  12. package/dist/decorators/Output.d.ts +9 -0
  13. package/dist/decorators/Output.js +18 -0
  14. package/dist/decorators/index.d.ts +1 -1
  15. package/dist/decorators/index.js +1 -1
  16. package/dist/decorators/types.d.ts +2 -0
  17. package/dist/exeptions/HyperException.d.ts +2 -1
  18. package/dist/exeptions/HyperException.js +2 -1
  19. package/dist/exeptions/HyperFileException.js +1 -1
  20. package/dist/index.d.ts +2 -0
  21. package/dist/index.js +2 -0
  22. package/dist/lib/openapi/collectors/method.collector.js +25 -12
  23. package/dist/lib/openapi/collectors/param.collector.d.ts +2 -3
  24. package/dist/lib/openapi/collectors/param.collector.js +49 -12
  25. package/dist/lib/openapi/decorators/api-parameter.decorator.d.ts +2 -2
  26. package/dist/lib/openapi/decorators/api-response.decorator.d.ts +2 -2
  27. package/dist/lib/openapi/decorators/api-tag.decorator.d.ts +1 -1
  28. package/dist/lib/openapi/decorators/api-tag.decorator.js +3 -0
  29. package/dist/lib/openapi/helpers/parameter.helper.d.ts +2 -2
  30. package/dist/lib/openapi/helpers/response.helper.d.ts +2 -2
  31. package/dist/lib/openapi/types.d.ts +8 -8
  32. package/package.json +3 -2
@@ -24,7 +24,6 @@ const middleware_transform_1 = __importDefault(require("../transform/middleware.
24
24
  const tsyringe_1 = require("tsyringe");
25
25
  const path_util_1 = require("../utils/path.util");
26
26
  const imports_helper_1 = require("./imports.helper");
27
- const constants_2 = require("../constants");
28
27
  const message_bus_1 = require("../../common/message-bus");
29
28
  const transport_1 = require("../../common/transport");
30
29
  const transform_registry_1 = require("../transform/transform.registry");
@@ -243,11 +242,54 @@ function prepareTarget(_a) {
243
242
  };
244
243
  });
245
244
  }
245
+ /**
246
+ * Resolves method parameters and applies adaptive transformations.
247
+ * Optimized: Metadata is resolved once and passed here.
248
+ */
249
+ function resolveMethodParams(req, res, params) {
250
+ return __awaiter(this, void 0, void 0, function* () {
251
+ const len = params.length;
252
+ const args = new Array(len);
253
+ for (let i = 0; i < len; i++) {
254
+ args[i] = yield params[i].resolver(req, res);
255
+ }
256
+ return args;
257
+ });
258
+ }
259
+ /**
260
+ * Handles output transformation and sends the response.
261
+ * Optimized: outputSchema is pre-resolved outside the request hotpath.
262
+ */
263
+ function handleResponse(req, res, result, outputSchema) {
264
+ return __awaiter(this, void 0, void 0, function* () {
265
+ if (result === undefined || res.completed)
266
+ return;
267
+ if (outputSchema && outputSchema !== Object && outputSchema !== Promise) {
268
+ const transformed = yield transform_registry_1.transformRegistry.resolve({
269
+ data: result,
270
+ schema: outputSchema,
271
+ options: {},
272
+ req,
273
+ res,
274
+ from: "response",
275
+ });
276
+ if (transformed !== undefined && !res.completed) {
277
+ res.json(transformed);
278
+ return;
279
+ }
280
+ }
281
+ if (!res.completed) {
282
+ if (typeof result === "object" && result !== null) {
283
+ res.json(result);
284
+ }
285
+ else {
286
+ res.send(result);
287
+ }
288
+ }
289
+ });
290
+ }
246
291
  /**
247
292
  * Prepare the routes for the target class.
248
- *
249
- * @param param0
250
- * @returns
251
293
  */
252
294
  function prepareRoutes(_a) {
253
295
  return __awaiter(this, arguments, void 0, function* ({ target, router, route, instance, namespace, log, }) {
@@ -256,63 +298,41 @@ function prepareRoutes(_a) {
256
298
  const metadata = getData(handler);
257
299
  const params = (_d = (_c = (_b = metadata.params) === null || _b === void 0 ? void 0 : _b.params) === null || _c === void 0 ? void 0 : _c[propertyKey]) !== null && _d !== void 0 ? _d : [];
258
300
  const $fn = Reflect.get(router, method);
259
- const hasParams = params.length > 0;
260
301
  if (!$fn)
261
302
  return;
262
303
  const middlewares = [...metadata.middlewares];
263
- // Agnostic Transform Integration
264
- const transform = (0, decorator_base_1.getDecorData)(constants_2.KEY_PARAMS_TRANSFORM, target.prototype || target, propertyKey);
265
- if (transform) {
266
- middlewares.push((req, res, next) => __awaiter(this, void 0, void 0, function* () {
267
- try {
268
- const from = transform.options.from || "body";
269
- let data;
270
- if (from === "body") {
271
- data = req.body !== undefined ? req.body : yield req.json();
272
- }
273
- else {
274
- data = req[from];
275
- }
276
- // Resolve through the chain of responsibility
277
- const transformed = yield transform_registry_1.transformRegistry.resolve({
278
- data,
279
- schema: transform.schema,
280
- options: transform.options,
281
- req,
282
- res,
283
- from
284
- });
285
- // Update the request object
286
- req[from] = transformed;
287
- next();
288
- }
289
- catch (err) {
290
- next(err);
291
- }
292
- }));
293
- }
304
+ const proto = target.prototype || target;
305
+ // Pre-resolve Output-Metadata
306
+ const outputSchema = Reflect.getMetadata(constants_1.KEY_OUTPUT_SCHEMA, proto, propertyKey) ||
307
+ Reflect.getMetadata(constants_1.DESIGN_RETURNTYPE, proto, propertyKey);
294
308
  (0, role_transform_1.default)(metadata.roles, (middleware) => middlewares.push(middleware));
295
309
  (0, scope_transfrom_1.default)(metadata.scopes, (middleware, scopes) => {
296
310
  middlewares.push(middleware);
297
311
  stores_1.ScopeStore.addAll(scopes);
298
312
  });
299
- log("routes", `${namespace}/${propertyKey} ${method.toUpperCase()} { ${path} }`);
300
- if (!hasParams) {
301
- if (method === "ws" && options) {
302
- router.ws(path, options, handler.bind(instance));
303
- return;
313
+ log("routes", `${namespace}/${propertyKey.toString()} ${method.toUpperCase()} { ${path} }`);
314
+ const routeHandler = (req, res) => __awaiter(this, void 0, void 0, function* () {
315
+ try {
316
+ const args = params.length > 0
317
+ ? yield resolveMethodParams(req, res, params)
318
+ : [req, res];
319
+ const result = yield handler.apply(instance, args);
320
+ yield handleResponse(req, res, result, outputSchema);
304
321
  }
305
- $fn.call(router, path, ...middlewares, handler.bind(instance));
306
- }
307
- else {
308
- $fn.call(router, path, ...middlewares, (req, res) => __awaiter(this, void 0, void 0, function* () {
309
- const len = params.length;
310
- const args = new Array(len);
311
- for (let i = 0; i < len; i++) {
312
- args[i] = yield params[i].resolver(req, res);
322
+ catch (err) {
323
+ if (!res.completed) {
324
+ const error = err;
325
+ res.status(error.status || 500).json({
326
+ error: error.message || "Internal Server Error",
327
+ code: error.code,
328
+ });
313
329
  }
314
- return handler.apply(instance, args);
315
- }));
330
+ }
331
+ });
332
+ if (method === "ws" && options) {
333
+ router.ws(path, options, handler.bind(instance));
334
+ return;
316
335
  }
336
+ $fn.call(router, path, ...middlewares, routeHandler);
317
337
  });
318
338
  }
@@ -1,31 +1,17 @@
1
1
  import { Request } from "hyper-express/types";
2
2
  /**
3
3
  * File upload restrictions
4
- *
5
- * @param allowedMimeTypes Allowed MIME types
6
- * @param maxFileSize Maximum file size in bytes
7
- *
8
4
  */
9
- export interface FileUplopadRestrictions {
5
+ export interface FileUploadRestrictions {
10
6
  allowedMimeTypes: string[];
11
7
  maxFileSize: number;
12
8
  }
13
9
  /**
14
10
  * File restrictions resolver
15
- *
16
- * @param request Request object
17
- * @returns File restrictions
18
- *
19
11
  */
20
- export type FileRestrictions = FileUplopadRestrictions | ((request: Request) => FileUplopadRestrictions | Promise<FileUplopadRestrictions>);
12
+ export type FileRestrictions = FileUploadRestrictions | ((request: Request) => FileUploadRestrictions | Promise<FileUploadRestrictions>);
21
13
  /**
22
14
  * File decorator options
23
- *
24
- * @param fieldName Field name to extract from the request
25
- * @param restrictions File restrictions
26
- * @param required If the file is required
27
- *
28
- *
29
15
  */
30
16
  export interface FileOptions {
31
17
  fieldName: string | string[];
@@ -33,33 +19,19 @@ export interface FileOptions {
33
19
  required?: boolean;
34
20
  }
35
21
  export interface UploadedFile {
22
+ field: string;
36
23
  name: string;
37
24
  filename: string;
38
25
  mimeType: string;
39
26
  size: number;
40
- ext: string;
27
+ extension: string;
41
28
  buffer: Buffer;
42
29
  }
43
30
  /**
44
31
  * Decorator to extract file from the request
45
- *
46
- * @param param0
47
32
  */
48
33
  export declare const File: {
49
34
  (options: FileOptions | string): ParameterDecorator;
50
- /**
51
- *
52
- * Helper function to create a file decorator with options
53
- *
54
- * @param options
55
- * @returns
56
- */
57
35
  options(options: FileOptions, required?: boolean): (fieldName: string | string[]) => ParameterDecorator;
58
- /**
59
- * Helper function to create a file decorator with restrictions
60
- *
61
- * @param restrictions
62
- * @returns
63
- */
64
36
  restrictions(restrictions: FileRestrictions): (fieldName: string | string[], required?: boolean) => ParameterDecorator;
65
37
  };
@@ -8,46 +8,37 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
+ var __asyncValues = (this && this.__asyncValues) || function (o) {
12
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
13
+ var m = o[Symbol.asyncIterator], i;
14
+ return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
15
+ function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
16
+ function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
17
+ };
11
18
  var __importDefault = (this && this.__importDefault) || function (mod) {
12
19
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
20
  };
14
21
  Object.defineProperty(exports, "__esModule", { value: true });
15
22
  exports.File = void 0;
16
23
  const Http_1 = require("./Http");
17
- const consumers_1 = require("stream/consumers");
18
24
  const HyperFileException_1 = __importDefault(require("../exeptions/HyperFileException"));
19
25
  /**
20
26
  * Decorator to extract file from the request
21
- *
22
- * @param param0
23
27
  */
24
28
  const File = (options) => {
25
29
  const _options = typeof options === "string" ? { fieldName: options } : options;
26
30
  return (0, Http_1.createCustomRequestDecorator)("File", (request) => __awaiter(void 0, void 0, void 0, function* () {
27
- var _a, _b;
31
+ var _a;
28
32
  const { allowedMimeTypes, maxFileSize } = yield resolveRestrictions(request, _options.restrictions);
29
33
  const passTypes = allowedMimeTypes.includes("*") || allowedMimeTypes.length === 0;
30
34
  const passSize = maxFileSize === Infinity;
31
35
  const contentType = (_a = request.headers) === null || _a === void 0 ? void 0 : _a["content-type"];
32
- const fileSize = Number((_b = request.headers) === null || _b === void 0 ? void 0 : _b["content-length"]);
33
36
  const isMultipart = contentType === null || contentType === void 0 ? void 0 : contentType.includes("multipart/form-data");
34
37
  if (!isMultipart) {
35
38
  throw new HyperFileException_1.default("Invalid request, expected multipart form data", {
36
39
  fieldName: _options.fieldName,
37
40
  });
38
41
  }
39
- if (isNaN(fileSize) || fileSize <= 0) {
40
- throw new HyperFileException_1.default(`File ${_options.fieldName} is missing or empty`, {
41
- fieldName: _options.fieldName,
42
- fileSize,
43
- });
44
- }
45
- if (!passSize && fileSize > maxFileSize) {
46
- throw new HyperFileException_1.default(`File ${_options.fieldName} is too large. Max size is ${maxFileSize} bytes`, {
47
- fieldName: _options.fieldName,
48
- maxFileSize,
49
- });
50
- }
51
42
  const files = yield extractFiles(request, {
52
43
  maxSize: passSize ? undefined : maxFileSize,
53
44
  mimeTypes: passTypes ? undefined : allowedMimeTypes,
@@ -64,35 +55,16 @@ const File = (options) => {
64
55
  }));
65
56
  };
66
57
  exports.File = File;
67
- /**
68
- *
69
- * Helper function to create a file decorator with options
70
- *
71
- * @param options
72
- * @returns
73
- */
74
58
  exports.File.options = (options, required = false) => {
75
59
  return (fieldName) => {
76
60
  return (0, exports.File)(Object.assign(Object.assign({}, options), { required, fieldName }));
77
61
  };
78
62
  };
79
- /**
80
- * Helper function to create a file decorator with restrictions
81
- *
82
- * @param restrictions
83
- * @returns
84
- */
85
63
  exports.File.restrictions = (restrictions) => {
86
64
  return (fieldName, required = false) => {
87
65
  return (0, exports.File)({ fieldName, restrictions, required });
88
66
  };
89
67
  };
90
- /////////////////////////////
91
- // Types
92
- /////////////////////////////
93
- /////////////////////////////
94
- /// Utility
95
- /////////////////////////////
96
68
  const resolveRestrictions = (request, restrictions) => __awaiter(void 0, void 0, void 0, function* () {
97
69
  if (typeof restrictions === "function") {
98
70
  return yield restrictions(request);
@@ -103,63 +75,78 @@ const resolveRestrictions = (request, restrictions) => __awaiter(void 0, void 0,
103
75
  });
104
76
  });
105
77
  /**
106
- * Extract files from a request with validation, transformation, and required files check.
107
- *
108
- * @param request
109
- * @param options Validation and transformation options
110
- * @returns Transformed or raw file details
78
+ * Extract files from a request with validation and secure streaming.
111
79
  */
112
80
  const extractFiles = (request, options) => __awaiter(void 0, void 0, void 0, function* () {
113
81
  var _a;
114
- const contentType = (_a = request.headers) === null || _a === void 0 ? void 0 : _a["content-type"];
115
- if (!(contentType === null || contentType === void 0 ? void 0 : contentType.includes("multipart/form-data"))) {
116
- throw new Error("Invalid request, expected multipart form data");
117
- }
118
82
  const filesMap = {};
83
+ // Track if we are already over total limit (Content-Length check as first line of defense)
84
+ const totalLength = Number((_a = request.headers) === null || _a === void 0 ? void 0 : _a["content-length"]);
85
+ if ((options === null || options === void 0 ? void 0 : options.maxSize) && !isNaN(totalLength) && totalLength > options.maxSize + 1024 * 10) { // Buffer for boundaries
86
+ // Content-Length is significantly larger than allowed file size
87
+ // We don't throw yet because multipart might have multiple files or large fields
88
+ }
119
89
  yield request.multipart((field) => __awaiter(void 0, void 0, void 0, function* () {
120
- var _a, _b, _c, _d;
90
+ var _a, e_1, _b, _c;
91
+ var _d, _e;
121
92
  if (field.file && field.file.stream) {
122
- const bufferFile = yield (0, consumers_1.buffer)(field.file.stream);
123
- const type = yield (yield import("file-type")).fileTypeFromBuffer(bufferFile);
124
- if (!type) {
125
- throw new HyperFileException_1.default(`Invalid file type`, {
126
- field: field.name,
127
- });
93
+ const chunks = [];
94
+ let totalRead = 0;
95
+ try {
96
+ // Safe stream reading with early exit
97
+ for (var _f = true, _g = __asyncValues(field.file.stream), _h; _h = yield _g.next(), _a = _h.done, !_a; _f = true) {
98
+ _c = _h.value;
99
+ _f = false;
100
+ const chunk = _c;
101
+ totalRead += chunk.length;
102
+ if ((options === null || options === void 0 ? void 0 : options.maxSize) && totalRead > options.maxSize) {
103
+ throw new HyperFileException_1.default(`File '${field.name}' exceeds the maximum limit of ${options.maxSize} bytes`, { field: field.name, maxSize: options.maxSize });
104
+ }
105
+ chunks.push(chunk);
106
+ }
128
107
  }
129
- const mimeType = type.mime;
130
- const extention = type.ext;
131
- // Validate MIME type early
132
- if ((options === null || options === void 0 ? void 0 : options.mimeTypes) && !options.mimeTypes.includes(mimeType)) {
133
- throw new HyperFileException_1.default(`Invalid file type, only ${options.mimeTypes.join(", ")} is allowed`, {
134
- field: field.name,
135
- allowedMimeTypes: options.mimeTypes,
136
- mimeType,
137
- });
108
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
109
+ finally {
110
+ try {
111
+ if (!_f && !_a && (_b = _g.return)) yield _b.call(_g);
112
+ }
113
+ finally { if (e_1) throw e_1.error; }
138
114
  }
139
- if (!type) {
140
- throw new HyperFileException_1.default(`Invalid file type`, {
141
- field: field.name,
142
- });
115
+ const bufferFile = Buffer.concat(chunks);
116
+ const { fileTypeFromBuffer } = yield import("file-type");
117
+ const type = yield fileTypeFromBuffer(bufferFile);
118
+ const mimeType = (type === null || type === void 0 ? void 0 : type.mime) || "unknown/unrecognized";
119
+ const extension = (type === null || type === void 0 ? void 0 : type.ext) || "bin";
120
+ // Validate MIME type early if unrecognized
121
+ if (!type && (options === null || options === void 0 ? void 0 : options.mimeTypes) && options.mimeTypes.length > 0) {
122
+ throw new HyperFileException_1.default(`Invalid file type for field '${field.name}'. Could not recognize file signature. Expected one of: ${options.mimeTypes.join(", ")}`, { field: field.name, allowedMimeTypes: options.mimeTypes });
143
123
  }
144
- if ((options === null || options === void 0 ? void 0 : options.maxSize) && bufferFile.byteLength > options.maxSize) {
145
- throw new HyperFileException_1.default(`File '${field.name}' size exceeds the maximum limit of ${options.maxSize} bytes`, {
124
+ // Validate MIME type if recognized
125
+ if ((options === null || options === void 0 ? void 0 : options.mimeTypes) && !options.mimeTypes.includes(mimeType)) {
126
+ throw new HyperFileException_1.default(`Invalid file type for field '${field.name}'. Expected: ${options.mimeTypes.join(", ")}, Received: ${mimeType}`, {
146
127
  field: field.name,
147
- maxSize: options.maxSize,
128
+ allowedMimeTypes: options.mimeTypes,
129
+ receivedMimeType: mimeType,
148
130
  });
149
131
  }
150
- if ((options === null || options === void 0 ? void 0 : options.requireName) && !((_a = field.file) === null || _a === void 0 ? void 0 : _a.name)) {
151
- throw new HyperFileException_1.default(`File name is required`, {
132
+ if ((options === null || options === void 0 ? void 0 : options.requireName) && !((_d = field.file) === null || _d === void 0 ? void 0 : _d.name)) {
133
+ throw new HyperFileException_1.default(`File name is required for field '${field.name}'`, {
152
134
  field: field.name,
153
135
  });
154
136
  }
155
- // Derive file details
156
- const name = (_d = (_c = (_b = field.file) === null || _b === void 0 ? void 0 : _b.name) === null || _c === void 0 ? void 0 : _c.split(".").shift()) !== null && _d !== void 0 ? _d : field.name;
157
- const filename = `${name === null || name === void 0 ? void 0 : name.replace(extention, "")}.${extention}`;
137
+ // Secure filename resolution
138
+ const rawName = ((_e = field.file) === null || _e === void 0 ? void 0 : _e.name) || field.name;
139
+ // Sanitize: remove null bytes, path traversal sequences, and extract base name
140
+ const sanitizedBase = rawName.replace(/\0/g, "").split(/[\\/]/).pop() || "file";
141
+ const nameOnly = sanitizedBase.includes(".")
142
+ ? sanitizedBase.substring(0, sanitizedBase.lastIndexOf("."))
143
+ : sanitizedBase;
144
+ const filename = `${nameOnly}.${extension}`;
158
145
  filesMap[field.name] = {
159
146
  field: field.name,
160
- name: name,
147
+ name: nameOnly,
161
148
  filename: filename,
162
- ext: extention,
149
+ extension: extension,
163
150
  size: bufferFile.byteLength,
164
151
  mimeType: mimeType,
165
152
  buffer: bufferFile,
@@ -5,51 +5,18 @@ import { ParameterResolver } from "./types";
5
5
  * @param key
6
6
  * @returns
7
7
  */
8
- export declare const Query: (key?: string, transfrom?: (data: any) => any) => ParameterDecorator;
9
8
  /**
10
- * Get the body of the request
9
+ * Get the value of a key from the request object or transform the entire query.
11
10
  *
12
- * @param resolver
13
- * @returns
14
- */
15
- export declare const Body: <T, R>(resolver?: (data: T) => R) => ParameterDecorator;
16
- /**
17
- * Get the params from the request
18
- *
19
- * @param k
20
- * @returns
21
- */
22
- export declare const Param: (k?: string, validator?: (data: any) => any) => ParameterDecorator;
23
- /**
24
- * get the headers from the request
25
- *
26
- * @param key
27
- * @returns
11
+ * @example
12
+ * \@Query('id')
13
+ * \@Query('id', IntSchema)
14
+ * \@Query(UserQueryDto)
28
15
  */
29
- export declare const Headers: <T extends string>(key?: T) => ParameterDecorator;
16
+ export declare function Query(keyOrSchema?: string | any, schemaOrTransform?: any): any;
17
+ export declare function Body(keyOrSchema?: string | any, schemaOrTransform?: any): any;
18
+ export declare function Param(keyOrSchema: string | any, schemaOrTransform?: any): any;
19
+ export declare function Headers(keyOrSchema?: string | any, schemaOrTransform?: any): any;
30
20
  export declare const Req: () => ParameterDecorator;
31
21
  export declare const Res: () => ParameterDecorator;
32
- /**
33
- * Create a custom request decorator
34
- * that can be used to extract data from the request object
35
- *
36
- *
37
- * @example
38
- * ```typescript
39
- *
40
- * interface LoginData {
41
- * username: string;
42
- * password: string;
43
- * }
44
- *
45
- * const LoginData = createCustomRequestDecorator<LoginData>({
46
- * resolver: async (request) => {
47
- * const data = await request.json();
48
- * return [data];
49
- * }
50
- * }
51
- *
52
- *
53
- *
54
- */
55
22
  export declare const createCustomRequestDecorator: (decoratorName: string, resolver: ParameterResolver) => ParameterDecorator;
@@ -1,92 +1,44 @@
1
1
  "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
12
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
4
  };
14
5
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.createCustomRequestDecorator = exports.Res = exports.Req = exports.Headers = exports.Param = exports.Body = exports.Query = void 0;
16
- const request_creator_1 = __importDefault(require("../__internals/creators/request.creator"));
17
- const object_util_1 = require("../__internals/utils/object.util");
18
- /**
19
- * Get the value of a key from the request object
20
- *
21
- * @param key
22
- * @returns
23
- */
24
- const Query = (key, transfrom) => (0, request_creator_1.default)("query", "Query", (request) => {
25
- const value = (0, object_util_1.$get)(request.query, key, request.query);
26
- if (!transfrom)
27
- return value;
28
- return value;
29
- });
6
+ exports.createCustomRequestDecorator = exports.Res = exports.Req = void 0;
30
7
  exports.Query = Query;
31
- /**
32
- * Get the body of the request
33
- *
34
- * @param resolver
35
- * @returns
36
- */
37
- const Body = (resolver) => (0, request_creator_1.default)("req", "BODY", (request) => __awaiter(void 0, void 0, void 0, function* () {
38
- const value = request.body !== undefined ? request.body : yield request.json();
39
- return resolver ? resolver(value) : value;
40
- }));
41
8
  exports.Body = Body;
42
- /**
43
- * Get the params from the request
44
- *
45
- * @param k
46
- * @returns
47
- */
48
- const Param = (k, validator) => (0, request_creator_1.default)("params", "Param", (req) => {
49
- const value = (0, object_util_1.$get)(req.params, k, req.params);
50
- if (typeof validator === "function")
51
- return validator(value);
52
- return value;
53
- });
54
9
  exports.Param = Param;
10
+ exports.Headers = Headers;
11
+ const request_creator_1 = __importDefault(require("../__internals/creators/request.creator"));
55
12
  /**
56
- * get the headers from the request
13
+ * Get the value of a key from the request object
57
14
  *
58
15
  * @param key
59
16
  * @returns
60
17
  */
61
- const Headers = (key) => (0, request_creator_1.default)("headers", "Headers", (req) => (0, object_util_1.$get)(req.headers, key, req.headers));
62
- exports.Headers = Headers;
63
- const Req = () => (0, request_creator_1.default)("req", "Req", (req) => req);
64
- exports.Req = Req;
65
- const Res = () => (0, request_creator_1.default)("res", "Res", (req, res) => res);
66
- exports.Res = Res;
67
18
  /**
68
- * Create a custom request decorator
69
- * that can be used to extract data from the request object
70
- *
19
+ * Get the value of a key from the request object or transform the entire query.
71
20
  *
72
21
  * @example
73
- * ```typescript
74
- *
75
- * interface LoginData {
76
- * username: string;
77
- * password: string;
78
- * }
79
- *
80
- * const LoginData = createCustomRequestDecorator<LoginData>({
81
- * resolver: async (request) => {
82
- * const data = await request.json();
83
- * return [data];
84
- * }
85
- * }
86
- *
87
- *
88
- *
22
+ * \@Query('id')
23
+ * \@Query('id', IntSchema)
24
+ * \@Query(UserQueryDto)
89
25
  */
26
+ function Query(keyOrSchema, schemaOrTransform) {
27
+ return (0, request_creator_1.default)("query", "Query", keyOrSchema, schemaOrTransform);
28
+ }
29
+ function Body(keyOrSchema, schemaOrTransform) {
30
+ return (0, request_creator_1.default)("body", "Body", keyOrSchema, schemaOrTransform, true);
31
+ }
32
+ function Param(keyOrSchema, schemaOrTransform) {
33
+ return (0, request_creator_1.default)("params", "Param", keyOrSchema, schemaOrTransform);
34
+ }
35
+ function Headers(keyOrSchema, schemaOrTransform) {
36
+ return (0, request_creator_1.default)("headers", "Headers", keyOrSchema, schemaOrTransform);
37
+ }
38
+ const Req = () => (0, request_creator_1.default)("req", "Req");
39
+ exports.Req = Req;
40
+ const Res = () => (0, request_creator_1.default)("res", "Res");
41
+ exports.Res = Res;
90
42
  const createCustomRequestDecorator = (decoratorName, resolver) => {
91
43
  return (0, request_creator_1.default)("req", decoratorName, resolver);
92
44
  };
@@ -0,0 +1,9 @@
1
+ import "reflect-metadata";
2
+ /**
3
+ * Decorator to explicitly mark a method for response transformation and OpenAPI documentation.
4
+ *
5
+ * @param schema The schema or DTO class for the successful response (200).
6
+ * @example
7
+ * \@Output(UserDto)
8
+ */
9
+ export declare function Output(schema?: any): (target: any, propertyKey?: any, descriptor?: PropertyDescriptor) => void;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Output = Output;
4
+ require("reflect-metadata");
5
+ const decorator_base_1 = require("../__internals/decorator-base");
6
+ const constants_1 = require("../__internals/constants");
7
+ /**
8
+ * Decorator to explicitly mark a method for response transformation and OpenAPI documentation.
9
+ *
10
+ * @param schema The schema or DTO class for the successful response (200).
11
+ * @example
12
+ * \@Output(UserDto)
13
+ */
14
+ function Output(schema) {
15
+ return (target, propertyKey, descriptor) => {
16
+ (0, decorator_base_1.defineDecorData)(constants_1.KEY_OUTPUT_SCHEMA, schema, target, propertyKey);
17
+ };
18
+ }
@@ -11,4 +11,4 @@ export * from "./Http";
11
11
  export * from "./Pass";
12
12
  export * from "./File";
13
13
  export * from "./Messaging";
14
- export * from "./Transform";
14
+ export * from "./Output";
@@ -27,4 +27,4 @@ __exportStar(require("./Http"), exports);
27
27
  __exportStar(require("./Pass"), exports);
28
28
  __exportStar(require("./File"), exports);
29
29
  __exportStar(require("./Messaging"), exports);
30
- __exportStar(require("./Transform"), exports);
30
+ __exportStar(require("./Output"), exports);