@geekmidas/cli 0.31.0 → 0.33.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.cjs CHANGED
@@ -27,7 +27,7 @@ const node_module = require_chunk.__toESM(require("node:module"));
27
27
 
28
28
  //#region package.json
29
29
  var name = "@geekmidas/cli";
30
- var version = "0.30.0";
30
+ var version = "0.33.0";
31
31
  var description = "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs";
32
32
  var private$1 = false;
33
33
  var type = "module";
@@ -1012,6 +1012,7 @@ function getProductionConfigFromGkm(config) {
1012
1012
  return void 0;
1013
1013
  }
1014
1014
  async function devCommand(options) {
1015
+ if (options.entry) return entryDevCommand(options);
1015
1016
  const defaultEnv = loadEnvFiles(".env");
1016
1017
  if (defaultEnv.loaded.length > 0) logger$8.log(`šŸ“¦ Loaded env: ${defaultEnv.loaded.join(", ")}`);
1017
1018
  const appName = require_config.getAppNameFromCwd();
@@ -1523,6 +1524,168 @@ async function buildServer(config, context, provider, enableOpenApi, appRoot = p
1523
1524
  subscriberGenerator.build(context, allSubscribers, outputDir, { provider })
1524
1525
  ]);
1525
1526
  }
1527
+ /**
1528
+ * Find the directory containing .gkm/secrets/.
1529
+ * Walks up from cwd until it finds one, or returns cwd.
1530
+ * @internal Exported for testing
1531
+ */
1532
+ function findSecretsRoot(startDir) {
1533
+ let dir = startDir;
1534
+ while (dir !== "/") {
1535
+ if ((0, node_fs.existsSync)((0, node_path.join)(dir, ".gkm", "secrets"))) return dir;
1536
+ const parent = (0, node_path.dirname)(dir);
1537
+ if (parent === dir) break;
1538
+ dir = parent;
1539
+ }
1540
+ return startDir;
1541
+ }
1542
+ /**
1543
+ * Create a wrapper script that injects secrets before importing the entry file.
1544
+ * @internal Exported for testing
1545
+ */
1546
+ async function createEntryWrapper(wrapperPath, entryPath, secretsJsonPath) {
1547
+ const credentialsInjection = secretsJsonPath ? `import { Credentials } from '@geekmidas/envkit/credentials';
1548
+ import { existsSync, readFileSync } from 'node:fs';
1549
+
1550
+ // Inject dev secrets into Credentials (before app import)
1551
+ const secretsPath = '${secretsJsonPath}';
1552
+ if (existsSync(secretsPath)) {
1553
+ Object.assign(Credentials, JSON.parse(readFileSync(secretsPath, 'utf-8')));
1554
+ }
1555
+
1556
+ ` : "";
1557
+ const content = `#!/usr/bin/env node
1558
+ /**
1559
+ * Entry wrapper generated by 'gkm dev --entry'
1560
+ */
1561
+ ${credentialsInjection}// Import and run the user's entry file (dynamic import ensures secrets load first)
1562
+ await import('${entryPath}');
1563
+ `;
1564
+ await (0, node_fs_promises.writeFile)(wrapperPath, content);
1565
+ }
1566
+ /**
1567
+ * Run any TypeScript file with secret injection.
1568
+ * Does not require gkm.config.ts.
1569
+ */
1570
+ async function entryDevCommand(options) {
1571
+ const { entry, port = 3e3, watch = true } = options;
1572
+ if (!entry) throw new Error("--entry requires a file path");
1573
+ const entryPath = (0, node_path.resolve)(process.cwd(), entry);
1574
+ if (!(0, node_fs.existsSync)(entryPath)) throw new Error(`Entry file not found: ${entryPath}`);
1575
+ logger$8.log(`šŸš€ Starting entry file: ${entry}`);
1576
+ const defaultEnv = loadEnvFiles(".env");
1577
+ if (defaultEnv.loaded.length > 0) logger$8.log(`šŸ“¦ Loaded env: ${defaultEnv.loaded.join(", ")}`);
1578
+ const secretsRoot = findSecretsRoot(process.cwd());
1579
+ logger$8.log(`šŸ” Secrets root: ${secretsRoot}`);
1580
+ const appName = require_config.getAppNameFromCwd() ?? void 0;
1581
+ if (appName) logger$8.log(`šŸ“¦ App name: ${appName}`);
1582
+ const appSecrets = await loadSecretsForApp(secretsRoot, appName);
1583
+ if (Object.keys(appSecrets).length > 0) logger$8.log(`šŸ” Loaded ${Object.keys(appSecrets).length} secret(s)`);
1584
+ else logger$8.log(`āš ļø No secrets found in ${secretsRoot}/.gkm/secrets/`);
1585
+ let secretsJsonPath;
1586
+ if (Object.keys(appSecrets).length > 0) {
1587
+ const secretsDir = (0, node_path.join)(secretsRoot, ".gkm");
1588
+ await (0, node_fs_promises.mkdir)(secretsDir, { recursive: true });
1589
+ secretsJsonPath = (0, node_path.join)(secretsDir, "dev-secrets.json");
1590
+ await (0, node_fs_promises.writeFile)(secretsJsonPath, JSON.stringify(appSecrets, null, 2));
1591
+ }
1592
+ const wrapperDir = (0, node_path.join)(process.cwd(), ".gkm");
1593
+ await (0, node_fs_promises.mkdir)(wrapperDir, { recursive: true });
1594
+ const wrapperPath = (0, node_path.join)(wrapperDir, "entry-wrapper.ts");
1595
+ await createEntryWrapper(wrapperPath, entryPath, secretsJsonPath);
1596
+ const runner = new EntryRunner(wrapperPath, entryPath, watch, port);
1597
+ await runner.start();
1598
+ let isShuttingDown = false;
1599
+ const shutdown = () => {
1600
+ if (isShuttingDown) return;
1601
+ isShuttingDown = true;
1602
+ logger$8.log("\nšŸ›‘ Shutting down...");
1603
+ runner.stop();
1604
+ process.exit(0);
1605
+ };
1606
+ process.on("SIGINT", shutdown);
1607
+ process.on("SIGTERM", shutdown);
1608
+ await new Promise(() => {});
1609
+ }
1610
+ /**
1611
+ * Runs and watches a TypeScript entry file using tsx.
1612
+ */
1613
+ var EntryRunner = class {
1614
+ childProcess = null;
1615
+ watcher = null;
1616
+ isRunning = false;
1617
+ constructor(wrapperPath, entryPath, watch, port) {
1618
+ this.wrapperPath = wrapperPath;
1619
+ this.entryPath = entryPath;
1620
+ this.watch = watch;
1621
+ this.port = port;
1622
+ }
1623
+ async start() {
1624
+ await this.runProcess();
1625
+ if (this.watch) {
1626
+ const watchDir = (0, node_path.dirname)(this.entryPath);
1627
+ this.watcher = chokidar.default.watch(watchDir, {
1628
+ ignored: /(^|[/\\])\../,
1629
+ persistent: true,
1630
+ ignoreInitial: true
1631
+ });
1632
+ let restartTimeout = null;
1633
+ this.watcher.on("change", (path) => {
1634
+ logger$8.log(`šŸ“ File changed: ${path}`);
1635
+ if (restartTimeout) clearTimeout(restartTimeout);
1636
+ restartTimeout = setTimeout(async () => {
1637
+ logger$8.log("šŸ”„ Restarting...");
1638
+ await this.restart();
1639
+ }, 300);
1640
+ });
1641
+ logger$8.log(`šŸ‘€ Watching for changes in: ${watchDir}`);
1642
+ }
1643
+ }
1644
+ async runProcess() {
1645
+ const env = {
1646
+ ...process.env,
1647
+ PORT: String(this.port)
1648
+ };
1649
+ this.childProcess = (0, node_child_process.spawn)("npx", ["tsx", this.wrapperPath], {
1650
+ stdio: "inherit",
1651
+ env,
1652
+ detached: true
1653
+ });
1654
+ this.isRunning = true;
1655
+ this.childProcess.on("error", (error) => {
1656
+ logger$8.error("āŒ Process error:", error);
1657
+ });
1658
+ this.childProcess.on("exit", (code) => {
1659
+ if (code !== null && code !== 0 && code !== 143) logger$8.error(`āŒ Process exited with code ${code}`);
1660
+ this.isRunning = false;
1661
+ });
1662
+ await new Promise((resolve$1) => setTimeout(resolve$1, 500));
1663
+ if (this.isRunning) logger$8.log(`\nšŸŽ‰ Running at http://localhost:${this.port}`);
1664
+ }
1665
+ async restart() {
1666
+ this.stopProcess();
1667
+ await new Promise((resolve$1) => setTimeout(resolve$1, 500));
1668
+ await this.runProcess();
1669
+ }
1670
+ stop() {
1671
+ this.watcher?.close();
1672
+ this.stopProcess();
1673
+ }
1674
+ stopProcess() {
1675
+ if (this.childProcess && this.isRunning) {
1676
+ const pid = this.childProcess.pid;
1677
+ if (pid) try {
1678
+ process.kill(-pid, "SIGTERM");
1679
+ } catch {
1680
+ try {
1681
+ process.kill(pid, "SIGTERM");
1682
+ } catch {}
1683
+ }
1684
+ this.childProcess = null;
1685
+ this.isRunning = false;
1686
+ }
1687
+ }
1688
+ };
1526
1689
  var DevServer = class {
1527
1690
  serverProcess = null;
1528
1691
  isRunning = false;
@@ -1616,9 +1779,9 @@ var DevServer = class {
1616
1779
  }
1617
1780
  async createServerEntry() {
1618
1781
  const { writeFile: fsWriteFile } = await import("node:fs/promises");
1619
- const { relative: relative$7, dirname: dirname$7 } = await import("node:path");
1782
+ const { relative: relative$7, dirname: dirname$8 } = await import("node:path");
1620
1783
  const serverPath = (0, node_path.join)(this.appRoot, ".gkm", this.provider, "server.ts");
1621
- const relativeAppPath = relative$7(dirname$7(serverPath), (0, node_path.join)(dirname$7(serverPath), "app.js"));
1784
+ const relativeAppPath = relative$7(dirname$8(serverPath), (0, node_path.join)(dirname$8(serverPath), "app.js"));
1622
1785
  const credentialsInjection = this.secretsJsonPath ? `import { Credentials } from '@geekmidas/envkit/credentials';
1623
1786
  import { existsSync, readFileSync } from 'node:fs';
1624
1787
 
@@ -4333,7 +4496,7 @@ const GEEKMIDAS_VERSIONS = {
4333
4496
  "@geekmidas/constructs": "~0.6.0",
4334
4497
  "@geekmidas/db": "~0.3.0",
4335
4498
  "@geekmidas/emailkit": "~0.2.0",
4336
- "@geekmidas/envkit": "~0.4.0",
4499
+ "@geekmidas/envkit": "~0.5.0",
4337
4500
  "@geekmidas/errors": "~0.1.0",
4338
4501
  "@geekmidas/events": "~0.2.0",
4339
4502
  "@geekmidas/logger": "~0.4.0",
@@ -4362,7 +4525,7 @@ function generateAuthAppFiles(options) {
4362
4525
  private: true,
4363
4526
  type: "module",
4364
4527
  scripts: {
4365
- dev: "tsx watch src/index.ts",
4528
+ dev: "gkm dev --entry ./src/index.ts",
4366
4529
  build: "tsc",
4367
4530
  start: "node dist/index.js",
4368
4531
  typecheck: "tsc --noEmit"
@@ -4378,6 +4541,7 @@ function generateAuthAppFiles(options) {
4378
4541
  pg: "~8.13.0"
4379
4542
  },
4380
4543
  devDependencies: {
4544
+ "@geekmidas/cli": GEEKMIDAS_VERSIONS["@geekmidas/cli"],
4381
4545
  "@types/node": "~22.0.0",
4382
4546
  "@types/pg": "~8.11.0",
4383
4547
  tsx: "~4.20.0",
@@ -7219,14 +7383,16 @@ program.command("build").description("Build handlers from endpoints, functions,
7219
7383
  process.exit(1);
7220
7384
  }
7221
7385
  });
7222
- 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) => {
7386
+ program.command("dev").description("Start development server with automatic reload").option("-p, --port <port>", "Port to run the development server on").option("--entry <file>", "Entry file to run (bypasses gkm config)").option("--watch", "Watch for file changes (default: true with --entry)").option("--no-watch", "Disable file watching").option("--enable-openapi", "Enable OpenAPI documentation for development server", true).action(async (options) => {
7223
7387
  try {
7224
7388
  const globalOptions = program.opts();
7225
7389
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
7226
7390
  await devCommand({
7227
7391
  port: options.port ? Number.parseInt(options.port, 10) : 3e3,
7228
7392
  portExplicit: !!options.port,
7229
- enableOpenApi: options.enableOpenapi ?? true
7393
+ enableOpenApi: options.enableOpenapi ?? true,
7394
+ entry: options.entry,
7395
+ watch: options.watch
7230
7396
  });
7231
7397
  } catch (error) {
7232
7398
  console.error(error instanceof Error ? error.message : "Command failed");