adorn-api 1.0.0

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 (50) hide show
  1. package/README.md +249 -0
  2. package/dist/cli/generate-routes.js +101 -0
  3. package/dist/cli/generate-swagger.js +197 -0
  4. package/dist/controllers/advanced.controller.js +131 -0
  5. package/dist/controllers/user.controller.js +121 -0
  6. package/dist/entities/user.entity.js +1 -0
  7. package/dist/index.js +2 -0
  8. package/dist/lib/common.js +62 -0
  9. package/dist/lib/decorators.js +116 -0
  10. package/dist/middleware/auth.middleware.js +13 -0
  11. package/dist/routes.js +80 -0
  12. package/dist/server.js +18 -0
  13. package/dist/src/cli/generate-routes.js +105 -0
  14. package/dist/src/cli/generate-swagger.js +197 -0
  15. package/dist/src/index.js +4 -0
  16. package/dist/src/lib/common.js +62 -0
  17. package/dist/src/lib/decorators.js +116 -0
  18. package/dist/src/routes.js +80 -0
  19. package/dist/src/server.js +18 -0
  20. package/dist/tests/example-app/controllers/advanced.controller.js +130 -0
  21. package/dist/tests/example-app/controllers/controllers/advanced.controller.js +131 -0
  22. package/dist/tests/example-app/controllers/controllers/user.controller.js +121 -0
  23. package/dist/tests/example-app/controllers/user.controller.js +121 -0
  24. package/dist/tests/example-app/entities/entities/user.entity.js +1 -0
  25. package/dist/tests/example-app/entities/user.entity.js +1 -0
  26. package/dist/tests/example-app/middleware/auth.middleware.js +13 -0
  27. package/dist/tests/example-app/middleware/middleware/auth.middleware.js +13 -0
  28. package/dist/tests/example-app/routes.js +80 -0
  29. package/dist/tests/example-app/server.js +23 -0
  30. package/package.json +34 -0
  31. package/scripts/run-example.js +32 -0
  32. package/src/cli/generate-routes.ts +123 -0
  33. package/src/cli/generate-swagger.ts +216 -0
  34. package/src/index.js +20 -0
  35. package/src/index.ts +4 -0
  36. package/src/lib/common.js +68 -0
  37. package/src/lib/common.ts +35 -0
  38. package/src/lib/decorators.js +128 -0
  39. package/src/lib/decorators.ts +136 -0
  40. package/swagger.json +238 -0
  41. package/tests/e2e.test.ts +72 -0
  42. package/tests/example-app/controllers/advanced.controller.ts +52 -0
  43. package/tests/example-app/controllers/user.controller.ts +35 -0
  44. package/tests/example-app/entities/user.entity.ts +8 -0
  45. package/tests/example-app/middleware/auth.middleware.ts +16 -0
  46. package/tests/example-app/routes.ts +102 -0
  47. package/tests/example-app/server.ts +30 -0
  48. package/tests/generators.test.ts +48 -0
  49. package/tests/utils.ts +46 -0
  50. package/tsconfig.json +20 -0
@@ -0,0 +1,121 @@
1
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
2
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
3
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
4
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
5
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
6
+ var _, done = false;
7
+ for (var i = decorators.length - 1; i >= 0; i--) {
8
+ var context = {};
9
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
10
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
11
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
12
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
13
+ if (kind === "accessor") {
14
+ if (result === void 0) continue;
15
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
16
+ if (_ = accept(result.get)) descriptor.get = _;
17
+ if (_ = accept(result.set)) descriptor.set = _;
18
+ if (_ = accept(result.init)) initializers.unshift(_);
19
+ }
20
+ else if (_ = accept(result)) {
21
+ if (kind === "field") initializers.unshift(_);
22
+ else descriptor[key] = _;
23
+ }
24
+ }
25
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
26
+ done = true;
27
+ };
28
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
29
+ var useValue = arguments.length > 2;
30
+ for (var i = 0; i < initializers.length; i++) {
31
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
32
+ }
33
+ return useValue ? value : void 0;
34
+ };
35
+ // src/controllers/user.controller.ts
36
+ import { Controller, Get, Post, FromQuery, FromPath, FromBody } from "../lib/decorators.js";
37
+ // --- DTO Definitions (The substitute for Parameter Decorators) ---
38
+ let GetUserRequest = (() => {
39
+ let _userId_decorators;
40
+ let _userId_initializers = [];
41
+ let _userId_extraInitializers = [];
42
+ let _details_decorators;
43
+ let _details_initializers = [];
44
+ let _details_extraInitializers = [];
45
+ return class GetUserRequest {
46
+ static {
47
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
48
+ _userId_decorators = [FromPath()];
49
+ _details_decorators = [FromQuery()];
50
+ __esDecorate(null, null, _userId_decorators, { kind: "field", name: "userId", static: false, private: false, access: { has: obj => "userId" in obj, get: obj => obj.userId, set: (obj, value) => { obj.userId = value; } }, metadata: _metadata }, _userId_initializers, _userId_extraInitializers);
51
+ __esDecorate(null, null, _details_decorators, { kind: "field", name: "details", static: false, private: false, access: { has: obj => "details" in obj, get: obj => obj.details, set: (obj, value) => { obj.details = value; } }, metadata: _metadata }, _details_initializers, _details_extraInitializers);
52
+ if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
53
+ }
54
+ userId = __runInitializers(this, _userId_initializers, void 0);
55
+ details = (__runInitializers(this, _userId_extraInitializers), __runInitializers(this, _details_initializers, void 0));
56
+ constructor() {
57
+ __runInitializers(this, _details_extraInitializers);
58
+ }
59
+ };
60
+ })();
61
+ export { GetUserRequest };
62
+ let CreateUserRequest = (() => {
63
+ let _name_decorators;
64
+ let _name_initializers = [];
65
+ let _name_extraInitializers = [];
66
+ let _email_decorators;
67
+ let _email_initializers = [];
68
+ let _email_extraInitializers = [];
69
+ return class CreateUserRequest {
70
+ static {
71
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
72
+ _name_decorators = [FromBody()];
73
+ _email_decorators = [FromBody()];
74
+ __esDecorate(null, null, _name_decorators, { kind: "field", name: "name", static: false, private: false, access: { has: obj => "name" in obj, get: obj => obj.name, set: (obj, value) => { obj.name = value; } }, metadata: _metadata }, _name_initializers, _name_extraInitializers);
75
+ __esDecorate(null, null, _email_decorators, { kind: "field", name: "email", static: false, private: false, access: { has: obj => "email" in obj, get: obj => obj.email, set: (obj, value) => { obj.email = value; } }, metadata: _metadata }, _email_initializers, _email_extraInitializers);
76
+ if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
77
+ }
78
+ name = __runInitializers(this, _name_initializers, void 0);
79
+ email = (__runInitializers(this, _name_extraInitializers), __runInitializers(this, _email_initializers, void 0));
80
+ constructor() {
81
+ __runInitializers(this, _email_extraInitializers);
82
+ }
83
+ };
84
+ })();
85
+ export { CreateUserRequest };
86
+ // --- The Controller ---
87
+ let UserController = (() => {
88
+ let _classDecorators = [Controller("users")];
89
+ let _classDescriptor;
90
+ let _classExtraInitializers = [];
91
+ let _classThis;
92
+ let _instanceExtraInitializers = [];
93
+ let _getUser_decorators;
94
+ let _createUser_decorators;
95
+ var UserController = class {
96
+ static { _classThis = this; }
97
+ static {
98
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
99
+ _getUser_decorators = [Get("/{userId}")];
100
+ _createUser_decorators = [Post("/")];
101
+ __esDecorate(this, null, _getUser_decorators, { kind: "method", name: "getUser", static: false, private: false, access: { has: obj => "getUser" in obj, get: obj => obj.getUser }, metadata: _metadata }, null, _instanceExtraInitializers);
102
+ __esDecorate(this, null, _createUser_decorators, { kind: "method", name: "createUser", static: false, private: false, access: { has: obj => "createUser" in obj, get: obj => obj.createUser }, metadata: _metadata }, null, _instanceExtraInitializers);
103
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
104
+ UserController = _classThis = _classDescriptor.value;
105
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
106
+ __runInitializers(_classThis, _classExtraInitializers);
107
+ }
108
+ // Strong typing: 'req' is checked at edit time.
109
+ async getUser(req) {
110
+ return `Getting user ${req.userId} with details: ${req.details}`;
111
+ }
112
+ async createUser(req) {
113
+ console.log(`Creating user ${req.name}`);
114
+ }
115
+ constructor() {
116
+ __runInitializers(this, _instanceExtraInitializers);
117
+ }
118
+ };
119
+ return UserController = _classThis;
120
+ })();
121
+ export { UserController };
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ console.log("App loaded. Run 'npm run gen' to see swagger output.");
2
+ export {};
@@ -0,0 +1,62 @@
1
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
2
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
3
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
4
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
5
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
6
+ var _, done = false;
7
+ for (var i = decorators.length - 1; i >= 0; i--) {
8
+ var context = {};
9
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
10
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
11
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
12
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
13
+ if (kind === "accessor") {
14
+ if (result === void 0) continue;
15
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
16
+ if (_ = accept(result.get)) descriptor.get = _;
17
+ if (_ = accept(result.set)) descriptor.set = _;
18
+ if (_ = accept(result.init)) initializers.unshift(_);
19
+ }
20
+ else if (_ = accept(result)) {
21
+ if (kind === "field") initializers.unshift(_);
22
+ else descriptor[key] = _;
23
+ }
24
+ }
25
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
26
+ done = true;
27
+ };
28
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
29
+ var useValue = arguments.length > 2;
30
+ for (var i = 0; i < initializers.length; i++) {
31
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
32
+ }
33
+ return useValue ? value : void 0;
34
+ };
35
+ // src/lib/common.ts
36
+ import { FromQuery } from "./decorators.js";
37
+ // --- 2. Pagination Logic ---
38
+ // We need a Concrete Class for Pagination because it carries Metadata (@FromQuery)
39
+ let PaginationQuery = (() => {
40
+ let _page_decorators;
41
+ let _page_initializers = [];
42
+ let _page_extraInitializers = [];
43
+ let _limit_decorators;
44
+ let _limit_initializers = [];
45
+ let _limit_extraInitializers = [];
46
+ return class PaginationQuery {
47
+ static {
48
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
49
+ _page_decorators = [FromQuery()];
50
+ _limit_decorators = [FromQuery()];
51
+ __esDecorate(null, null, _page_decorators, { kind: "field", name: "page", static: false, private: false, access: { has: obj => "page" in obj, get: obj => obj.page, set: (obj, value) => { obj.page = value; } }, metadata: _metadata }, _page_initializers, _page_extraInitializers);
52
+ __esDecorate(null, null, _limit_decorators, { kind: "field", name: "limit", static: false, private: false, access: { has: obj => "limit" in obj, get: obj => obj.limit, set: (obj, value) => { obj.limit = value; } }, metadata: _metadata }, _limit_initializers, _limit_extraInitializers);
53
+ if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
54
+ }
55
+ page = __runInitializers(this, _page_initializers, 1);
56
+ limit = (__runInitializers(this, _page_extraInitializers), __runInitializers(this, _limit_initializers, 20));
57
+ constructor() {
58
+ __runInitializers(this, _limit_extraInitializers);
59
+ }
60
+ };
61
+ })();
62
+ export { PaginationQuery };
@@ -0,0 +1,116 @@
1
+ // src/lib/decorators.ts
2
+ // Context metadata symbol for standard decorators
3
+ const META_KEY = Symbol('adorn:route');
4
+ export const SCHEMA_META = Symbol('adorn:schema');
5
+ export const AUTH_META = Symbol('adorn:auth');
6
+ // -- Method Decorator --
7
+ // Using Standard TC39 Signature
8
+ export function Get(path) {
9
+ return function (originalMethod, context) {
10
+ // In standard decorators, we can attach metadata to the class prototype via context
11
+ context.addInitializer(function () {
12
+ const routes = this[META_KEY] || [];
13
+ routes.push({
14
+ method: 'get',
15
+ path,
16
+ methodName: String(context.name),
17
+ });
18
+ this[META_KEY] = routes;
19
+ });
20
+ return originalMethod;
21
+ };
22
+ }
23
+ export function Post(path) {
24
+ return function (originalMethod, context) {
25
+ context.addInitializer(function () {
26
+ const routes = this[META_KEY] || [];
27
+ routes.push({
28
+ method: 'post',
29
+ path,
30
+ methodName: String(context.name),
31
+ });
32
+ this[META_KEY] = routes;
33
+ });
34
+ return originalMethod;
35
+ };
36
+ }
37
+ export function Put(path) {
38
+ return function (originalMethod, context) {
39
+ context.addInitializer(function () {
40
+ const routes = this[META_KEY] || [];
41
+ routes.push({
42
+ method: 'put',
43
+ path,
44
+ methodName: String(context.name),
45
+ });
46
+ this[META_KEY] = routes;
47
+ });
48
+ return originalMethod;
49
+ };
50
+ }
51
+ export function Delete(path) {
52
+ return function (originalMethod, context) {
53
+ context.addInitializer(function () {
54
+ const routes = this[META_KEY] || [];
55
+ routes.push({
56
+ method: 'delete',
57
+ path,
58
+ methodName: String(context.name),
59
+ });
60
+ this[META_KEY] = routes;
61
+ });
62
+ return originalMethod;
63
+ };
64
+ }
65
+ // -- Class Decorator --
66
+ export function Controller(basePath) {
67
+ return function (target, context) {
68
+ // We attach the base path to the class constructor
69
+ context.addInitializer(function () {
70
+ this._basePath = basePath;
71
+ });
72
+ return target;
73
+ };
74
+ }
75
+ // -- DTO Field Decorators --
76
+ // Since we can't decorate parameters, we decorate fields in a class
77
+ // e.g. class GetUserParams { @FromPath id: string }
78
+ export function FromQuery(name) {
79
+ return function (target, context) {
80
+ context.addInitializer(function () {
81
+ const meta = this[SCHEMA_META] || {};
82
+ meta[context.name] = { type: 'query' };
83
+ this[SCHEMA_META] = meta;
84
+ });
85
+ return function (initialValue) { return initialValue; };
86
+ };
87
+ }
88
+ export function FromPath(name) {
89
+ return function (target, context) {
90
+ context.addInitializer(function () {
91
+ const meta = this[SCHEMA_META] || {};
92
+ meta[context.name] = { type: 'path' };
93
+ this[SCHEMA_META] = meta;
94
+ });
95
+ return function (initialValue) { return initialValue; };
96
+ };
97
+ }
98
+ export function FromBody() {
99
+ return function (target, context) {
100
+ context.addInitializer(function () {
101
+ const meta = this[SCHEMA_META] || {};
102
+ meta[context.name] = { type: 'body' };
103
+ this[SCHEMA_META] = meta;
104
+ });
105
+ return function (initialValue) { return initialValue; };
106
+ };
107
+ }
108
+ // -- Authentication Decorator --
109
+ export function Authorized(role) {
110
+ return function (target, context) {
111
+ context.addInitializer(function () {
112
+ this[AUTH_META] = role || 'default';
113
+ });
114
+ return target;
115
+ };
116
+ }
@@ -0,0 +1,13 @@
1
+ export function authenticationMiddleware(req, res, next) {
2
+ const token = req.headers.authorization;
3
+ if (!token) {
4
+ res.status(401).json({ message: "No token provided" });
5
+ return;
6
+ }
7
+ if (token === "Bearer secret") {
8
+ next();
9
+ }
10
+ else {
11
+ res.status(403).json({ message: "Invalid token" });
12
+ }
13
+ }
package/dist/routes.js ADDED
@@ -0,0 +1,80 @@
1
+ import { AdvancedController } from 'C:/Users/celso/Documents/projetos/adorn-api/src/controllers/advanced.controller.js';
2
+ import { UserController } from 'C:/Users/celso/Documents/projetos/adorn-api/src/controllers/user.controller.js';
3
+ export function RegisterRoutes(app) {
4
+ app.get('/advanced/:tenantId/users', async (req, res) => {
5
+ const controller = new AdvancedController();
6
+ try {
7
+ const input = {};
8
+ // Map Query
9
+ Object.assign(input, req.query);
10
+ // Map Params
11
+ Object.assign(input, req.params);
12
+ // Map Body
13
+ Object.assign(input, req.body);
14
+ // In a real app, you would run 'zod' or 'class-validator' here on 'input'
15
+ const response = await controller.listUsers(input);
16
+ res.status(200).json(response);
17
+ }
18
+ catch (err) {
19
+ console.error(err);
20
+ res.status(500).send(err.message);
21
+ }
22
+ });
23
+ app.post('/advanced/', async (req, res) => {
24
+ const controller = new AdvancedController();
25
+ try {
26
+ const input = {};
27
+ // Map Query
28
+ Object.assign(input, req.query);
29
+ // Map Params
30
+ Object.assign(input, req.params);
31
+ // Map Body
32
+ Object.assign(input, req.body);
33
+ // In a real app, you would run 'zod' or 'class-validator' here on 'input'
34
+ const response = await controller.create(input);
35
+ res.status(200).json(response);
36
+ }
37
+ catch (err) {
38
+ console.error(err);
39
+ res.status(500).send(err.message);
40
+ }
41
+ });
42
+ app.get('/users/:userId', async (req, res) => {
43
+ const controller = new UserController();
44
+ try {
45
+ const input = {};
46
+ // Map Query
47
+ Object.assign(input, req.query);
48
+ // Map Params
49
+ Object.assign(input, req.params);
50
+ // Map Body
51
+ Object.assign(input, req.body);
52
+ // In a real app, you would run 'zod' or 'class-validator' here on 'input'
53
+ const response = await controller.getUser(input);
54
+ res.status(200).json(response);
55
+ }
56
+ catch (err) {
57
+ console.error(err);
58
+ res.status(500).send(err.message);
59
+ }
60
+ });
61
+ app.post('/users/', async (req, res) => {
62
+ const controller = new UserController();
63
+ try {
64
+ const input = {};
65
+ // Map Query
66
+ Object.assign(input, req.query);
67
+ // Map Params
68
+ Object.assign(input, req.params);
69
+ // Map Body
70
+ Object.assign(input, req.body);
71
+ // In a real app, you would run 'zod' or 'class-validator' here on 'input'
72
+ const response = await controller.createUser(input);
73
+ res.status(200).json(response);
74
+ }
75
+ catch (err) {
76
+ console.error(err);
77
+ res.status(500).send(err.message);
78
+ }
79
+ });
80
+ }
package/dist/server.js ADDED
@@ -0,0 +1,18 @@
1
+ // src/server.ts
2
+ import express from "express";
3
+ import bodyParser from "body-parser";
4
+ // This is the file we just generated!
5
+ import { RegisterRoutes } from "./routes.js";
6
+ import swaggerUi from "swagger-ui-express";
7
+ import { readFileSync } from "fs";
8
+ const app = express();
9
+ app.use(bodyParser.json());
10
+ // 1. Register the Generated Routes
11
+ RegisterRoutes(app);
12
+ // 2. Serve Swagger UI
13
+ const swaggerDoc = JSON.parse(readFileSync("./swagger.json", "utf-8"));
14
+ app.use("/docs", swaggerUi.serve, swaggerUi.setup(swaggerDoc));
15
+ app.listen(3000, () => {
16
+ console.log("🚀 Server running on http://localhost:3000");
17
+ console.log("📄 Swagger running on http://localhost:3000/docs");
18
+ });
@@ -0,0 +1,105 @@
1
+ // src/cli/generate-routes.ts
2
+ import path from "path";
3
+ import { Project } from "ts-morph";
4
+ import * as fs from "fs";
5
+ const PROJECT_ROOT = "./tsconfig.json";
6
+ const OUTPUT_FILE = "./tests/example-app/routes.ts"; // We generate this file!
7
+ const project = new Project({ tsConfigFilePath: PROJECT_ROOT });
8
+ let routeCode = `/* tslint:disable */
9
+ /* eslint-disable */
10
+ // WARNING: This file was auto-generated by adorn-api. Do not edit.
11
+ import { Express, Request, Response } from 'express';
12
+ `;
13
+ // Helper to keep track of imports we need to add to the generated file
14
+ const imports = new Set();
15
+ function processController(classDec) {
16
+ const className = classDec.getName();
17
+ if (!className)
18
+ return "";
19
+ // Track import (assuming controllers are in ./controllers/)
20
+ const sourceFile = classDec.getSourceFile();
21
+ const exampleAppRoot = path.join(process.cwd(), "tests", "example-app");
22
+ const relativeDir = path.relative(exampleAppRoot, sourceFile.getDirectoryPath());
23
+ const relativeSegments = relativeDir ? relativeDir.split(path.sep) : [];
24
+ const baseName = sourceFile.getBaseName().replace(/\.ts$/, ".js");
25
+ const importPath = `./${path.posix.join(...relativeSegments, baseName)}`;
26
+ imports.add(`import { ${className} } from '${importPath}';`);
27
+ const controllerDec = classDec.getDecorators().find(d => d.getName() === "Controller");
28
+ if (!controllerDec)
29
+ return "";
30
+ const basePath = controllerDec.getArguments()[0]?.getText().replace(/['"]/g, "") || "/";
31
+ let methodBlocks = "";
32
+ classDec.getMethods().forEach(method => {
33
+ const getDec = method.getDecorator("Get");
34
+ const postDec = method.getDecorator("Post");
35
+ const putDec = method.getDecorator("Put");
36
+ const deleteDec = method.getDecorator("Delete");
37
+ const decorator = getDec || postDec || putDec || deleteDec;
38
+ if (!decorator)
39
+ return;
40
+ const httpMethod = getDec ? "get" : postDec ? "post" : putDec ? "put" : "delete";
41
+ const pathArg = decorator.getArguments()[0]?.getText().replace(/['"]/g, "") || "/";
42
+ const fullPath = `/${basePath}${pathArg}`.replace("//", "/").replace(/{/g, ":").replace(/}/g, ""); // Convert {id} to :id for Express
43
+ const methodName = method.getName();
44
+ const params = method.getParameters();
45
+ // Authentication check
46
+ const authDec = method.getDecorator("Authorized");
47
+ const controllerAuthDec = classDec.getDecorator("Authorized");
48
+ const hasAuth = !!authDec || !!controllerAuthDec;
49
+ const middlewareArgs = hasAuth ? "authenticationMiddleware, " : "";
50
+ // Logic to instantiate the DTO
51
+ let paramInstantiation = "";
52
+ if (params.length > 0) {
53
+ const paramName = params[0].getName();
54
+ const paramType = params[0].getType().getSymbol()?.getName();
55
+ // We assume the DTO is a class we can instantiate, or a shape we construct
56
+ // Here we map request parts to the DTO
57
+ paramInstantiation = `
58
+ const input: any = {};
59
+ // Map Query
60
+ Object.assign(input, req.query);
61
+ // Map Params
62
+ Object.assign(input, req.params);
63
+ // Map Body
64
+ Object.assign(input, req.body);
65
+
66
+ // In a real app, you would run 'zod' or 'class-validator' here on 'input'
67
+ `;
68
+ }
69
+ methodBlocks += `
70
+ app.${httpMethod}('${fullPath}', ${middlewareArgs}async (req: Request, res: Response) => {
71
+ const controller = new ${className}();
72
+ try {
73
+ ${paramInstantiation}
74
+ const response = await controller.${methodName}(${params.length > 0 ? 'input' : ''});
75
+ res.status(200).json(response);
76
+ } catch (err: any) {
77
+ console.error(err);
78
+ res.status(500).send(err.message);
79
+ }
80
+ });
81
+ `;
82
+ });
83
+ return methodBlocks;
84
+ }
85
+ const sourceFiles = project.getSourceFiles("tests/example-app/controllers/**/*.ts");
86
+ let allRoutes = "";
87
+ sourceFiles.forEach(file => {
88
+ file.getClasses().forEach(c => {
89
+ allRoutes += processController(c);
90
+ });
91
+ });
92
+ // Add authentication middleware import if needed
93
+ if (allRoutes.includes('authenticationMiddleware')) {
94
+ routeCode += `import { authenticationMiddleware } from './middleware/auth.middleware.js';\n`;
95
+ }
96
+ // Prepend imports
97
+ routeCode += Array.from(imports).join('\n');
98
+ routeCode += `
99
+
100
+ export function RegisterRoutes(app: Express) {
101
+ ${allRoutes}
102
+ }
103
+ `;
104
+ fs.writeFileSync(OUTPUT_FILE, routeCode);
105
+ console.log(`✅ Generated Routes at ${OUTPUT_FILE}`);