@avleon/core 0.0.40 → 0.0.43

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 (52) hide show
  1. package/README.md +37 -5
  2. package/dist/cache.test.d.ts +1 -0
  3. package/dist/cache.test.js +36 -0
  4. package/dist/controller.d.ts +2 -0
  5. package/dist/controller.js +13 -0
  6. package/dist/controller.test.d.ts +1 -0
  7. package/dist/controller.test.js +111 -0
  8. package/dist/environment-variables.test.d.ts +1 -0
  9. package/dist/environment-variables.test.js +70 -0
  10. package/dist/exceptions/http-exceptions.d.ts +1 -0
  11. package/dist/exceptions/http-exceptions.js +3 -1
  12. package/dist/file-storage.d.ts +44 -9
  13. package/dist/file-storage.js +209 -59
  14. package/dist/file-storage.test.d.ts +1 -0
  15. package/dist/file-storage.test.js +104 -0
  16. package/dist/helpers.test.d.ts +1 -0
  17. package/dist/helpers.test.js +95 -0
  18. package/dist/icore.d.ts +8 -5
  19. package/dist/icore.js +225 -81
  20. package/dist/icore.test.d.ts +1 -0
  21. package/dist/icore.test.js +14 -0
  22. package/dist/index.d.ts +24 -0
  23. package/dist/index.js +8 -1
  24. package/dist/kenx-provider.test.d.ts +1 -0
  25. package/dist/kenx-provider.test.js +36 -0
  26. package/dist/logger.test.d.ts +1 -0
  27. package/dist/logger.test.js +42 -0
  28. package/dist/middleware.test.d.ts +1 -0
  29. package/dist/middleware.test.js +121 -0
  30. package/dist/multipart.test.d.ts +1 -0
  31. package/dist/multipart.test.js +87 -0
  32. package/dist/openapi.test.d.ts +1 -0
  33. package/dist/openapi.test.js +111 -0
  34. package/dist/params.test.d.ts +1 -0
  35. package/dist/params.test.js +83 -0
  36. package/dist/queue.d.ts +16 -14
  37. package/dist/queue.js +76 -63
  38. package/dist/queue.test.d.ts +1 -0
  39. package/dist/queue.test.js +79 -0
  40. package/dist/route-methods.test.d.ts +1 -0
  41. package/dist/route-methods.test.js +129 -0
  42. package/dist/swagger-schema.d.ts +42 -0
  43. package/dist/swagger-schema.js +331 -58
  44. package/dist/swagger-schema.test.d.ts +1 -0
  45. package/dist/swagger-schema.test.js +105 -0
  46. package/dist/validation.d.ts +7 -0
  47. package/dist/validation.js +2 -0
  48. package/dist/validation.test.d.ts +1 -0
  49. package/dist/validation.test.js +61 -0
  50. package/dist/websocket.test.d.ts +1 -0
  51. package/dist/websocket.test.js +27 -0
  52. package/package.json +11 -9
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const queue_1 = require("./queue");
4
+ const fs_1 = require("fs");
5
+ const path_1 = require("path");
6
+ jest.mock("fs", () => ({
7
+ promises: {
8
+ readFile: jest.fn(),
9
+ writeFile: jest.fn(),
10
+ },
11
+ }));
12
+ const mockQueueFile = (0, path_1.join)(__dirname, "testqueue.json");
13
+ describe("FileQueueAdapter", () => {
14
+ const jobs = [{ id: "1", data: "foo" }, { id: "2", data: "bar" }];
15
+ beforeEach(() => {
16
+ jest.clearAllMocks();
17
+ });
18
+ it("should load jobs from file", async () => {
19
+ fs_1.promises.readFile.mockResolvedValue(JSON.stringify(jobs));
20
+ const adapter = new queue_1.FileQueueAdapter("testqueue");
21
+ const loaded = await adapter.loadJobs();
22
+ expect(loaded).toEqual(jobs);
23
+ expect(fs_1.promises.readFile).toHaveBeenCalledWith(mockQueueFile, "utf-8");
24
+ });
25
+ it("should return empty array if file does not exist", async () => {
26
+ fs_1.promises.readFile.mockRejectedValue(new Error("not found"));
27
+ const adapter = new queue_1.FileQueueAdapter("testqueue");
28
+ const loaded = await adapter.loadJobs();
29
+ expect(loaded).toEqual([]);
30
+ });
31
+ it("should save jobs to file", async () => {
32
+ const adapter = new queue_1.FileQueueAdapter("testqueue");
33
+ await adapter.saveJobs(jobs);
34
+ expect(fs_1.promises.writeFile).toHaveBeenCalledWith(mockQueueFile, JSON.stringify(jobs, null, 2), "utf-8");
35
+ });
36
+ });
37
+ describe("QueueManager and SimpleQueue", () => {
38
+ let adapter;
39
+ // let queueManager: QueueManager;
40
+ let handler;
41
+ // beforeEach(() => {
42
+ // jest.clearAllMocks();
43
+ // adapter = new FileQueueAdapter("testqueue");
44
+ // queueManager = QueueManager.getInstance(adapter);
45
+ // handler = jest.fn().mockResolvedValue(undefined);
46
+ // (fs.readFile as jest.Mock).mockResolvedValue("[]");
47
+ // (fs.writeFile as jest.Mock).mockResolvedValue(undefined);
48
+ // });
49
+ // it("should create a queue and add a job", async () => {
50
+ // const queue = queueManager.createQueue(handler);
51
+ // await queue.addJob({ foo: "bar" });
52
+ // expect(fs.readFile).toHaveBeenCalled();
53
+ // expect(fs.writeFile).toHaveBeenCalled();
54
+ // });
55
+ // it("should process jobs using handler", async () => {
56
+ // (fs.readFile as jest.Mock)
57
+ // .mockResolvedValueOnce("[]")
58
+ // .mockResolvedValueOnce(JSON.stringify([{ id: "1", data: "baz" }]))
59
+ // .mockResolvedValueOnce("[]");
60
+ // const queue = queueManager.createQueue(handler);
61
+ // await queue.addJob("baz");
62
+ // expect(handler).toHaveBeenCalled();
63
+ // });
64
+ // it("should requeue job if handler throws", async () => {
65
+ // handler.mockRejectedValueOnce(new Error("fail"));
66
+ // (fs.readFile as jest.Mock)
67
+ // .mockResolvedValueOnce("[]")
68
+ // .mockResolvedValueOnce(JSON.stringify([{ id: "1", data: "baz" }]))
69
+ // .mockResolvedValueOnce(JSON.stringify([{ id: "1", data: "baz" }]));
70
+ // const queue = queueManager.createQueue(handler);
71
+ // await queue.addJob("baz");
72
+ // expect(handler).toHaveBeenCalled();
73
+ // expect(fs.writeFile).toHaveBeenCalledTimes(2);
74
+ // });
75
+ // it("QueueManager should be singleton", () => {
76
+ // const another = QueueManager.getInstance(adapter);
77
+ // expect(another).toBe(queueManager);
78
+ // });
79
+ });
@@ -0,0 +1 @@
1
+ import "reflect-metadata";
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ 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;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ require("reflect-metadata");
13
+ const route_methods_1 = require("./route-methods");
14
+ describe("Route Decorators", () => {
15
+ class TestController {
16
+ getMethod() { }
17
+ postMethod() { }
18
+ putMethod() { }
19
+ deleteMethod() { }
20
+ patchMethod() { }
21
+ optionsMethod() { }
22
+ allMethod() { }
23
+ customRouteMethod() { }
24
+ }
25
+ __decorate([
26
+ (0, route_methods_1.Get)("/get"),
27
+ __metadata("design:type", Function),
28
+ __metadata("design:paramtypes", []),
29
+ __metadata("design:returntype", void 0)
30
+ ], TestController.prototype, "getMethod", null);
31
+ __decorate([
32
+ (0, route_methods_1.Post)({ path: "/post", name: "customPost" }),
33
+ __metadata("design:type", Function),
34
+ __metadata("design:paramtypes", []),
35
+ __metadata("design:returntype", void 0)
36
+ ], TestController.prototype, "postMethod", null);
37
+ __decorate([
38
+ (0, route_methods_1.Put)("/put", { name: "putMethod" }),
39
+ __metadata("design:type", Function),
40
+ __metadata("design:paramtypes", []),
41
+ __metadata("design:returntype", void 0)
42
+ ], TestController.prototype, "putMethod", null);
43
+ __decorate([
44
+ (0, route_methods_1.Delete)(),
45
+ __metadata("design:type", Function),
46
+ __metadata("design:paramtypes", []),
47
+ __metadata("design:returntype", void 0)
48
+ ], TestController.prototype, "deleteMethod", null);
49
+ __decorate([
50
+ (0, route_methods_1.Patch)({ name: "patchMethod" }),
51
+ __metadata("design:type", Function),
52
+ __metadata("design:paramtypes", []),
53
+ __metadata("design:returntype", void 0)
54
+ ], TestController.prototype, "patchMethod", null);
55
+ __decorate([
56
+ (0, route_methods_1.Options)("/options"),
57
+ __metadata("design:type", Function),
58
+ __metadata("design:paramtypes", []),
59
+ __metadata("design:returntype", void 0)
60
+ ], TestController.prototype, "optionsMethod", null);
61
+ __decorate([
62
+ (0, route_methods_1.All)({ path: "/all" }),
63
+ __metadata("design:type", Function),
64
+ __metadata("design:paramtypes", []),
65
+ __metadata("design:returntype", void 0)
66
+ ], TestController.prototype, "allMethod", null);
67
+ __decorate([
68
+ (0, route_methods_1.Route)("GET", "/custom", { name: "customRoute" }),
69
+ __metadata("design:type", Function),
70
+ __metadata("design:paramtypes", []),
71
+ __metadata("design:returntype", void 0)
72
+ ], TestController.prototype, "customRouteMethod", null);
73
+ it("should define metadata for Get decorator", () => {
74
+ const meta = Reflect.getMetadata("route:method", TestController.prototype, "getMethod");
75
+ expect(meta).toBe("GET");
76
+ const path = Reflect.getMetadata("route:path", TestController.prototype, "getMethod");
77
+ expect(path).toBe("/get");
78
+ });
79
+ // it("should define metadata for Post decorator with options object", () => {
80
+ // const meta = Reflect.getMetadata("route:method", TestController.prototype, "postMethod");
81
+ // expect(meta).toBe("POST");
82
+ // const path = Reflect.getMetadata("route:path", TestController.prototype, "postMethod");
83
+ // expect(path).toBe("/post");
84
+ // const options = Reflect.getMetadata("route:options", TestController.prototype, "postMethod");
85
+ // expect(options).toMatchObject({ path: "/post", name: "customPost" });
86
+ // });
87
+ it("should define metadata for Put decorator with path and options", () => {
88
+ const meta = Reflect.getMetadata("route:method", TestController.prototype, "putMethod");
89
+ expect(meta).toBe("PUT");
90
+ const path = Reflect.getMetadata("route:path", TestController.prototype, "putMethod");
91
+ expect(path).toBe("/put");
92
+ const options = Reflect.getMetadata("route:options", TestController.prototype, "putMethod");
93
+ expect(options).toMatchObject({ name: "putMethod" });
94
+ });
95
+ it("should define metadata for Delete decorator with default path", () => {
96
+ const meta = Reflect.getMetadata("route:method", TestController.prototype, "deleteMethod");
97
+ expect(meta).toBe("DELETE");
98
+ const path = Reflect.getMetadata("route:path", TestController.prototype, "deleteMethod");
99
+ expect(path).toBe("/");
100
+ });
101
+ it("should define metadata for Patch decorator with options object", () => {
102
+ const meta = Reflect.getMetadata("route:method", TestController.prototype, "patchMethod");
103
+ expect(meta).toBe("PATCH");
104
+ const path = Reflect.getMetadata("route:path", TestController.prototype, "patchMethod");
105
+ expect(path).toBe("patchMethod");
106
+ const options = Reflect.getMetadata("route:options", TestController.prototype, "patchMethod");
107
+ expect(options).toMatchObject({ name: "patchMethod" });
108
+ });
109
+ it("should define metadata for Options decorator with path", () => {
110
+ const meta = Reflect.getMetadata("route:method", TestController.prototype, "optionsMethod");
111
+ expect(meta).toBe("OPTIONS");
112
+ const path = Reflect.getMetadata("route:path", TestController.prototype, "optionsMethod");
113
+ expect(path).toBe("/options");
114
+ });
115
+ it("should define metadata for All decorator with options object", () => {
116
+ const meta = Reflect.getMetadata("route:method", TestController.prototype, "allMethod");
117
+ expect(meta).toBe("ALL");
118
+ const path = Reflect.getMetadata("route:path", TestController.prototype, "allMethod");
119
+ expect(path).toBe("/all");
120
+ });
121
+ it("should define metadata for generic Route decorator", () => {
122
+ const meta = Reflect.getMetadata("route:method", TestController.prototype, "customRouteMethod");
123
+ expect(meta).toBe("GET");
124
+ const path = Reflect.getMetadata("route:path", TestController.prototype, "customRouteMethod");
125
+ expect(path).toBe("/custom");
126
+ const options = Reflect.getMetadata("route:options", TestController.prototype, "customRouteMethod");
127
+ expect(options).toMatchObject({ name: "customRoute" });
128
+ });
129
+ });
@@ -1 +1,43 @@
1
+ export declare function OpenApiProperty(options?: {
2
+ type?: any;
3
+ description?: string;
4
+ deprecated?: boolean;
5
+ example?: any;
6
+ enum?: any[];
7
+ format?: string;
8
+ default?: any;
9
+ minimum?: number;
10
+ maximum?: number;
11
+ minLength?: number;
12
+ maxLength?: number;
13
+ pattern?: string;
14
+ oneOf?: any[];
15
+ allOf?: any[];
16
+ anyOf?: any[];
17
+ exclude?: boolean;
18
+ isArray?: boolean;
19
+ items?: Record<string, any>;
20
+ }): (target: any, propertyKey: string) => void;
21
+ export declare function CreateSwaggerObjectSchema(classType: any): any;
1
22
  export declare function generateSwaggerSchema(classType: any): any;
23
+ export declare function OpenApiResponse(code: number | undefined, model: any, description?: string): {
24
+ description: string;
25
+ content: {
26
+ "application/json": {
27
+ schema: {
28
+ type: string;
29
+ properties: {
30
+ code: {
31
+ type: string;
32
+ example: number;
33
+ };
34
+ status: {
35
+ type: string;
36
+ example: string;
37
+ };
38
+ data: any;
39
+ };
40
+ };
41
+ };
42
+ };
43
+ };
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OpenApiProperty = OpenApiProperty;
4
+ exports.CreateSwaggerObjectSchema = CreateSwaggerObjectSchema;
3
5
  exports.generateSwaggerSchema = generateSwaggerSchema;
6
+ exports.OpenApiResponse = OpenApiResponse;
4
7
  /**
5
8
  * @copyright 2024
6
9
  * @author Tareq Hossain
@@ -8,6 +11,241 @@ exports.generateSwaggerSchema = generateSwaggerSchema;
8
11
  * @url https://github.com/xtareq
9
12
  */
10
13
  const class_validator_1 = require("class-validator");
14
+ // Decorator to add OpenAPI metadata to properties
15
+ function OpenApiProperty(options) {
16
+ return function (target, propertyKey) {
17
+ var _a;
18
+ let meta = options ? { ...options } : {};
19
+ if (meta.format === "binary") {
20
+ if (meta.isArray) {
21
+ meta = {
22
+ ...meta,
23
+ type: "array",
24
+ items: (_a = meta.items) !== null && _a !== void 0 ? _a : { type: "string", format: "binary" },
25
+ description: meta.description || "Array of files",
26
+ };
27
+ }
28
+ else {
29
+ meta = {
30
+ ...meta,
31
+ type: "string",
32
+ format: "binary",
33
+ description: meta.description || "File upload",
34
+ };
35
+ }
36
+ }
37
+ Reflect.defineMetadata("property:openapi", meta, target, propertyKey);
38
+ };
39
+ }
40
+ function extractOpenApiFields(meta) {
41
+ const result = {};
42
+ const jsonSchemaFields = [
43
+ "description",
44
+ "deprecated",
45
+ "example",
46
+ "enum",
47
+ "format",
48
+ "default",
49
+ "minimum",
50
+ "maximum",
51
+ "minLength",
52
+ "maxLength",
53
+ "pattern",
54
+ "oneOf",
55
+ "allOf",
56
+ "anyOf",
57
+ ];
58
+ // Valid JSON Schema formats
59
+ const validFormats = [
60
+ "date-time",
61
+ "date",
62
+ "time",
63
+ "duration",
64
+ "email",
65
+ "idn-email",
66
+ "hostname",
67
+ "idn-hostname",
68
+ "ipv4",
69
+ "ipv6",
70
+ "uri",
71
+ "uri-reference",
72
+ "iri",
73
+ "iri-reference",
74
+ "uuid",
75
+ "uri-template",
76
+ "json-pointer",
77
+ "relative-json-pointer",
78
+ "regex",
79
+ "int32",
80
+ "int64",
81
+ "float",
82
+ "double",
83
+ "byte",
84
+ "binary",
85
+ "password",
86
+ ];
87
+ jsonSchemaFields.forEach((field) => {
88
+ if (meta[field] !== undefined) {
89
+ // Validate format field
90
+ if (field === "format") {
91
+ const formatValue = meta[field];
92
+ // Only add format if it's a valid format string
93
+ if (validFormats.includes(formatValue)) {
94
+ result[field] = formatValue;
95
+ }
96
+ // Skip invalid formats
97
+ }
98
+ else {
99
+ result[field] = meta[field];
100
+ }
101
+ }
102
+ });
103
+ return result;
104
+ }
105
+ function CreateSwaggerObjectSchema(classType) {
106
+ const metadataStorage = (0, class_validator_1.getMetadataStorage)();
107
+ const validationMetadata = metadataStorage.getTargetValidationMetadatas(classType, "", true, false);
108
+ const schema = {
109
+ type: "object",
110
+ properties: {},
111
+ required: [],
112
+ };
113
+ const prototype = classType.prototype;
114
+ const propertyKeys = new Set();
115
+ // Collect property names
116
+ Object.getOwnPropertyNames(prototype).forEach((k) => propertyKeys.add(k));
117
+ Object.keys(prototype).forEach((k) => propertyKeys.add(k));
118
+ validationMetadata.forEach((m) => propertyKeys.add(m.propertyName));
119
+ try {
120
+ const instance = new classType();
121
+ Reflect.ownKeys(instance).forEach((k) => {
122
+ if (typeof k === "string")
123
+ propertyKeys.add(k);
124
+ });
125
+ }
126
+ catch (_) { }
127
+ propertyKeys.forEach((propertyName) => {
128
+ var _a;
129
+ if (!propertyName || propertyName === "constructor")
130
+ return;
131
+ // Get decorator metadata
132
+ const openApiMeta = Reflect.getMetadata("property:openapi", prototype, propertyName);
133
+ if (openApiMeta === null || openApiMeta === void 0 ? void 0 : openApiMeta.exclude)
134
+ return;
135
+ // Get TypeScript type
136
+ const propertyType = Reflect.getMetadata("design:type", prototype, propertyName);
137
+ let swaggerProperty = {};
138
+ switch (propertyType) {
139
+ case String:
140
+ swaggerProperty.type = "string";
141
+ break;
142
+ case Number:
143
+ swaggerProperty.type = "number";
144
+ break;
145
+ case Boolean:
146
+ swaggerProperty.type = "boolean";
147
+ break;
148
+ case Date:
149
+ swaggerProperty.type = "string";
150
+ swaggerProperty.format = "date-time";
151
+ break;
152
+ case Array:
153
+ swaggerProperty.type = "array";
154
+ swaggerProperty.items = { type: "string" }; // fallback
155
+ break;
156
+ case Object:
157
+ swaggerProperty = CreateSwaggerObjectSchema(propertyType);
158
+ break;
159
+ default:
160
+ if (propertyType && typeof propertyType === "function") {
161
+ swaggerProperty.$ref = `#/components/schemas/${propertyType.name}`;
162
+ }
163
+ else {
164
+ swaggerProperty.type = ((_a = propertyType === null || propertyType === void 0 ? void 0 : propertyType.name) === null || _a === void 0 ? void 0 : _a.toLowerCase()) || "string";
165
+ }
166
+ }
167
+ if (openApiMeta) {
168
+ swaggerProperty = {
169
+ ...swaggerProperty,
170
+ ...openApiMeta,
171
+ ...extractOpenApiFields(openApiMeta),
172
+ };
173
+ // 🪄 Auto-handle file uploads
174
+ if (openApiMeta.format === "binary") {
175
+ if (openApiMeta.isArray || propertyType === Array) {
176
+ swaggerProperty = {
177
+ type: "array",
178
+ items: { type: "string", format: "binary" },
179
+ description: openApiMeta.description || "Array of files",
180
+ };
181
+ }
182
+ else {
183
+ swaggerProperty = {
184
+ type: "string",
185
+ format: "binary",
186
+ description: openApiMeta.description || "File upload",
187
+ };
188
+ }
189
+ }
190
+ }
191
+ schema.properties[propertyName] = swaggerProperty;
192
+ });
193
+ // Handle validation decorators
194
+ validationMetadata.forEach((meta) => {
195
+ const propertyName = meta.propertyName;
196
+ const property = schema.properties[propertyName];
197
+ if (!property)
198
+ return;
199
+ switch (meta.name) {
200
+ case "isNotEmpty":
201
+ case "isDefined":
202
+ if (!schema.required.includes(propertyName)) {
203
+ schema.required.push(propertyName);
204
+ }
205
+ break;
206
+ case "isOptional":
207
+ schema.required = schema.required.filter((item) => item !== propertyName);
208
+ break;
209
+ case "minLength":
210
+ property.minLength = meta.constraints[0];
211
+ break;
212
+ case "maxLength":
213
+ property.maxLength = meta.constraints[0];
214
+ break;
215
+ case "min":
216
+ property.minimum = meta.constraints[0];
217
+ break;
218
+ case "max":
219
+ property.maximum = meta.constraints[0];
220
+ break;
221
+ case "isEmail":
222
+ property.format = "email";
223
+ break;
224
+ case "isDate":
225
+ property.format = "date-time";
226
+ break;
227
+ case "isIn":
228
+ property.enum = meta.constraints[0];
229
+ break;
230
+ case "isNumber":
231
+ property.type = "number";
232
+ break;
233
+ case "isInt":
234
+ property.type = "integer";
235
+ break;
236
+ case "isBoolean":
237
+ property.type = "boolean";
238
+ break;
239
+ case "isString":
240
+ property.type = "string";
241
+ break;
242
+ }
243
+ });
244
+ if (schema.required.length === 0) {
245
+ delete schema.required;
246
+ }
247
+ return schema;
248
+ }
11
249
  function generateSwaggerSchema(classType) {
12
250
  const metadataStorage = (0, class_validator_1.getMetadataStorage)();
13
251
  const validationMetadata = metadataStorage.getTargetValidationMetadatas(classType, "", true, false);
@@ -117,63 +355,98 @@ function generateSwaggerSchema(classType) {
117
355
  });
118
356
  return schema;
119
357
  }
120
- function extractOpenApiFields(meta) {
121
- const result = {};
122
- const fields = [
123
- "description",
124
- "summary",
125
- "deprecated",
126
- "example",
127
- "enum",
128
- "format",
129
- "default",
130
- "minimum",
131
- "maximum",
132
- "minLength",
133
- "maxLength",
134
- "pattern",
135
- "oneOf",
136
- "allOf",
137
- "anyOf",
138
- ];
139
- fields.forEach((field) => {
140
- if (meta[field] !== undefined) {
141
- result[field] = meta[field];
358
+ function OpenApiResponse(code = 200, model, description = "Successful response") {
359
+ let dataSchema;
360
+ if (typeof model === "function") {
361
+ // Class or constructor
362
+ dataSchema = generateSwaggerSchema(model);
363
+ }
364
+ else if (model && typeof model === "object") {
365
+ // Example object
366
+ dataSchema = inferSchemaFromExample(model);
367
+ }
368
+ else {
369
+ // Fallback
370
+ dataSchema = { type: "string" };
371
+ }
372
+ let message = "OK";
373
+ switch (code) {
374
+ case 400:
375
+ message = "Error";
376
+ description = "Error: Bad Request";
377
+ break;
378
+ case 401:
379
+ message = "Error";
380
+ description = "Error: Unauthorized";
381
+ break;
382
+ case 403:
383
+ message = "Error";
384
+ description = "Error: Forbidden";
385
+ break;
386
+ case 201:
387
+ message = "Created";
388
+ description = "Success: Created";
389
+ break;
390
+ case 500:
391
+ message = "Error";
392
+ description = "Error: InternalError";
393
+ break;
394
+ }
395
+ return {
396
+ description,
397
+ content: {
398
+ "application/json": {
399
+ schema: {
400
+ type: "object",
401
+ properties: {
402
+ code: { type: "number", example: code },
403
+ status: { type: "string", example: message },
404
+ data: dataSchema,
405
+ },
406
+ },
407
+ },
408
+ },
409
+ };
410
+ }
411
+ /**
412
+ * Infer a basic JSON schema from a plain JavaScript object.
413
+ */
414
+ function inferSchemaFromExample(obj) {
415
+ var _a;
416
+ if (Array.isArray(obj)) {
417
+ return {
418
+ type: "array",
419
+ items: inferSchemaFromExample((_a = obj[0]) !== null && _a !== void 0 ? _a : {}),
420
+ };
421
+ }
422
+ if (obj && typeof obj === "object") {
423
+ const properties = {};
424
+ for (const [key, value] of Object.entries(obj)) {
425
+ properties[key] = inferType(value);
142
426
  }
143
- });
144
- return result;
427
+ return { type: "object", properties };
428
+ }
429
+ return inferType(obj);
430
+ }
431
+ /**
432
+ * Infer primitive schema type
433
+ */
434
+ function inferType(value) {
435
+ const type = typeof value;
436
+ switch (type) {
437
+ case "string":
438
+ return { type: "string", example: value };
439
+ case "number":
440
+ return { type: "number", example: value };
441
+ case "boolean":
442
+ return { type: "boolean", example: value };
443
+ case "object":
444
+ if (Array.isArray(value))
445
+ return inferSchemaFromExample(value);
446
+ if (value === null)
447
+ return { type: "null" };
448
+ return inferSchemaFromExample(value);
449
+ default:
450
+ return { type: "string" };
451
+ }
145
452
  }
146
- // export function generateSwaggerSchema(classType: any) {
147
- // const { getMetadataStorage } = require("class-validator");
148
- // const { plainToInstance } = require("class-transformer");
149
- // const metadataStorage = getMetadataStorage();
150
- // const validationMetadata = metadataStorage.getTargetValidationMetadatas(
151
- // classType,
152
- // "",
153
- // true,
154
- // );
155
- // const schema: any = {
156
- // type: "object",
157
- // properties: {},
158
- // required: [],
159
- // };
160
- // validationMetadata.forEach((meta: any) => {
161
- // const propertyName = meta.propertyName;
162
- // // Infer the type dynamically using Reflect metadata
163
- // const propertyType = Reflect.getMetadata(
164
- // "design:type",
165
- // classType.prototype,
166
- // propertyName,
167
- // );
168
- // schema.properties[propertyName] = {
169
- // type: propertyType?.name.toLowerCase() || "string", // Default to string if type cannot be inferred
170
- // };
171
- // if (meta.name === "isNotEmpty") {
172
- // schema.required.push(propertyName);
173
- // }
174
- // if (meta.name === "minLength") {
175
- // schema.properties[propertyName].minLength = meta.constraints[0];
176
- // }
177
- // });
178
- // return schema;
179
- // }
@@ -0,0 +1 @@
1
+ import "reflect-metadata";