@geekmidas/cli 0.0.1

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.
@@ -0,0 +1,156 @@
1
+ import { loadConfig } from "./config-Bzzc89wR.mjs";
2
+ import { loadEndpoints } from "./loadEndpoints-BL8q2rTO.mjs";
3
+ import { mkdir, writeFile } from "node:fs/promises";
4
+ import { dirname, join, relative } from "path";
5
+
6
+ //#region src/build.ts
7
+ const logger = console;
8
+ async function buildCommand(options) {
9
+ logger.log(`Building with provider: ${options.provider}`);
10
+ const config = await loadConfig();
11
+ logger.log(`Loading routes from: ${config.routes}`);
12
+ logger.log(`Using envParser: ${config.envParser}`);
13
+ const [envParserPath, envParserName] = config.envParser.split("#");
14
+ const envParserImportPattern = !envParserName ? "envParser" : envParserName === "envParser" ? "{ envParser }" : `{ ${envParserName} as envParser }`;
15
+ const [loggerPath, loggerName] = config.logger.split("#");
16
+ const loggerImportPattern = !loggerName ? "logger" : loggerName === "logger" ? "{ logger }" : `{ ${loggerName} as logger }`;
17
+ const routes = [];
18
+ const outputDir = join(process.cwd(), ".gkm", options.provider);
19
+ await mkdir(outputDir, { recursive: true });
20
+ const loadedEndpoints = await loadEndpoints(config.routes);
21
+ if (loadedEndpoints.length === 0) {
22
+ logger.log("No endpoints found to process");
23
+ return;
24
+ }
25
+ const allEndpoints = loadedEndpoints.map(({ name, endpoint, file }) => {
26
+ const routeInfo = {
27
+ path: endpoint.route,
28
+ method: endpoint.method,
29
+ handler: ""
30
+ };
31
+ logger.log(`Found endpoint: ${name} - ${routeInfo.method} ${routeInfo.path}`);
32
+ return {
33
+ file: relative(process.cwd(), file),
34
+ exportName: name,
35
+ endpoint,
36
+ routeInfo
37
+ };
38
+ });
39
+ if (options.provider === "server") {
40
+ const serverFile = await generateServerFile(outputDir, allEndpoints, envParserPath, envParserImportPattern, loggerPath, loggerImportPattern);
41
+ routes.push({
42
+ path: "*",
43
+ method: "ALL",
44
+ handler: relative(process.cwd(), serverFile)
45
+ });
46
+ logger.log(`Generated server app with ${allEndpoints.length} endpoints`);
47
+ } else for (const { file, exportName, routeInfo } of allEndpoints) {
48
+ const handlerFile = await generateHandlerFile(outputDir, file, exportName, options.provider, routeInfo, envParserPath, envParserImportPattern);
49
+ routes.push({
50
+ ...routeInfo,
51
+ handler: relative(process.cwd(), handlerFile).replace(/\.ts$/, ".handler")
52
+ });
53
+ logger.log(`Generated handler for ${routeInfo.method} ${routeInfo.path}`);
54
+ }
55
+ const manifest = { routes };
56
+ const manifestPath = join(outputDir, "routes.json");
57
+ await writeFile(manifestPath, JSON.stringify(manifest, null, 2));
58
+ logger.log(`Generated ${routes.length} handlers in ${relative(process.cwd(), outputDir)}`);
59
+ logger.log(`Routes manifest: ${relative(process.cwd(), manifestPath)}`);
60
+ }
61
+ async function generateServerFile(outputDir, endpoints, envParserPath, envParserImportPattern, loggerPath, loggerImportPattern) {
62
+ const serverFileName = "app.ts";
63
+ const serverPath = join(outputDir, serverFileName);
64
+ const importsByFile = /* @__PURE__ */ new Map();
65
+ for (const { file, exportName } of endpoints) {
66
+ const relativePath = relative(dirname(serverPath), file);
67
+ const importPath = relativePath.replace(/\.ts$/, ".js");
68
+ if (!importsByFile.has(importPath)) importsByFile.set(importPath, []);
69
+ importsByFile.get(importPath).push(exportName);
70
+ }
71
+ const relativeEnvParserPath = relative(dirname(serverPath), envParserPath);
72
+ const relativeLoggerPath = relative(dirname(serverPath), loggerPath);
73
+ const imports = Array.from(importsByFile.entries()).map(([importPath, exports]) => `import { ${exports.join(", ")} } from '${importPath}';`).join("\n");
74
+ const allExportNames = endpoints.map(({ exportName }) => exportName);
75
+ const content = `import { HonoEndpoint } from '@geekmidas/api/hono';
76
+ import { HermodServiceDiscovery } from '@geekmidas/api/services';
77
+ import { Hono } from 'hono';
78
+ import ${envParserImportPattern} from '${relativeEnvParserPath}';
79
+ import ${loggerImportPattern} from '${relativeLoggerPath}';
80
+ ${imports}
81
+
82
+ export function createApp(app?: Hono): Hono {
83
+ const honoApp = app || new Hono();
84
+
85
+ const endpoints = [
86
+ ${allExportNames.join(",\n ")}
87
+ ];
88
+
89
+ const serviceDiscovery = HermodServiceDiscovery.getInstance(
90
+ logger,
91
+ envParser
92
+ );
93
+
94
+ HonoEndpoint.addRoutes(endpoints, serviceDiscovery, honoApp);
95
+
96
+ return honoApp;
97
+ }
98
+
99
+ // Default export for convenience
100
+ export default createApp;
101
+ `;
102
+ await writeFile(serverPath, content);
103
+ return serverPath;
104
+ }
105
+ async function generateHandlerFile(outputDir, sourceFile, exportName, provider, routeInfo, envParserPath, envParserImportPattern) {
106
+ const handlerFileName = `${exportName}.ts`;
107
+ const handlerPath = join(outputDir, handlerFileName);
108
+ const relativePath = relative(dirname(handlerPath), sourceFile);
109
+ const importPath = relativePath.replace(/\.ts$/, ".js");
110
+ const relativeEnvParserPath = relative(dirname(handlerPath), envParserPath);
111
+ let content;
112
+ switch (provider) {
113
+ case "aws-apigatewayv1":
114
+ content = generateAWSApiGatewayV1Handler(importPath, exportName, relativeEnvParserPath, envParserImportPattern);
115
+ break;
116
+ case "aws-apigatewayv2":
117
+ content = generateAWSApiGatewayV2Handler(importPath, exportName, relativeEnvParserPath, envParserImportPattern);
118
+ break;
119
+ case "server":
120
+ content = generateServerHandler(importPath, exportName);
121
+ break;
122
+ default: throw new Error(`Unsupported provider: ${provider}`);
123
+ }
124
+ await writeFile(handlerPath, content);
125
+ return handlerPath;
126
+ }
127
+ function generateAWSApiGatewayV1Handler(importPath, exportName, envParserPath, envParserImportPattern) {
128
+ return `import { AmazonApiGatewayV1Endpoint } from '@geekmidas/api/aws-apigateway';
129
+ import { ${exportName} } from '${importPath}';
130
+ import ${envParserImportPattern} from '${envParserPath}';
131
+
132
+ const adapter = new AmazonApiGatewayV1Endpoint(envParser, ${exportName});
133
+
134
+ export const handler = adapter.handler;
135
+ `;
136
+ }
137
+ function generateAWSApiGatewayV2Handler(importPath, exportName, envParserPath, envParserImportPattern) {
138
+ return `import { AmazonApiGatewayV2Endpoint } from '@geekmidas/api/aws-apigateway';
139
+ import { ${exportName} } from '${importPath}';
140
+ import ${envParserImportPattern} from '${envParserPath}';
141
+
142
+ const adapter = new AmazonApiGatewayV2Endpoint(envParser, ${exportName});
143
+
144
+ export const handler = adapter.handler;
145
+ `;
146
+ }
147
+ function generateServerHandler(importPath, exportName) {
148
+ return `import { ${exportName} } from '${importPath}';
149
+
150
+ // Server handler - implement based on your server framework
151
+ export const handler = ${exportName};
152
+ `;
153
+ }
154
+
155
+ //#endregion
156
+ export { buildCommand };
@@ -0,0 +1,162 @@
1
+ const require_chunk = require('./chunk-CUT6urMc.cjs');
2
+ const require_config = require('./config-B-D8cs7V.cjs');
3
+ const require_loadEndpoints = require('./loadEndpoints-CYFwuPZr.cjs');
4
+ const node_fs_promises = require_chunk.__toESM(require("node:fs/promises"));
5
+ const path = require_chunk.__toESM(require("path"));
6
+
7
+ //#region src/build.ts
8
+ const logger = console;
9
+ async function buildCommand(options) {
10
+ logger.log(`Building with provider: ${options.provider}`);
11
+ const config = await require_config.loadConfig();
12
+ logger.log(`Loading routes from: ${config.routes}`);
13
+ logger.log(`Using envParser: ${config.envParser}`);
14
+ const [envParserPath, envParserName] = config.envParser.split("#");
15
+ const envParserImportPattern = !envParserName ? "envParser" : envParserName === "envParser" ? "{ envParser }" : `{ ${envParserName} as envParser }`;
16
+ const [loggerPath, loggerName] = config.logger.split("#");
17
+ const loggerImportPattern = !loggerName ? "logger" : loggerName === "logger" ? "{ logger }" : `{ ${loggerName} as logger }`;
18
+ const routes = [];
19
+ const outputDir = (0, path.join)(process.cwd(), ".gkm", options.provider);
20
+ await (0, node_fs_promises.mkdir)(outputDir, { recursive: true });
21
+ const loadedEndpoints = await require_loadEndpoints.loadEndpoints(config.routes);
22
+ if (loadedEndpoints.length === 0) {
23
+ logger.log("No endpoints found to process");
24
+ return;
25
+ }
26
+ const allEndpoints = loadedEndpoints.map(({ name, endpoint, file }) => {
27
+ const routeInfo = {
28
+ path: endpoint.route,
29
+ method: endpoint.method,
30
+ handler: ""
31
+ };
32
+ logger.log(`Found endpoint: ${name} - ${routeInfo.method} ${routeInfo.path}`);
33
+ return {
34
+ file: (0, path.relative)(process.cwd(), file),
35
+ exportName: name,
36
+ endpoint,
37
+ routeInfo
38
+ };
39
+ });
40
+ if (options.provider === "server") {
41
+ const serverFile = await generateServerFile(outputDir, allEndpoints, envParserPath, envParserImportPattern, loggerPath, loggerImportPattern);
42
+ routes.push({
43
+ path: "*",
44
+ method: "ALL",
45
+ handler: (0, path.relative)(process.cwd(), serverFile)
46
+ });
47
+ logger.log(`Generated server app with ${allEndpoints.length} endpoints`);
48
+ } else for (const { file, exportName, routeInfo } of allEndpoints) {
49
+ const handlerFile = await generateHandlerFile(outputDir, file, exportName, options.provider, routeInfo, envParserPath, envParserImportPattern);
50
+ routes.push({
51
+ ...routeInfo,
52
+ handler: (0, path.relative)(process.cwd(), handlerFile).replace(/\.ts$/, ".handler")
53
+ });
54
+ logger.log(`Generated handler for ${routeInfo.method} ${routeInfo.path}`);
55
+ }
56
+ const manifest = { routes };
57
+ const manifestPath = (0, path.join)(outputDir, "routes.json");
58
+ await (0, node_fs_promises.writeFile)(manifestPath, JSON.stringify(manifest, null, 2));
59
+ logger.log(`Generated ${routes.length} handlers in ${(0, path.relative)(process.cwd(), outputDir)}`);
60
+ logger.log(`Routes manifest: ${(0, path.relative)(process.cwd(), manifestPath)}`);
61
+ }
62
+ async function generateServerFile(outputDir, endpoints, envParserPath, envParserImportPattern, loggerPath, loggerImportPattern) {
63
+ const serverFileName = "app.ts";
64
+ const serverPath = (0, path.join)(outputDir, serverFileName);
65
+ const importsByFile = /* @__PURE__ */ new Map();
66
+ for (const { file, exportName } of endpoints) {
67
+ const relativePath = (0, path.relative)((0, path.dirname)(serverPath), file);
68
+ const importPath = relativePath.replace(/\.ts$/, ".js");
69
+ if (!importsByFile.has(importPath)) importsByFile.set(importPath, []);
70
+ importsByFile.get(importPath).push(exportName);
71
+ }
72
+ const relativeEnvParserPath = (0, path.relative)((0, path.dirname)(serverPath), envParserPath);
73
+ const relativeLoggerPath = (0, path.relative)((0, path.dirname)(serverPath), loggerPath);
74
+ const imports = Array.from(importsByFile.entries()).map(([importPath, exports$1]) => `import { ${exports$1.join(", ")} } from '${importPath}';`).join("\n");
75
+ const allExportNames = endpoints.map(({ exportName }) => exportName);
76
+ const content = `import { HonoEndpoint } from '@geekmidas/api/hono';
77
+ import { HermodServiceDiscovery } from '@geekmidas/api/services';
78
+ import { Hono } from 'hono';
79
+ import ${envParserImportPattern} from '${relativeEnvParserPath}';
80
+ import ${loggerImportPattern} from '${relativeLoggerPath}';
81
+ ${imports}
82
+
83
+ export function createApp(app?: Hono): Hono {
84
+ const honoApp = app || new Hono();
85
+
86
+ const endpoints = [
87
+ ${allExportNames.join(",\n ")}
88
+ ];
89
+
90
+ const serviceDiscovery = HermodServiceDiscovery.getInstance(
91
+ logger,
92
+ envParser
93
+ );
94
+
95
+ HonoEndpoint.addRoutes(endpoints, serviceDiscovery, honoApp);
96
+
97
+ return honoApp;
98
+ }
99
+
100
+ // Default export for convenience
101
+ export default createApp;
102
+ `;
103
+ await (0, node_fs_promises.writeFile)(serverPath, content);
104
+ return serverPath;
105
+ }
106
+ async function generateHandlerFile(outputDir, sourceFile, exportName, provider, routeInfo, envParserPath, envParserImportPattern) {
107
+ const handlerFileName = `${exportName}.ts`;
108
+ const handlerPath = (0, path.join)(outputDir, handlerFileName);
109
+ const relativePath = (0, path.relative)((0, path.dirname)(handlerPath), sourceFile);
110
+ const importPath = relativePath.replace(/\.ts$/, ".js");
111
+ const relativeEnvParserPath = (0, path.relative)((0, path.dirname)(handlerPath), envParserPath);
112
+ let content;
113
+ switch (provider) {
114
+ case "aws-apigatewayv1":
115
+ content = generateAWSApiGatewayV1Handler(importPath, exportName, relativeEnvParserPath, envParserImportPattern);
116
+ break;
117
+ case "aws-apigatewayv2":
118
+ content = generateAWSApiGatewayV2Handler(importPath, exportName, relativeEnvParserPath, envParserImportPattern);
119
+ break;
120
+ case "server":
121
+ content = generateServerHandler(importPath, exportName);
122
+ break;
123
+ default: throw new Error(`Unsupported provider: ${provider}`);
124
+ }
125
+ await (0, node_fs_promises.writeFile)(handlerPath, content);
126
+ return handlerPath;
127
+ }
128
+ function generateAWSApiGatewayV1Handler(importPath, exportName, envParserPath, envParserImportPattern) {
129
+ return `import { AmazonApiGatewayV1Endpoint } from '@geekmidas/api/aws-apigateway';
130
+ import { ${exportName} } from '${importPath}';
131
+ import ${envParserImportPattern} from '${envParserPath}';
132
+
133
+ const adapter = new AmazonApiGatewayV1Endpoint(envParser, ${exportName});
134
+
135
+ export const handler = adapter.handler;
136
+ `;
137
+ }
138
+ function generateAWSApiGatewayV2Handler(importPath, exportName, envParserPath, envParserImportPattern) {
139
+ return `import { AmazonApiGatewayV2Endpoint } from '@geekmidas/api/aws-apigateway';
140
+ import { ${exportName} } from '${importPath}';
141
+ import ${envParserImportPattern} from '${envParserPath}';
142
+
143
+ const adapter = new AmazonApiGatewayV2Endpoint(envParser, ${exportName});
144
+
145
+ export const handler = adapter.handler;
146
+ `;
147
+ }
148
+ function generateServerHandler(importPath, exportName) {
149
+ return `import { ${exportName} } from '${importPath}';
150
+
151
+ // Server handler - implement based on your server framework
152
+ export const handler = ${exportName};
153
+ `;
154
+ }
155
+
156
+ //#endregion
157
+ Object.defineProperty(exports, 'buildCommand', {
158
+ enumerable: true,
159
+ get: function () {
160
+ return buildCommand;
161
+ }
162
+ });
package/dist/build.cjs ADDED
@@ -0,0 +1,5 @@
1
+ require('./config-B-D8cs7V.cjs');
2
+ require('./loadEndpoints-CYFwuPZr.cjs');
3
+ const require_build = require('./build-DN7u1ccT.cjs');
4
+
5
+ exports.buildCommand = require_build.buildCommand;
package/dist/build.mjs ADDED
@@ -0,0 +1,5 @@
1
+ import "./config-Bzzc89wR.mjs";
2
+ import "./loadEndpoints-BL8q2rTO.mjs";
3
+ import { buildCommand } from "./build-BgAllbWO.mjs";
4
+
5
+ export { buildCommand };
@@ -0,0 +1,30 @@
1
+ //#region rolldown:runtime
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
+ });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
+ value: mod,
20
+ enumerable: true
21
+ }) : target, mod));
22
+
23
+ //#endregion
24
+
25
+ Object.defineProperty(exports, '__toESM', {
26
+ enumerable: true,
27
+ get: function () {
28
+ return __toESM;
29
+ }
30
+ });
@@ -0,0 +1,35 @@
1
+ import { buildCommand } from "./build-BgAllbWO.mjs";
2
+ import { openapiCommand } from "./openapi-DLix_iqn.mjs";
3
+ import { Command } from "commander";
4
+
5
+ //#region src/cli.ts
6
+ const program = new Command();
7
+ program.name("gkm").description("GeekMidas backend framework CLI").version("0.0.2");
8
+ program.command("build").description("Build API handlers from endpoints").option("--provider <provider>", "Target provider for generated handlers", "aws-apigatewayv1").action(async (options) => {
9
+ try {
10
+ await buildCommand(options);
11
+ } catch (error) {
12
+ console.error("Build failed:", error.message);
13
+ process.exit(1);
14
+ }
15
+ });
16
+ program.command("cron").description("Manage cron jobs").action(() => {
17
+ process.stdout.write("Cron management - coming soon\n");
18
+ });
19
+ program.command("function").description("Manage serverless functions").action(() => {
20
+ process.stdout.write("Serverless function management - coming soon\n");
21
+ });
22
+ program.command("api").description("Manage REST API endpoints").action(() => {
23
+ process.stdout.write("REST API management - coming soon\n");
24
+ });
25
+ program.command("openapi").description("Generate OpenAPI 3.0 specification from endpoints").option("--output <path>", "Output file path for the OpenAPI spec", "openapi.json").action(async (options) => {
26
+ try {
27
+ await openapiCommand(options);
28
+ } catch (error) {
29
+ console.error("OpenAPI generation failed:", error.message);
30
+ process.exit(1);
31
+ }
32
+ });
33
+ program.parse();
34
+
35
+ //#endregion
@@ -0,0 +1,36 @@
1
+ const require_chunk = require('./chunk-CUT6urMc.cjs');
2
+ const require_build = require('./build-DN7u1ccT.cjs');
3
+ const require_openapi = require('./openapi-BKjcm03Q.cjs');
4
+ const commander = require_chunk.__toESM(require("commander"));
5
+
6
+ //#region src/cli.ts
7
+ const program = new commander.Command();
8
+ program.name("gkm").description("GeekMidas backend framework CLI").version("0.0.2");
9
+ program.command("build").description("Build API handlers from endpoints").option("--provider <provider>", "Target provider for generated handlers", "aws-apigatewayv1").action(async (options) => {
10
+ try {
11
+ await require_build.buildCommand(options);
12
+ } catch (error) {
13
+ console.error("Build failed:", error.message);
14
+ process.exit(1);
15
+ }
16
+ });
17
+ program.command("cron").description("Manage cron jobs").action(() => {
18
+ process.stdout.write("Cron management - coming soon\n");
19
+ });
20
+ program.command("function").description("Manage serverless functions").action(() => {
21
+ process.stdout.write("Serverless function management - coming soon\n");
22
+ });
23
+ program.command("api").description("Manage REST API endpoints").action(() => {
24
+ process.stdout.write("REST API management - coming soon\n");
25
+ });
26
+ program.command("openapi").description("Generate OpenAPI 3.0 specification from endpoints").option("--output <path>", "Output file path for the OpenAPI spec", "openapi.json").action(async (options) => {
27
+ try {
28
+ await require_openapi.openapiCommand(options);
29
+ } catch (error) {
30
+ console.error("OpenAPI generation failed:", error.message);
31
+ process.exit(1);
32
+ }
33
+ });
34
+ program.parse();
35
+
36
+ //#endregion
package/dist/cli.cjs ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ require('./config-B-D8cs7V.cjs');
3
+ require('./loadEndpoints-CYFwuPZr.cjs');
4
+ require('./build-DN7u1ccT.cjs');
5
+ require('./openapi-BKjcm03Q.cjs');
6
+ require('./cli-eVdV1MSk.cjs');
package/dist/cli.mjs ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ import "./config-Bzzc89wR.mjs";
3
+ import "./loadEndpoints-BL8q2rTO.mjs";
4
+ import "./build-BgAllbWO.mjs";
5
+ import "./openapi-DLix_iqn.mjs";
6
+ import "./cli-D6VgkVGJ.mjs";
@@ -0,0 +1,23 @@
1
+ const require_chunk = require('./chunk-CUT6urMc.cjs');
2
+ const path = require_chunk.__toESM(require("path"));
3
+ const fs = require_chunk.__toESM(require("fs"));
4
+
5
+ //#region src/config.ts
6
+ async function loadConfig() {
7
+ const configPath = (0, path.join)(process.cwd(), "gkm.config.ts");
8
+ if (!(0, fs.existsSync)(configPath)) throw new Error("gkm.config.ts not found. Please create a configuration file.");
9
+ try {
10
+ const config = await import(configPath);
11
+ return config.default || config;
12
+ } catch (error) {
13
+ throw new Error(`Failed to load gkm.config.ts: ${error.message}`);
14
+ }
15
+ }
16
+
17
+ //#endregion
18
+ Object.defineProperty(exports, 'loadConfig', {
19
+ enumerable: true,
20
+ get: function () {
21
+ return loadConfig;
22
+ }
23
+ });
@@ -0,0 +1,17 @@
1
+ import { join } from "path";
2
+ import { existsSync } from "fs";
3
+
4
+ //#region src/config.ts
5
+ async function loadConfig() {
6
+ const configPath = join(process.cwd(), "gkm.config.ts");
7
+ if (!existsSync(configPath)) throw new Error("gkm.config.ts not found. Please create a configuration file.");
8
+ try {
9
+ const config = await import(configPath);
10
+ return config.default || config;
11
+ } catch (error) {
12
+ throw new Error(`Failed to load gkm.config.ts: ${error.message}`);
13
+ }
14
+ }
15
+
16
+ //#endregion
17
+ export { loadConfig };
@@ -0,0 +1,3 @@
1
+ const require_config = require('./config-B-D8cs7V.cjs');
2
+
3
+ exports.loadConfig = require_config.loadConfig;
@@ -0,0 +1,3 @@
1
+ import { loadConfig } from "./config-Bzzc89wR.mjs";
2
+
3
+ export { loadConfig };
package/dist/index.cjs ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env -S npx tsx
2
+ require('./config-B-D8cs7V.cjs');
3
+ require('./loadEndpoints-CYFwuPZr.cjs');
4
+ require('./build-DN7u1ccT.cjs');
5
+ require('./openapi-BKjcm03Q.cjs');
6
+ require('./cli-eVdV1MSk.cjs');
package/dist/index.mjs ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env -S npx tsx
2
+ import "./config-Bzzc89wR.mjs";
3
+ import "./loadEndpoints-BL8q2rTO.mjs";
4
+ import "./build-BgAllbWO.mjs";
5
+ import "./openapi-DLix_iqn.mjs";
6
+ import "./cli-D6VgkVGJ.mjs";
@@ -0,0 +1,27 @@
1
+ import { Endpoint } from "@geekmidas/api/server";
2
+ import fg from "fast-glob";
3
+
4
+ //#region src/loadEndpoints.ts
5
+ async function loadEndpoints(routes) {
6
+ const logger = console;
7
+ const files = await fg.stream(routes, {
8
+ cwd: process.cwd(),
9
+ absolute: true
10
+ });
11
+ const endpoints = [];
12
+ for await (const f of files) try {
13
+ const file = f.toString();
14
+ const module = await import(file);
15
+ for (const [exportName, exportValue] of Object.entries(module)) if (Endpoint.isEndpoint(exportValue)) endpoints.push({
16
+ name: exportName,
17
+ endpoint: exportValue,
18
+ file
19
+ });
20
+ } catch (error) {
21
+ logger.warn(`Failed to load ${f}:`, error.message);
22
+ }
23
+ return endpoints;
24
+ }
25
+
26
+ //#endregion
27
+ export { loadEndpoints };
@@ -0,0 +1,33 @@
1
+ const require_chunk = require('./chunk-CUT6urMc.cjs');
2
+ const __geekmidas_api_server = require_chunk.__toESM(require("@geekmidas/api/server"));
3
+ const fast_glob = require_chunk.__toESM(require("fast-glob"));
4
+
5
+ //#region src/loadEndpoints.ts
6
+ async function loadEndpoints(routes) {
7
+ const logger = console;
8
+ const files = await fast_glob.default.stream(routes, {
9
+ cwd: process.cwd(),
10
+ absolute: true
11
+ });
12
+ const endpoints = [];
13
+ for await (const f of files) try {
14
+ const file = f.toString();
15
+ const module$1 = await import(file);
16
+ for (const [exportName, exportValue] of Object.entries(module$1)) if (__geekmidas_api_server.Endpoint.isEndpoint(exportValue)) endpoints.push({
17
+ name: exportName,
18
+ endpoint: exportValue,
19
+ file
20
+ });
21
+ } catch (error) {
22
+ logger.warn(`Failed to load ${f}:`, error.message);
23
+ }
24
+ return endpoints;
25
+ }
26
+
27
+ //#endregion
28
+ Object.defineProperty(exports, 'loadEndpoints', {
29
+ enumerable: true,
30
+ get: function () {
31
+ return loadEndpoints;
32
+ }
33
+ });
@@ -0,0 +1,3 @@
1
+ const require_loadEndpoints = require('./loadEndpoints-CYFwuPZr.cjs');
2
+
3
+ exports.loadEndpoints = require_loadEndpoints.loadEndpoints;
@@ -0,0 +1,3 @@
1
+ import { loadEndpoints } from "./loadEndpoints-BL8q2rTO.mjs";
2
+
3
+ export { loadEndpoints };
@@ -0,0 +1,40 @@
1
+ const require_chunk = require('./chunk-CUT6urMc.cjs');
2
+ const require_config = require('./config-B-D8cs7V.cjs');
3
+ const require_loadEndpoints = require('./loadEndpoints-CYFwuPZr.cjs');
4
+ const node_fs_promises = require_chunk.__toESM(require("node:fs/promises"));
5
+ const __geekmidas_api_server = require_chunk.__toESM(require("@geekmidas/api/server"));
6
+ const node_path = require_chunk.__toESM(require("node:path"));
7
+
8
+ //#region src/openapi.ts
9
+ async function openapiCommand(options = {}) {
10
+ const logger = console;
11
+ try {
12
+ const config = await require_config.loadConfig();
13
+ const loadedEndpoints = await require_loadEndpoints.loadEndpoints(config.routes);
14
+ if (loadedEndpoints.length === 0) {
15
+ logger.log("No valid endpoints found");
16
+ return;
17
+ }
18
+ const endpoints = loadedEndpoints.map(({ endpoint }) => endpoint);
19
+ const spec = await __geekmidas_api_server.Endpoint.buildOpenApiSchema(endpoints, {
20
+ title: "API Documentation",
21
+ version: "1.0.0",
22
+ description: "Auto-generated API documentation from endpoints"
23
+ });
24
+ const outputPath = options.output || (0, node_path.join)(process.cwd(), "openapi.json");
25
+ await (0, node_fs_promises.mkdir)((0, node_path.join)(outputPath, ".."), { recursive: true });
26
+ await (0, node_fs_promises.writeFile)(outputPath, JSON.stringify(spec, null, 2));
27
+ logger.log(`OpenAPI spec generated: ${outputPath}`);
28
+ logger.log(`Found ${endpoints.length} endpoints`);
29
+ } catch (error) {
30
+ throw new Error(`OpenAPI generation failed: ${error.message}`);
31
+ }
32
+ }
33
+
34
+ //#endregion
35
+ Object.defineProperty(exports, 'openapiCommand', {
36
+ enumerable: true,
37
+ get: function () {
38
+ return openapiCommand;
39
+ }
40
+ });
@@ -0,0 +1,34 @@
1
+ import { loadConfig } from "./config-Bzzc89wR.mjs";
2
+ import { loadEndpoints } from "./loadEndpoints-BL8q2rTO.mjs";
3
+ import { mkdir, writeFile } from "node:fs/promises";
4
+ import { Endpoint } from "@geekmidas/api/server";
5
+ import { join } from "node:path";
6
+
7
+ //#region src/openapi.ts
8
+ async function openapiCommand(options = {}) {
9
+ const logger = console;
10
+ try {
11
+ const config = await loadConfig();
12
+ const loadedEndpoints = await loadEndpoints(config.routes);
13
+ if (loadedEndpoints.length === 0) {
14
+ logger.log("No valid endpoints found");
15
+ return;
16
+ }
17
+ const endpoints = loadedEndpoints.map(({ endpoint }) => endpoint);
18
+ const spec = await Endpoint.buildOpenApiSchema(endpoints, {
19
+ title: "API Documentation",
20
+ version: "1.0.0",
21
+ description: "Auto-generated API documentation from endpoints"
22
+ });
23
+ const outputPath = options.output || join(process.cwd(), "openapi.json");
24
+ await mkdir(join(outputPath, ".."), { recursive: true });
25
+ await writeFile(outputPath, JSON.stringify(spec, null, 2));
26
+ logger.log(`OpenAPI spec generated: ${outputPath}`);
27
+ logger.log(`Found ${endpoints.length} endpoints`);
28
+ } catch (error) {
29
+ throw new Error(`OpenAPI generation failed: ${error.message}`);
30
+ }
31
+ }
32
+
33
+ //#endregion
34
+ export { openapiCommand };
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env -S npx tsx
2
+ require('./config-B-D8cs7V.cjs');
3
+ require('./loadEndpoints-CYFwuPZr.cjs');
4
+ const require_openapi = require('./openapi-BKjcm03Q.cjs');
5
+
6
+ exports.openapiCommand = require_openapi.openapiCommand;
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env -S npx tsx
2
+ import "./config-Bzzc89wR.mjs";
3
+ import "./loadEndpoints-BL8q2rTO.mjs";
4
+ import { openapiCommand } from "./openapi-DLix_iqn.mjs";
5
+
6
+ export { openapiCommand };
package/dist/types.cjs ADDED
File without changes
package/dist/types.mjs ADDED
File without changes
@@ -0,0 +1,33 @@
1
+ import { EnvironmentParser } from '@geekmidas/envkit';
2
+
3
+ export const envParser = new EnvironmentParser(process.env)
4
+ .create((get) => ({
5
+ // Database configuration
6
+ database: {
7
+ url: get('DATABASE_URL').string().url(),
8
+ maxConnections: get('DB_MAX_CONNECTIONS')
9
+ .string()
10
+ .transform(Number)
11
+ .default('10'),
12
+ },
13
+
14
+ // API configuration
15
+ api: {
16
+ port: get('PORT').string().transform(Number).default('3000'),
17
+ cors: {
18
+ origin: get('CORS_ORIGIN').string().default('*'),
19
+ },
20
+ },
21
+
22
+ // AWS configuration (for Lambda deployments)
23
+ aws: {
24
+ region: get('AWS_REGION').string().default('us-east-1'),
25
+ },
26
+
27
+ // Application settings
28
+ app: {
29
+ environment: get('NODE_ENV').string().default('development'),
30
+ logLevel: get('LOG_LEVEL').string().default('info'),
31
+ },
32
+ }))
33
+ .parse();
@@ -0,0 +1,16 @@
1
+ import type { GkmConfig } from '@geekmidas/cli';
2
+
3
+ const config: GkmConfig = {
4
+ // Glob pattern to find endpoint files
5
+ routes: 'src/routes/**/*.ts',
6
+
7
+ // Environment parser configuration
8
+ // Format: path#exportName (if no #exportName, treats as default import)
9
+ envParser: './src/env.ts#envParser',
10
+
11
+ // Logger configuration
12
+ // Format: path#exportName (if no #exportName, treats as default import)
13
+ logger: './src/logger.ts#logger',
14
+ };
15
+
16
+ export default config;
@@ -0,0 +1,10 @@
1
+ import { ConsoleLogger } from '@geekmidas/api/logger';
2
+
3
+ // Create a console logger instance
4
+ export const logger = new ConsoleLogger({
5
+ level: process.env.LOG_LEVEL || 'info',
6
+ pretty: process.env.NODE_ENV !== 'production',
7
+ });
8
+
9
+ // You can also export a default logger
10
+ export default logger;
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@geekmidas/cli",
3
+ "version": "0.0.1",
4
+ "private": false,
5
+ "type": "module",
6
+ "bin": {
7
+ "gkm": "./dist/index.mjs"
8
+ },
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.cjs",
13
+ "types": "./src/index.ts"
14
+ }
15
+ },
16
+ "publishConfig": {
17
+ "registry": "https://registry.npmjs.org/",
18
+ "access": "public"
19
+ },
20
+ "dependencies": {
21
+ "commander": "~14.0.0",
22
+ "lodash.get": "~4.4.2",
23
+ "lodash.set": "~4.3.2",
24
+ "zod": "~3.25.67",
25
+ "fast-glob": "~3.3.3",
26
+ "@geekmidas/api": "0.0.3"
27
+ },
28
+ "devDependencies": {
29
+ "@types/lodash.get": "~4.4.9",
30
+ "@types/lodash.set": "~4.3.9"
31
+ }
32
+ }
package/src/build.ts ADDED
@@ -0,0 +1,283 @@
1
+ import { mkdir, writeFile } from 'node:fs/promises';
2
+ import { dirname, join, relative } from 'path';
3
+ import { loadConfig } from './config.js';
4
+ import { loadEndpoints } from './loadEndpoints.js';
5
+ import type {
6
+ BuildOptions,
7
+ Provider,
8
+ RouteInfo,
9
+ RoutesManifest,
10
+ } from './types.js';
11
+
12
+ const logger = console;
13
+ export async function buildCommand(options: BuildOptions): Promise<void> {
14
+ logger.log(`Building with provider: ${options.provider}`);
15
+
16
+ const config = await loadConfig();
17
+ logger.log(`Loading routes from: ${config.routes}`);
18
+ logger.log(`Using envParser: ${config.envParser}`);
19
+
20
+ // Parse envParser configuration
21
+ const [envParserPath, envParserName] = config.envParser.split('#');
22
+ const envParserImportPattern = !envParserName
23
+ ? 'envParser'
24
+ : envParserName === 'envParser'
25
+ ? '{ envParser }'
26
+ : `{ ${envParserName} as envParser }`;
27
+
28
+ // Parse logger configuration
29
+ const [loggerPath, loggerName] = config.logger.split('#');
30
+ const loggerImportPattern = !loggerName
31
+ ? 'logger'
32
+ : loggerName === 'logger'
33
+ ? '{ logger }'
34
+ : `{ ${loggerName} as logger }`;
35
+
36
+ const routes: RouteInfo[] = [];
37
+ const outputDir = join(process.cwd(), '.gkm', options.provider);
38
+
39
+ // Ensure output directory exists
40
+ await mkdir(outputDir, { recursive: true });
41
+
42
+ // Load all endpoints using the refactored function
43
+ const loadedEndpoints = await loadEndpoints(config.routes);
44
+
45
+ if (loadedEndpoints.length === 0) {
46
+ logger.log('No endpoints found to process');
47
+ return;
48
+ }
49
+
50
+ const allEndpoints = loadedEndpoints.map(({ name, endpoint, file }) => {
51
+ const routeInfo: RouteInfo = {
52
+ path: endpoint.route,
53
+ method: endpoint.method,
54
+ handler: '', // Will be filled in later
55
+ };
56
+
57
+ logger.log(
58
+ `Found endpoint: ${name} - ${routeInfo.method} ${routeInfo.path}`,
59
+ );
60
+
61
+ return {
62
+ file: relative(process.cwd(), file),
63
+ exportName: name,
64
+ endpoint,
65
+ routeInfo,
66
+ };
67
+ });
68
+
69
+ // Generate handlers based on provider
70
+ if (options.provider === 'server') {
71
+ // Generate single server file with all endpoints
72
+ const serverFile = await generateServerFile(
73
+ outputDir,
74
+ allEndpoints,
75
+ envParserPath,
76
+ envParserImportPattern,
77
+ loggerPath,
78
+ loggerImportPattern,
79
+ );
80
+
81
+ routes.push({
82
+ path: '*',
83
+ method: 'ALL',
84
+ handler: relative(process.cwd(), serverFile),
85
+ });
86
+
87
+ logger.log(`Generated server app with ${allEndpoints.length} endpoints`);
88
+ } else {
89
+ // Generate individual handler files for AWS providers
90
+ for (const { file, exportName, routeInfo } of allEndpoints) {
91
+ const handlerFile = await generateHandlerFile(
92
+ outputDir,
93
+ file,
94
+ exportName,
95
+ options.provider,
96
+ routeInfo,
97
+ envParserPath,
98
+ envParserImportPattern,
99
+ );
100
+
101
+ routes.push({
102
+ ...routeInfo,
103
+ handler: relative(process.cwd(), handlerFile).replace(
104
+ /\.ts$/,
105
+ '.handler',
106
+ ),
107
+ });
108
+
109
+ logger.log(`Generated handler for ${routeInfo.method} ${routeInfo.path}`);
110
+ }
111
+ }
112
+
113
+ // Generate routes.json
114
+ const manifest: RoutesManifest = { routes };
115
+ const manifestPath = join(outputDir, 'routes.json');
116
+ await writeFile(manifestPath, JSON.stringify(manifest, null, 2));
117
+
118
+ logger.log(
119
+ `Generated ${routes.length} handlers in ${relative(process.cwd(), outputDir)}`,
120
+ );
121
+ logger.log(`Routes manifest: ${relative(process.cwd(), manifestPath)}`);
122
+ }
123
+
124
+ async function generateServerFile(
125
+ outputDir: string,
126
+ endpoints: Array<{
127
+ file: string;
128
+ exportName: string;
129
+ endpoint: any;
130
+ routeInfo: RouteInfo;
131
+ }>,
132
+ envParserPath: string,
133
+ envParserImportPattern: string,
134
+ loggerPath: string,
135
+ loggerImportPattern: string,
136
+ ): Promise<string> {
137
+ const serverFileName = 'app.ts';
138
+ const serverPath = join(outputDir, serverFileName);
139
+
140
+ // Group imports by file
141
+ const importsByFile = new Map<string, string[]>();
142
+
143
+ for (const { file, exportName } of endpoints) {
144
+ const relativePath = relative(dirname(serverPath), file);
145
+ const importPath = relativePath.replace(/\.ts$/, '.js');
146
+
147
+ if (!importsByFile.has(importPath)) {
148
+ importsByFile.set(importPath, []);
149
+ }
150
+ importsByFile.get(importPath)!.push(exportName);
151
+ }
152
+
153
+ const relativeEnvParserPath = relative(dirname(serverPath), envParserPath);
154
+ const relativeLoggerPath = relative(dirname(serverPath), loggerPath);
155
+
156
+ // Generate import statements
157
+ const imports = Array.from(importsByFile.entries())
158
+ .map(
159
+ ([importPath, exports]) =>
160
+ `import { ${exports.join(', ')} } from '${importPath}';`,
161
+ )
162
+ .join('\n');
163
+
164
+ const allExportNames = endpoints.map(({ exportName }) => exportName);
165
+
166
+ const content = `import { HonoEndpoint } from '@geekmidas/api/hono';
167
+ import { HermodServiceDiscovery } from '@geekmidas/api/services';
168
+ import { Hono } from 'hono';
169
+ import ${envParserImportPattern} from '${relativeEnvParserPath}';
170
+ import ${loggerImportPattern} from '${relativeLoggerPath}';
171
+ ${imports}
172
+
173
+ export function createApp(app?: Hono): Hono {
174
+ const honoApp = app || new Hono();
175
+
176
+ const endpoints = [
177
+ ${allExportNames.join(',\n ')}
178
+ ];
179
+
180
+ const serviceDiscovery = HermodServiceDiscovery.getInstance(
181
+ logger,
182
+ envParser
183
+ );
184
+
185
+ HonoEndpoint.addRoutes(endpoints, serviceDiscovery, honoApp);
186
+
187
+ return honoApp;
188
+ }
189
+
190
+ // Default export for convenience
191
+ export default createApp;
192
+ `;
193
+
194
+ await writeFile(serverPath, content);
195
+ return serverPath;
196
+ }
197
+
198
+ async function generateHandlerFile(
199
+ outputDir: string,
200
+ sourceFile: string,
201
+ exportName: string,
202
+ provider: Provider,
203
+ routeInfo: RouteInfo,
204
+ envParserPath: string,
205
+ envParserImportPattern: string,
206
+ ): Promise<string> {
207
+ const handlerFileName = `${exportName}.ts`;
208
+ const handlerPath = join(outputDir, handlerFileName);
209
+
210
+ const relativePath = relative(dirname(handlerPath), sourceFile);
211
+ const importPath = relativePath.replace(/\.ts$/, '.js');
212
+
213
+ const relativeEnvParserPath = relative(dirname(handlerPath), envParserPath);
214
+
215
+ let content: string;
216
+
217
+ switch (provider) {
218
+ case 'aws-apigatewayv1':
219
+ content = generateAWSApiGatewayV1Handler(
220
+ importPath,
221
+ exportName,
222
+ relativeEnvParserPath,
223
+ envParserImportPattern,
224
+ );
225
+ break;
226
+ case 'aws-apigatewayv2':
227
+ content = generateAWSApiGatewayV2Handler(
228
+ importPath,
229
+ exportName,
230
+ relativeEnvParserPath,
231
+ envParserImportPattern,
232
+ );
233
+ break;
234
+ case 'server':
235
+ content = generateServerHandler(importPath, exportName);
236
+ break;
237
+ default:
238
+ throw new Error(`Unsupported provider: ${provider}`);
239
+ }
240
+
241
+ await writeFile(handlerPath, content);
242
+ return handlerPath;
243
+ }
244
+
245
+ function generateAWSApiGatewayV1Handler(
246
+ importPath: string,
247
+ exportName: string,
248
+ envParserPath: string,
249
+ envParserImportPattern: string,
250
+ ): string {
251
+ return `import { AmazonApiGatewayV1Endpoint } from '@geekmidas/api/aws-apigateway';
252
+ import { ${exportName} } from '${importPath}';
253
+ import ${envParserImportPattern} from '${envParserPath}';
254
+
255
+ const adapter = new AmazonApiGatewayV1Endpoint(envParser, ${exportName});
256
+
257
+ export const handler = adapter.handler;
258
+ `;
259
+ }
260
+
261
+ function generateAWSApiGatewayV2Handler(
262
+ importPath: string,
263
+ exportName: string,
264
+ envParserPath: string,
265
+ envParserImportPattern: string,
266
+ ): string {
267
+ return `import { AmazonApiGatewayV2Endpoint } from '@geekmidas/api/aws-apigateway';
268
+ import { ${exportName} } from '${importPath}';
269
+ import ${envParserImportPattern} from '${envParserPath}';
270
+
271
+ const adapter = new AmazonApiGatewayV2Endpoint(envParser, ${exportName});
272
+
273
+ export const handler = adapter.handler;
274
+ `;
275
+ }
276
+
277
+ function generateServerHandler(importPath: string, exportName: string): string {
278
+ return `import { ${exportName} } from '${importPath}';
279
+
280
+ // Server handler - implement based on your server framework
281
+ export const handler = ${exportName};
282
+ `;
283
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import { buildCommand } from './build.js';
5
+ import { openapiCommand } from './openapi.js';
6
+ import type { Provider } from './types.js';
7
+
8
+ const program = new Command();
9
+
10
+ program
11
+ .name('gkm')
12
+ .description('GeekMidas backend framework CLI')
13
+ .version('0.0.2');
14
+
15
+ program
16
+ .command('build')
17
+ .description('Build API handlers from endpoints')
18
+ .option(
19
+ '--provider <provider>',
20
+ 'Target provider for generated handlers',
21
+ 'aws-apigatewayv1',
22
+ )
23
+ .action(async (options: { provider: Provider }) => {
24
+ try {
25
+ await buildCommand(options);
26
+ } catch (error) {
27
+ console.error('Build failed:', (error as Error).message);
28
+ process.exit(1);
29
+ }
30
+ });
31
+
32
+ program
33
+ .command('cron')
34
+ .description('Manage cron jobs')
35
+ .action(() => {
36
+ process.stdout.write('Cron management - coming soon\n');
37
+ });
38
+
39
+ program
40
+ .command('function')
41
+ .description('Manage serverless functions')
42
+ .action(() => {
43
+ process.stdout.write('Serverless function management - coming soon\n');
44
+ });
45
+
46
+ program
47
+ .command('api')
48
+ .description('Manage REST API endpoints')
49
+ .action(() => {
50
+ process.stdout.write('REST API management - coming soon\n');
51
+ });
52
+
53
+ program
54
+ .command('openapi')
55
+ .description('Generate OpenAPI 3.0 specification from endpoints')
56
+ .option(
57
+ '--output <path>',
58
+ 'Output file path for the OpenAPI spec',
59
+ 'openapi.json',
60
+ )
61
+ .action(async (options: { output?: string }) => {
62
+ try {
63
+ await openapiCommand(options);
64
+ } catch (error) {
65
+ console.error('OpenAPI generation failed:', (error as Error).message);
66
+ process.exit(1);
67
+ }
68
+ });
69
+
70
+ program.parse();
package/src/config.ts ADDED
@@ -0,0 +1,22 @@
1
+ import { existsSync } from 'fs';
2
+ import { join } from 'path';
3
+ import type { GkmConfig } from './types.js';
4
+
5
+ export async function loadConfig(): Promise<GkmConfig> {
6
+ const configPath = join(process.cwd(), 'gkm.config.ts');
7
+
8
+ if (!existsSync(configPath)) {
9
+ throw new Error(
10
+ 'gkm.config.ts not found. Please create a configuration file.',
11
+ );
12
+ }
13
+
14
+ try {
15
+ const config = await import(configPath);
16
+ return config.default || config;
17
+ } catch (error) {
18
+ throw new Error(
19
+ `Failed to load gkm.config.ts: ${(error as Error).message}`,
20
+ );
21
+ }
22
+ }
package/src/index.ts ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env -S npx tsx
2
+
3
+ import './cli.js';
4
+
5
+ // Export types for user configuration
6
+ export type { GkmConfig, Provider, BuildOptions } from './types.js';
@@ -0,0 +1,43 @@
1
+ import { Endpoint } from '@geekmidas/api/server';
2
+ import fg from 'fast-glob';
3
+
4
+ export interface LoadedEndpoint {
5
+ name: string;
6
+ endpoint: Endpoint<any, any, any, any, any, any>;
7
+ file: string;
8
+ }
9
+
10
+ export async function loadEndpoints(routes: string): Promise<LoadedEndpoint[]> {
11
+ const logger = console;
12
+
13
+ // Find all endpoint files
14
+ const files = await fg.stream(routes, {
15
+ cwd: process.cwd(),
16
+ absolute: true,
17
+ });
18
+
19
+ // Load endpoints
20
+ const endpoints: LoadedEndpoint[] = [];
21
+
22
+ for await (const f of files) {
23
+ try {
24
+ const file = f.toString();
25
+ const module = await import(file);
26
+
27
+ // Check all exports for endpoints
28
+ for (const [exportName, exportValue] of Object.entries(module)) {
29
+ if (Endpoint.isEndpoint(exportValue as any)) {
30
+ endpoints.push({
31
+ name: exportName,
32
+ endpoint: exportValue as Endpoint<any, any, any, any, any, any>,
33
+ file,
34
+ });
35
+ }
36
+ }
37
+ } catch (error) {
38
+ logger.warn(`Failed to load ${f}:`, (error as Error).message);
39
+ }
40
+ }
41
+
42
+ return endpoints;
43
+ }
package/src/openapi.ts ADDED
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env -S npx tsx
2
+
3
+ import { mkdir, writeFile } from 'node:fs/promises';
4
+ import { join } from 'node:path';
5
+ import { Endpoint } from '@geekmidas/api/server';
6
+ import { loadConfig } from './config.js';
7
+ import { loadEndpoints } from './loadEndpoints.js';
8
+
9
+ interface OpenAPIOptions {
10
+ output?: string;
11
+ }
12
+
13
+ export async function openapiCommand(
14
+ options: OpenAPIOptions = {},
15
+ ): Promise<void> {
16
+ const logger = console;
17
+
18
+ try {
19
+ // Load config using existing function
20
+ const config = await loadConfig();
21
+
22
+ // Load all endpoints using the refactored function
23
+ const loadedEndpoints = await loadEndpoints(config.routes);
24
+
25
+ if (loadedEndpoints.length === 0) {
26
+ logger.log('No valid endpoints found');
27
+ return;
28
+ }
29
+
30
+ // Extract just the endpoint instances for OpenAPI generation
31
+ const endpoints = loadedEndpoints.map(({ endpoint }) => endpoint);
32
+
33
+ // Generate OpenAPI spec using built-in method
34
+ const spec = await Endpoint.buildOpenApiSchema(endpoints, {
35
+ title: 'API Documentation',
36
+ version: '1.0.0',
37
+ description: 'Auto-generated API documentation from endpoints',
38
+ });
39
+
40
+ // Write output
41
+ const outputPath = options.output || join(process.cwd(), 'openapi.json');
42
+ await mkdir(join(outputPath, '..'), { recursive: true });
43
+ await writeFile(outputPath, JSON.stringify(spec, null, 2));
44
+
45
+ logger.log(`OpenAPI spec generated: ${outputPath}`);
46
+ logger.log(`Found ${endpoints.length} endpoints`);
47
+ } catch (error) {
48
+ throw new Error(`OpenAPI generation failed: ${(error as Error).message}`);
49
+ }
50
+ }
package/src/types.ts ADDED
@@ -0,0 +1,21 @@
1
+ export type Provider = 'server' | 'aws-apigatewayv1' | 'aws-apigatewayv2';
2
+
3
+ export interface GkmConfig {
4
+ routes: string;
5
+ envParser: string;
6
+ logger: string;
7
+ }
8
+
9
+ export interface BuildOptions {
10
+ provider: Provider;
11
+ }
12
+
13
+ export interface RouteInfo {
14
+ path: string;
15
+ method: string;
16
+ handler: string;
17
+ }
18
+
19
+ export interface RoutesManifest {
20
+ routes: RouteInfo[];
21
+ }
@@ -0,0 +1,3 @@
1
+ import { defineConfig } from 'tsdown';
2
+
3
+ export default defineConfig({});