kastrick 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 (57) hide show
  1. package/README.md +38 -0
  2. package/examples/01-hello-world/api/hello.ts +19 -0
  3. package/examples/02-users-api/README.md +39 -0
  4. package/examples/02-users-api/api/users/[id].ts +80 -0
  5. package/examples/02-users-api/api/users/index.ts +50 -0
  6. package/examples/03-configuration/README.md +39 -0
  7. package/examples/03-configuration/kastrick.config.json +22 -0
  8. package/examples/03-configuration/tsconfig.json +20 -0
  9. package/examples/README.md +48 -0
  10. package/lib/cli/compile.d.ts +5 -0
  11. package/lib/cli/compile.d.ts.map +1 -0
  12. package/lib/cli/compile.js +25 -0
  13. package/lib/cli/compile.js.map +1 -0
  14. package/lib/cli/dev.d.ts +5 -0
  15. package/lib/cli/dev.d.ts.map +1 -0
  16. package/lib/cli/dev.js +79 -0
  17. package/lib/cli/dev.js.map +1 -0
  18. package/lib/cli/index.d.ts +3 -0
  19. package/lib/cli/index.d.ts.map +1 -0
  20. package/lib/cli/index.js +63 -0
  21. package/lib/cli/index.js.map +1 -0
  22. package/lib/cli/serve.d.ts +5 -0
  23. package/lib/cli/serve.d.ts.map +1 -0
  24. package/lib/cli/serve.js +106 -0
  25. package/lib/cli/serve.js.map +1 -0
  26. package/lib/core/compiler.d.ts +29 -0
  27. package/lib/core/compiler.d.ts.map +1 -0
  28. package/lib/core/compiler.js +153 -0
  29. package/lib/core/compiler.js.map +1 -0
  30. package/lib/core/router.d.ts +31 -0
  31. package/lib/core/router.d.ts.map +1 -0
  32. package/lib/core/router.js +106 -0
  33. package/lib/core/router.js.map +1 -0
  34. package/lib/core/scanner.d.ts +29 -0
  35. package/lib/core/scanner.d.ts.map +1 -0
  36. package/lib/core/scanner.js +88 -0
  37. package/lib/core/scanner.js.map +1 -0
  38. package/lib/index.d.ts +12 -0
  39. package/lib/index.d.ts.map +1 -0
  40. package/lib/index.js +35 -0
  41. package/lib/index.js.map +1 -0
  42. package/lib/server/expressServer.d.ts +28 -0
  43. package/lib/server/expressServer.d.ts.map +1 -0
  44. package/lib/server/expressServer.js +96 -0
  45. package/lib/server/expressServer.js.map +1 -0
  46. package/package.json +34 -0
  47. package/src/cli/compile.ts +27 -0
  48. package/src/cli/dev.ts +49 -0
  49. package/src/cli/index.ts +64 -0
  50. package/src/cli/serve.ts +81 -0
  51. package/src/core/compiler.ts +147 -0
  52. package/src/core/router.ts +88 -0
  53. package/src/core/scanner.ts +72 -0
  54. package/src/index.ts +38 -0
  55. package/src/server/expressServer.ts +107 -0
  56. package/templates/tsconfig.json +20 -0
  57. package/tsconfig.json +20 -0
@@ -0,0 +1,28 @@
1
+ import express from "express";
2
+ import { ApiFile } from "../core/scanner";
3
+ /**
4
+ * Express Server - Dev server for local API testing
5
+ */
6
+ export declare class ExpressServer {
7
+ private app;
8
+ private port;
9
+ private router;
10
+ private loadedModules;
11
+ constructor(port?: number);
12
+ private setupMiddleware;
13
+ /**
14
+ * Register routes from API files
15
+ */
16
+ registerRoutes(files: ApiFile[]): void;
17
+ private createHandler;
18
+ private parseHandler;
19
+ /**
20
+ * Start the dev server
21
+ */
22
+ start(): void;
23
+ /**
24
+ * Get Express app instance
25
+ */
26
+ getApp(): express.Application;
27
+ }
28
+ //# sourceMappingURL=expressServer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expressServer.d.ts","sourceRoot":"","sources":["../../src/server/expressServer.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAG9B,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE1C;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,GAAG,CAAsB;IACjC,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,aAAa,CAA+B;gBAExC,IAAI,GAAE,MAAa;IAO/B,OAAO,CAAC,eAAe;IAWvB;;OAEG;IACH,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,IAAI;IActC,OAAO,CAAC,aAAa;IAkBrB,OAAO,CAAC,YAAY;IAuBpB;;OAEG;IACH,KAAK,IAAI,IAAI;IAOb;;OAEG;IACH,MAAM,IAAI,OAAO,CAAC,WAAW;CAG9B"}
@@ -0,0 +1,96 @@
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.ExpressServer = void 0;
7
+ const express_1 = __importDefault(require("express"));
8
+ const router_1 = require("../core/router");
9
+ /**
10
+ * Express Server - Dev server for local API testing
11
+ */
12
+ class ExpressServer {
13
+ constructor(port = 3000) {
14
+ this.loadedModules = new Map();
15
+ this.app = (0, express_1.default)();
16
+ this.port = port;
17
+ this.router = new router_1.Router();
18
+ this.setupMiddleware();
19
+ }
20
+ setupMiddleware() {
21
+ this.app.use(express_1.default.json());
22
+ this.app.use(express_1.default.urlencoded({ extended: true }));
23
+ // Simple logging middleware
24
+ this.app.use((req, res, next) => {
25
+ console.log(`📡 ${req.method} ${req.path}`);
26
+ next();
27
+ });
28
+ }
29
+ /**
30
+ * Register routes from API files
31
+ */
32
+ registerRoutes(files) {
33
+ const routes = this.router.generateRoutes(files);
34
+ for (const route of routes) {
35
+ const method = route.method.toLowerCase();
36
+ const filePath = route.file;
37
+ const content = files.find((f) => f.relativePath === filePath)?.content || "";
38
+ this.app[method](route.path, this.createHandler(filePath, route.method, content));
39
+ }
40
+ console.log(`📡 Registered ${routes.length} routes`);
41
+ }
42
+ createHandler(filePath, method, content) {
43
+ return async (req, res) => {
44
+ try {
45
+ // Parse and execute handler
46
+ const fn = this.parseHandler(content, method);
47
+ if (!fn) {
48
+ return res.status(404).json({ error: `${method} handler not found` });
49
+ }
50
+ const result = await fn(req, { params: req.params });
51
+ res.status(result.status || 200).json(result.body !== undefined ? result.body : result);
52
+ }
53
+ catch (err) {
54
+ console.error(`❌ Handler error in ${filePath}:`, err.message);
55
+ res.status(500).json({ error: err.message || "Internal server error" });
56
+ }
57
+ };
58
+ }
59
+ parseHandler(content, method) {
60
+ try {
61
+ // Extract the function from the file content
62
+ const moduleExports = {};
63
+ // Create a minimal module context
64
+ const module = { exports: moduleExports };
65
+ const exportFn = (key, fn) => {
66
+ moduleExports[key] = fn;
67
+ };
68
+ // Execute the file content in a controlled context
69
+ const code = `(function(exports, require, module, __filename, __dirname) { ${content} return module.exports; })`;
70
+ const fn = eval(code);
71
+ const result = fn(moduleExports, require, module, "", "");
72
+ return result[method];
73
+ }
74
+ catch (err) {
75
+ console.error("Error parsing handler:", err);
76
+ return null;
77
+ }
78
+ }
79
+ /**
80
+ * Start the dev server
81
+ */
82
+ start() {
83
+ this.app.listen(this.port, () => {
84
+ console.log(`\n🚀 Kastrick dev server running on http://localhost:${this.port}\n`);
85
+ console.log(`👀 Watching for changes in /api folder...\n`);
86
+ });
87
+ }
88
+ /**
89
+ * Get Express app instance
90
+ */
91
+ getApp() {
92
+ return this.app;
93
+ }
94
+ }
95
+ exports.ExpressServer = ExpressServer;
96
+ //# sourceMappingURL=expressServer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expressServer.js","sourceRoot":"","sources":["../../src/server/expressServer.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "kastrick",
3
+ "version": "1.0.0",
4
+ "description": "a Easy Tool for Kastrick Services and Devoloper Experience",
5
+ "main": "lib/index.js",
6
+ "types": "lib/index.d.ts",
7
+ "bin": {
8
+ "kastrick": "lib/cli/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "dev": "tsc --watch",
13
+ "test": "echo \"Error: no test specified\" && exit 1",
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "keywords": [
17
+ "api",
18
+ "kastrick",
19
+ "framework",
20
+ "routing",
21
+ "backend"
22
+ ],
23
+ "author": "",
24
+ "license": "ISC",
25
+ "dependencies": {
26
+ "express": "^4.18.0",
27
+ "chokidar": "^3.5.0",
28
+ "typescript": "^5.0.0"
29
+ },
30
+ "devDependencies": {
31
+ "@types/node": "^20.0.0",
32
+ "@types/express": "^4.17.0"
33
+ }
34
+ }
@@ -0,0 +1,27 @@
1
+ import { Scanner } from "../core/scanner";
2
+ import { Compiler } from "../core/compiler";
3
+
4
+ /**
5
+ * Compile command - builds the /output folder
6
+ */
7
+ export async function compile(rootDir: string = process.cwd()): Promise<void> {
8
+ console.log("🔨 Compiling kastrick project...\n");
9
+
10
+ const scanner = new Scanner(rootDir);
11
+ const compiler = new Compiler(rootDir);
12
+
13
+ // Scan API folder
14
+ const files = scanner.scanApiFolder();
15
+
16
+ if (files.length === 0) {
17
+ console.log("⚠️ No API files found. Create files in /api folder first.");
18
+ return;
19
+ }
20
+
21
+ console.log(`📁 Found ${files.length} API files`);
22
+ files.forEach((f) => console.log(` - ${f.relativePath}`));
23
+ console.log();
24
+
25
+ // Compile
26
+ compiler.compile(files);
27
+ }
package/src/cli/dev.ts ADDED
@@ -0,0 +1,49 @@
1
+ import { Scanner } from "../core/scanner";
2
+ import { ExpressServer } from "../server/expressServer";
3
+ import * as chokidar from "chokidar";
4
+ import * as path from "path";
5
+
6
+ /**
7
+ * Dev command - starts dev server with hot reload
8
+ */
9
+ export async function dev(port: number = 3000, rootDir: string = process.cwd()): Promise<void> {
10
+ console.log("🚀 Starting kastrick dev server...\n");
11
+
12
+ const scanner = new Scanner(rootDir);
13
+ const server = new ExpressServer(port);
14
+
15
+ // Initial register
16
+ const files = scanner.scanApiFolder();
17
+ if (files.length > 0) {
18
+ server.registerRoutes(files);
19
+ } else {
20
+ console.log("⚠️ No API files found. Create files in /api folder.\n");
21
+ }
22
+
23
+ server.start();
24
+
25
+ // Watch for changes
26
+ const apiPath = path.join(rootDir, "api");
27
+ const watcher = chokidar.watch(apiPath, {
28
+ ignored: /(^|[\/\\])\.|node_modules/,
29
+ persistent: true,
30
+ });
31
+
32
+ watcher.on("change", () => {
33
+ console.log("\n♻️ Files changed, reloading routes...\n");
34
+ const updatedFiles = scanner.scanApiFolder();
35
+ server.registerRoutes(updatedFiles);
36
+ });
37
+
38
+ watcher.on("add", () => {
39
+ console.log("\n♻️ Files added, reloading routes...\n");
40
+ const updatedFiles = scanner.scanApiFolder();
41
+ server.registerRoutes(updatedFiles);
42
+ });
43
+
44
+ watcher.on("unlink", () => {
45
+ console.log("\n♻️ Files removed, reloading routes...\n");
46
+ const updatedFiles = scanner.scanApiFolder();
47
+ server.registerRoutes(updatedFiles);
48
+ });
49
+ }
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { compile } from "./compile";
4
+ import { dev } from "./dev";
5
+ import { serve } from "./serve";
6
+
7
+ const command = process.argv[2];
8
+ const port = process.argv[3] ? parseInt(process.argv[3]) : undefined;
9
+
10
+ async function main() {
11
+ try {
12
+ switch (command) {
13
+ case "compile":
14
+ await compile();
15
+ break;
16
+ case "dev":
17
+ await dev(port);
18
+ break;
19
+ case "serve":
20
+ await serve(port);
21
+ break;
22
+ case "--version":
23
+ case "-v":
24
+ console.log("kastrick v1.0.0");
25
+ break;
26
+ case "--help":
27
+ case "-h":
28
+ showHelp();
29
+ break;
30
+ default:
31
+ console.log("❌ Unknown command:", command);
32
+ showHelp();
33
+ process.exit(1);
34
+ }
35
+ } catch (err: any) {
36
+ console.error("❌ Error:", err.message);
37
+ process.exit(1);
38
+ }
39
+ }
40
+
41
+ function showHelp() {
42
+ console.log(`
43
+ kastrick - API Framework and Compiler
44
+
45
+ USAGE:
46
+ npx kastrick <command> [port]
47
+
48
+ COMMANDS:
49
+ dev <port> Start dev server with hot reload (default: 3000)
50
+ compile Compile API files to /output folder
51
+ serve <port> Run compiled Express server (default: 3000)
52
+ --help, -h Show this help message
53
+ --version, -v Show version
54
+
55
+ EXAMPLES:
56
+ npx kastrick dev # Start dev server on port 3000
57
+ npx kastrick dev 5000 # Start dev server on port 5000
58
+ npx kastrick compile # Compile to /output
59
+ npx kastrick serve # Run compiled app on port 3000
60
+ npx kastrick serve 8080 # Run compiled app on port 8080
61
+ `);
62
+ }
63
+
64
+ main();
@@ -0,0 +1,81 @@
1
+ import express from "express";
2
+ import * as path from "path";
3
+ import * as fs from "fs";
4
+
5
+ /**
6
+ * Serve command - runs compiled Express app from /output folder
7
+ */
8
+ export async function serve(port: number = 3000, rootDir: string = process.cwd()): Promise<void> {
9
+ const outputDir = path.join(rootDir, "output");
10
+ const manifestPath = path.join(outputDir, "manifest.json");
11
+
12
+ // Check if compiled output exists
13
+ if (!fs.existsSync(outputDir)) {
14
+ console.log("❌ /output folder not found. Run 'npx kastrick compile' first.\n");
15
+ process.exit(1);
16
+ }
17
+
18
+ if (!fs.existsSync(manifestPath)) {
19
+ console.log("❌ manifest.json not found. Run 'npx kastrick compile' first.\n");
20
+ process.exit(1);
21
+ }
22
+
23
+ console.log("🚀 Starting compiled kastrick server...\n");
24
+
25
+ const app = express();
26
+ app.use(express.json());
27
+ app.use(express.urlencoded({ extended: true }));
28
+
29
+ // Logging middleware
30
+ app.use((req: express.Request, res: express.Response, next: express.NextFunction) => {
31
+ console.log(`📡 ${req.method} ${req.path}`);
32
+ next();
33
+ });
34
+
35
+ // Load manifest
36
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
37
+
38
+ // Register routes from manifest
39
+ let routeCount = 0;
40
+ for (const route of manifest.routes || []) {
41
+ const handlerPath = path.join(outputDir, "handlers", route.file);
42
+
43
+ if (!fs.existsSync(handlerPath)) {
44
+ console.warn(`⚠️ Handler not found: ${handlerPath}`);
45
+ continue;
46
+ }
47
+
48
+ const method = route.method.toLowerCase() as keyof express.Application;
49
+ const routePath = route.path;
50
+
51
+ app[method](routePath, async (req: express.Request, res: express.Response) => {
52
+ try {
53
+ // Dynamically require the handler
54
+ const modulePath = path.resolve(handlerPath);
55
+ delete require.cache[modulePath];
56
+ const handler = require(modulePath);
57
+
58
+ // Call the appropriate method
59
+ const handlerFn = handler[route.method];
60
+ if (!handlerFn) {
61
+ return res.status(404).json({ error: `${route.method} handler not found` });
62
+ }
63
+
64
+ const result = await handlerFn(req, { params: req.params });
65
+ res.status(result.status || 200).json(result.body !== undefined ? result.body : result);
66
+ } catch (err: any) {
67
+ console.error(`❌ Handler error in ${route.file}:`, err.message);
68
+ res.status(500).json({ error: err.message || "Internal server error" });
69
+ }
70
+ });
71
+
72
+ routeCount++;
73
+ }
74
+
75
+ console.log(`📡 Registered ${routeCount} routes from manifest\n`);
76
+
77
+ // Start server
78
+ app.listen(port, () => {
79
+ console.log(`🚀 Compiled server running on http://localhost:${port}\n`);
80
+ });
81
+ }
@@ -0,0 +1,147 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ import * as ts from "typescript";
4
+ import { ApiFile } from "./scanner";
5
+ import { ApiRoute, Router } from "./router";
6
+
7
+ export interface ManifestRoute {
8
+ method: string;
9
+ path: string;
10
+ file: string;
11
+ }
12
+
13
+ export interface Manifest {
14
+ routes: ManifestRoute[];
15
+ version: string;
16
+ timestamp: string;
17
+ }
18
+
19
+ /**
20
+ * Compiler - generates the /output folder with compiled handlers
21
+ */
22
+ export class Compiler {
23
+ private outputDir: string;
24
+ private handlersDir: string;
25
+ private router: Router;
26
+
27
+ constructor(rootDir: string = process.cwd()) {
28
+ this.outputDir = path.join(rootDir, "output");
29
+ this.handlersDir = path.join(this.outputDir, "handlers");
30
+ this.router = new Router();
31
+ }
32
+
33
+ /**
34
+ * Compile API files to output folder
35
+ */
36
+ compile(files: ApiFile[]): boolean {
37
+ try {
38
+ // Create output directory structure
39
+ this.ensureDirectories();
40
+
41
+ // Generate routes
42
+ const routes = this.router.generateRoutes(files);
43
+
44
+ // Copy handler files
45
+ this.copyHandlers(files);
46
+
47
+ // Generate manifest
48
+ this.generateManifest(routes);
49
+
50
+ // Generate app.js (Express app)
51
+ this.generateAppJs(routes);
52
+
53
+ console.log(`✅ Compilation successful! Output: ${this.outputDir}`);
54
+ console.log(`📝 Routes registered: ${routes.length}`);
55
+ return true;
56
+ } catch (error) {
57
+ console.error(`❌ Compilation failed:`, error);
58
+ return false;
59
+ }
60
+ }
61
+
62
+ private ensureDirectories(): void {
63
+ if (!fs.existsSync(this.outputDir)) {
64
+ fs.mkdirSync(this.outputDir, { recursive: true });
65
+ }
66
+ if (!fs.existsSync(this.handlersDir)) {
67
+ fs.mkdirSync(this.handlersDir, { recursive: true });
68
+ }
69
+ }
70
+
71
+ private copyHandlers(files: ApiFile[]): void {
72
+ for (const file of files) {
73
+ // Change .ts to .js in output
74
+ let targetPath = path.join(this.handlersDir, file.relativePath);
75
+ if (targetPath.endsWith(".ts")) {
76
+ targetPath = targetPath.slice(0, -3) + ".js";
77
+ }
78
+
79
+ const targetDir = path.dirname(targetPath);
80
+
81
+ if (!fs.existsSync(targetDir)) {
82
+ fs.mkdirSync(targetDir, { recursive: true });
83
+ }
84
+
85
+ // Transpile TypeScript to JavaScript
86
+ if (file.relativePath.endsWith(".ts")) {
87
+ const transpiled = ts.transpileModule(file.content || "", {
88
+ compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
89
+ });
90
+ fs.writeFileSync(targetPath, transpiled.outputText, "utf-8");
91
+ } else {
92
+ // Plain JavaScript files - copy as-is
93
+ fs.writeFileSync(targetPath, file.content || "", "utf-8");
94
+ }
95
+ }
96
+ }
97
+
98
+ private generateManifest(routes: ApiRoute[]): void {
99
+ const manifest: Manifest = {
100
+ version: "1.0.0",
101
+ timestamp: new Date().toISOString(),
102
+ routes: routes.map((route) => ({
103
+ method: route.method,
104
+ path: route.path,
105
+ file: route.file,
106
+ })),
107
+ };
108
+
109
+ const manifestPath = path.join(this.outputDir, "manifest.json");
110
+ fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
111
+ }
112
+
113
+ private generateAppJs(routes: ApiRoute[]): void {
114
+ const routeCode = routes
115
+ .map((route) => {
116
+ const methodLower = route.method.toLowerCase();
117
+ const importPath = `./${path.join("handlers", route.file).replace(/\\/g, "/")}`;
118
+ return `app.${methodLower}('${route.path}', async (req, res) => {
119
+ try {
120
+ const handler = require('${importPath}');
121
+ const fn = handler.${route.method};
122
+ if (!fn) return res.status(404).json({ error: 'Handler not found' });
123
+
124
+ const result = await fn(req, { params: req.params });
125
+ res.status(result.status || 200).json(result.body || result);
126
+ } catch (err) {
127
+ res.status(500).json({ error: err.message });
128
+ }
129
+ });`;
130
+ })
131
+ .join("\n\n");
132
+
133
+ const appCode = `const express = require('express');
134
+ const app = express();
135
+
136
+ app.use(express.json());
137
+
138
+ // Routes from manifest
139
+ ${routeCode}
140
+
141
+ module.exports = app;
142
+ `;
143
+
144
+ const appPath = path.join(this.outputDir, "app.js");
145
+ fs.writeFileSync(appPath, appCode, "utf-8");
146
+ }
147
+ }
@@ -0,0 +1,88 @@
1
+ import * as path from "path";
2
+ import { ApiFile } from "./scanner";
3
+
4
+ export interface ApiRoute {
5
+ method: string;
6
+ path: string;
7
+ file: string;
8
+ handlerName: string;
9
+ }
10
+
11
+ /**
12
+ * Router - converts file structure to API routes
13
+ * Maps:
14
+ * - api/hello.js -> /hello (GET)
15
+ * - api/users/index.js -> /users (GET)
16
+ * - api/users/[id].js -> /users/:id (GET)
17
+ */
18
+ export class Router {
19
+ /**
20
+ * Convert file path to route path
21
+ * Handles dynamic segments [id] -> :id
22
+ */
23
+ filePathToRoute(relativePath: string): string {
24
+ // Remove extension
25
+ const withoutExt = relativePath.replace(/\.(js|ts)$/, "");
26
+
27
+ // Split into parts
28
+ let parts = withoutExt.split(path.sep);
29
+
30
+ // Remove 'index' filename, keep directory
31
+ if (parts[parts.length - 1] === "index") {
32
+ parts = parts.slice(0, -1);
33
+ }
34
+
35
+ // Convert [id] to :id (dynamic segments)
36
+ parts = parts.map((part) => {
37
+ if (part.startsWith("[") && part.endsWith("]")) {
38
+ return `:${part.slice(1, -1)}`;
39
+ }
40
+ return part;
41
+ });
42
+
43
+ // Build route path
44
+ const route = "/" + parts.join("/");
45
+ return route === "/" ? "/" : route;
46
+ }
47
+
48
+ /**
49
+ * Extract HTTP methods from file content
50
+ * Looks for exports: GET, POST, PUT, DELETE, etc.
51
+ */
52
+ extractMethods(content: string): string[] {
53
+ const methods = ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"];
54
+ const foundMethods: string[] = [];
55
+
56
+ for (const method of methods) {
57
+ if (new RegExp(`export\\s+function\\s+${method}\\s*\\(|export\\s+const\\s+${method}\\s*=`).test(content)) {
58
+ foundMethods.push(method);
59
+ }
60
+ }
61
+
62
+ // Default to GET if no methods found
63
+ return foundMethods.length > 0 ? foundMethods : ["GET"];
64
+ }
65
+
66
+ /**
67
+ * Generate routes from API files
68
+ */
69
+ generateRoutes(files: ApiFile[]): ApiRoute[] {
70
+ const routes: ApiRoute[] = [];
71
+
72
+ for (const file of files) {
73
+ const routePath = this.filePathToRoute(file.relativePath);
74
+ const methods = this.extractMethods(file.content || "");
75
+
76
+ for (const method of methods) {
77
+ routes.push({
78
+ method,
79
+ path: routePath,
80
+ file: file.relativePath,
81
+ handlerName: method,
82
+ });
83
+ }
84
+ }
85
+
86
+ return routes;
87
+ }
88
+ }