@geekmidas/cli 0.7.0 → 0.9.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.
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env -S npx tsx
2
2
  import { loadConfig, parseModuleConfig } from "./config-Bq72aj8e.mjs";
3
- import { ConstructGenerator, EndpointGenerator, generateOpenApi, openapiCommand, resolveOpenApiConfig } from "./openapi-Mwy2_R4W.mjs";
3
+ import { ConstructGenerator, EndpointGenerator, OPENAPI_OUTPUT_PATH, generateOpenApi, openapiCommand, resolveOpenApiConfig } from "./openapi--vOy9mo4.mjs";
4
4
  import { generateReactQueryCommand } from "./openapi-react-query-CcciaVu5.mjs";
5
5
  import { join, relative } from "path";
6
6
  import { Command } from "commander";
@@ -19,7 +19,7 @@ import prompts from "prompts";
19
19
 
20
20
  //#region package.json
21
21
  var name = "@geekmidas/cli";
22
- var version = "0.7.0";
22
+ var version = "0.9.0";
23
23
  var description = "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs";
24
24
  var private$1 = false;
25
25
  var type = "module";
@@ -571,6 +571,16 @@ function normalizeStudioConfig(config$1) {
571
571
  schema: studioConfig.schema ?? "public"
572
572
  };
573
573
  }
574
+ /**
575
+ * Normalize hooks configuration
576
+ * @internal Exported for testing
577
+ */
578
+ function normalizeHooksConfig(config$1) {
579
+ if (!config$1?.server) return void 0;
580
+ const serverPath = config$1.server.endsWith(".ts") ? config$1.server : `${config$1.server}.ts`;
581
+ const resolvedPath = resolve(process.cwd(), serverPath);
582
+ return { serverHooksPath: resolvedPath };
583
+ }
574
584
  async function devCommand(options) {
575
585
  const defaultEnv = loadEnvFiles(".env");
576
586
  if (defaultEnv.loaded.length > 0) logger$2.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
@@ -593,31 +603,36 @@ async function devCommand(options) {
593
603
  if (telescope) logger$2.log(`🔭 Telescope enabled at ${telescope.path}`);
594
604
  const studio = normalizeStudioConfig(config$1.studio);
595
605
  if (studio) logger$2.log(`🗄️ Studio enabled at ${studio.path}`);
606
+ const hooks = normalizeHooksConfig(config$1.hooks);
607
+ if (hooks) logger$2.log(`🪝 Server hooks enabled from ${config$1.hooks?.server}`);
596
608
  const openApiConfig = resolveOpenApiConfig(config$1);
597
609
  const enableOpenApi = openApiConfig.enabled || resolved.enableOpenApi;
598
- if (enableOpenApi) logger$2.log(`📄 OpenAPI output: ${openApiConfig.output}`);
610
+ if (enableOpenApi) logger$2.log(`📄 OpenAPI output: ${OPENAPI_OUTPUT_PATH}`);
599
611
  const buildContext = {
600
612
  envParserPath,
601
613
  envParserImportPattern,
602
614
  loggerPath,
603
615
  loggerImportPattern,
604
616
  telescope,
605
- studio
617
+ studio,
618
+ hooks
606
619
  };
607
620
  await buildServer(config$1, buildContext, resolved.providers[0], enableOpenApi);
608
621
  if (enableOpenApi) await generateOpenApi(config$1);
609
622
  const runtime = config$1.runtime ?? "node";
610
- const devServer = new DevServer(resolved.providers[0], options.port || 3e3, enableOpenApi, telescope, studio, runtime);
623
+ const devServer = new DevServer(resolved.providers[0], options.port || 3e3, options.portExplicit ?? false, enableOpenApi, telescope, studio, runtime);
611
624
  await devServer.start();
612
625
  const envParserFile = config$1.envParser.split("#")[0];
613
626
  const loggerFile = config$1.logger.split("#")[0];
627
+ const hooksFile = config$1.hooks?.server?.split("#")[0];
614
628
  const watchPatterns = [
615
629
  config$1.routes,
616
630
  ...config$1.functions ? [config$1.functions] : [],
617
631
  ...config$1.crons ? [config$1.crons] : [],
618
632
  ...config$1.subscribers ? [config$1.subscribers] : [],
619
633
  envParserFile.endsWith(".ts") ? envParserFile : `${envParserFile}.ts`,
620
- loggerFile.endsWith(".ts") ? loggerFile : `${loggerFile}.ts`
634
+ loggerFile.endsWith(".ts") ? loggerFile : `${loggerFile}.ts`,
635
+ ...hooksFile ? [hooksFile.endsWith(".ts") ? hooksFile : `${hooksFile}.ts`] : []
621
636
  ].flat();
622
637
  const normalizedPatterns = watchPatterns.map((p) => p.startsWith("./") ? p.slice(2) : p);
623
638
  logger$2.log(`👀 Watching for changes in: ${normalizedPatterns.join(", ")}`);
@@ -656,11 +671,16 @@ async function devCommand(options) {
656
671
  }
657
672
  }, 300);
658
673
  });
659
- const shutdown = async () => {
674
+ let isShuttingDown = false;
675
+ const shutdown = () => {
676
+ if (isShuttingDown) return;
677
+ isShuttingDown = true;
660
678
  logger$2.log("\n🛑 Shutting down...");
661
- await watcher.close();
662
- await devServer.stop();
663
- process.exit(0);
679
+ Promise.all([watcher.close(), devServer.stop()]).catch((err) => {
680
+ logger$2.error("Error during shutdown:", err);
681
+ }).finally(() => {
682
+ process.exit(0);
683
+ });
664
684
  };
665
685
  process.on("SIGINT", shutdown);
666
686
  process.on("SIGTERM", shutdown);
@@ -692,9 +712,10 @@ var DevServer = class {
692
712
  serverProcess = null;
693
713
  isRunning = false;
694
714
  actualPort;
695
- constructor(provider, requestedPort, enableOpenApi, telescope, studio, runtime = "node") {
715
+ constructor(provider, requestedPort, portExplicit, enableOpenApi, telescope, studio, runtime = "node") {
696
716
  this.provider = provider;
697
717
  this.requestedPort = requestedPort;
718
+ this.portExplicit = portExplicit;
698
719
  this.enableOpenApi = enableOpenApi;
699
720
  this.telescope = telescope;
700
721
  this.studio = studio;
@@ -703,8 +724,14 @@ var DevServer = class {
703
724
  }
704
725
  async start() {
705
726
  if (this.isRunning) await this.stop();
706
- this.actualPort = await findAvailablePort(this.requestedPort);
707
- if (this.actualPort !== this.requestedPort) logger$2.log(`ℹ️ Port ${this.requestedPort} was in use, using port ${this.actualPort} instead`);
727
+ if (this.portExplicit) {
728
+ const available = await isPortAvailable(this.requestedPort);
729
+ if (!available) throw new Error(`Port ${this.requestedPort} is already in use. Either stop the process using that port or omit -p/--port to auto-select an available port.`);
730
+ this.actualPort = this.requestedPort;
731
+ } else {
732
+ this.actualPort = await findAvailablePort(this.requestedPort);
733
+ if (this.actualPort !== this.requestedPort) logger$2.log(`ℹ️ Port ${this.requestedPort} was in use, using port ${this.actualPort} instead`);
734
+ }
708
735
  const serverEntryPath = join$1(process.cwd(), ".gkm", this.provider, "server.ts");
709
736
  await this.createServerEntry();
710
737
  logger$2.log(`\n✨ Starting server on port ${this.actualPort}...`);
@@ -738,26 +765,25 @@ var DevServer = class {
738
765
  }
739
766
  }
740
767
  async stop() {
768
+ const port = this.actualPort;
741
769
  if (this.serverProcess && this.isRunning) {
742
770
  const pid = this.serverProcess.pid;
743
771
  if (pid) try {
744
- process.kill(-pid, "SIGTERM");
745
- } catch {}
746
- await new Promise((resolve$1) => {
747
- const timeout = setTimeout(() => {
748
- if (pid) try {
749
- process.kill(-pid, "SIGKILL");
750
- } catch {}
751
- resolve$1();
752
- }, 3e3);
753
- this.serverProcess?.on("exit", () => {
754
- clearTimeout(timeout);
755
- resolve$1();
756
- });
757
- });
772
+ process.kill(-pid, "SIGKILL");
773
+ } catch {
774
+ try {
775
+ process.kill(pid, "SIGKILL");
776
+ } catch {}
777
+ }
758
778
  this.serverProcess = null;
759
779
  this.isRunning = false;
760
780
  }
781
+ this.killProcessesOnPort(port);
782
+ }
783
+ killProcessesOnPort(port) {
784
+ try {
785
+ execSync(`lsof -ti tcp:${port} | xargs kill -9 2>/dev/null || true`, { stdio: "ignore" });
786
+ } catch {}
761
787
  }
762
788
  async restart() {
763
789
  const portToReuse = this.actualPort;
@@ -898,12 +924,15 @@ async function buildCommand(options) {
898
924
  const { path: loggerPath, importPattern: loggerImportPattern } = parseModuleConfig(config$1.logger, "logger");
899
925
  const telescope = normalizeTelescopeConfig(config$1.telescope);
900
926
  if (telescope) logger.log(`🔭 Telescope enabled at ${telescope.path}`);
927
+ const hooks = normalizeHooksConfig(config$1.hooks);
928
+ if (hooks) logger.log(`🪝 Server hooks enabled`);
901
929
  const buildContext = {
902
930
  envParserPath,
903
931
  envParserImportPattern,
904
932
  loggerPath,
905
933
  loggerImportPattern,
906
- telescope
934
+ telescope,
935
+ hooks
907
936
  };
908
937
  const endpointGenerator = new EndpointGenerator();
909
938
  const functionGenerator = new FunctionGenerator();
@@ -2282,7 +2311,7 @@ export const telescope = new Telescope({
2282
2311
  /**
2283
2312
  * OpenAPI output path (fixed, not configurable)
2284
2313
  */
2285
- const OPENAPI_OUTPUT_PATH = "./.gkm/openapi.ts";
2314
+ const OPENAPI_OUTPUT_PATH$1 = "./.gkm/openapi.ts";
2286
2315
  /**
2287
2316
  * All available templates
2288
2317
  */
@@ -2399,8 +2428,8 @@ function generatePackageJson(options, template) {
2399
2428
  private: true,
2400
2429
  type: "module",
2401
2430
  exports: { "./client": {
2402
- types: OPENAPI_OUTPUT_PATH,
2403
- import: OPENAPI_OUTPUT_PATH
2431
+ types: OPENAPI_OUTPUT_PATH$1,
2432
+ import: OPENAPI_OUTPUT_PATH$1
2404
2433
  } },
2405
2434
  scripts: scripts$1,
2406
2435
  dependencies: sortObject(dependencies$1),
@@ -2678,12 +2707,13 @@ program.command("build").description("Build handlers from endpoints, functions,
2678
2707
  process.exit(1);
2679
2708
  }
2680
2709
  });
2681
- program.command("dev").description("Start development server with automatic reload").option("--port <port>", "Port to run the development server on", "3000").option("--enable-openapi", "Enable OpenAPI documentation for development server", true).action(async (options) => {
2710
+ program.command("dev").description("Start development server with automatic reload").option("-p, --port <port>", "Port to run the development server on").option("--enable-openapi", "Enable OpenAPI documentation for development server", true).action(async (options) => {
2682
2711
  try {
2683
2712
  const globalOptions = program.opts();
2684
2713
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
2685
2714
  await devCommand({
2686
2715
  port: options.port ? Number.parseInt(options.port) : 3e3,
2716
+ portExplicit: !!options.port,
2687
2717
  enableOpenApi: options.enableOpenapi ?? true
2688
2718
  });
2689
2719
  } catch (error) {
@@ -2706,11 +2736,11 @@ program.command("api").description("Manage REST API endpoints").action(() => {
2706
2736
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
2707
2737
  process.stdout.write("REST API management - coming soon\n");
2708
2738
  });
2709
- program.command("openapi").description("Generate OpenAPI specification from endpoints (TypeScript by default)").option("--output <path>", "Output file path for the OpenAPI spec", "openapi.ts").option("--json", "Generate JSON instead of TypeScript (legacy)", false).action(async (options) => {
2739
+ program.command("openapi").description("Generate OpenAPI specification from endpoints").action(async () => {
2710
2740
  try {
2711
2741
  const globalOptions = program.opts();
2712
2742
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
2713
- await openapiCommand(options);
2743
+ await openapiCommand({});
2714
2744
  } catch (error) {
2715
2745
  console.error("OpenAPI generation failed:", error.message);
2716
2746
  process.exit(1);