@geekmidas/cli 0.30.0 ā 0.32.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 +168 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +166 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/dev/__tests__/entry.spec.ts +140 -0
- package/src/dev/index.ts +246 -1
- package/src/index.ts +30 -16
- package/src/init/versions.ts +1 -1
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
|
+
var version = "0.31.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,165 @@ 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
|
|
1562
|
+
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
|
+
const appName = require_config.getAppNameFromCwd() ?? void 0;
|
|
1580
|
+
const appSecrets = await loadSecretsForApp(secretsRoot, appName);
|
|
1581
|
+
if (Object.keys(appSecrets).length > 0) logger$8.log(`š Loaded ${Object.keys(appSecrets).length} secret(s)`);
|
|
1582
|
+
let secretsJsonPath;
|
|
1583
|
+
if (Object.keys(appSecrets).length > 0) {
|
|
1584
|
+
const secretsDir = (0, node_path.join)(secretsRoot, ".gkm");
|
|
1585
|
+
await (0, node_fs_promises.mkdir)(secretsDir, { recursive: true });
|
|
1586
|
+
secretsJsonPath = (0, node_path.join)(secretsDir, "dev-secrets.json");
|
|
1587
|
+
await (0, node_fs_promises.writeFile)(secretsJsonPath, JSON.stringify(appSecrets, null, 2));
|
|
1588
|
+
}
|
|
1589
|
+
const wrapperDir = (0, node_path.join)(process.cwd(), ".gkm");
|
|
1590
|
+
await (0, node_fs_promises.mkdir)(wrapperDir, { recursive: true });
|
|
1591
|
+
const wrapperPath = (0, node_path.join)(wrapperDir, "entry-wrapper.ts");
|
|
1592
|
+
await createEntryWrapper(wrapperPath, entryPath, secretsJsonPath);
|
|
1593
|
+
const runner = new EntryRunner(wrapperPath, entryPath, watch, port);
|
|
1594
|
+
await runner.start();
|
|
1595
|
+
let isShuttingDown = false;
|
|
1596
|
+
const shutdown = () => {
|
|
1597
|
+
if (isShuttingDown) return;
|
|
1598
|
+
isShuttingDown = true;
|
|
1599
|
+
logger$8.log("\nš Shutting down...");
|
|
1600
|
+
runner.stop();
|
|
1601
|
+
process.exit(0);
|
|
1602
|
+
};
|
|
1603
|
+
process.on("SIGINT", shutdown);
|
|
1604
|
+
process.on("SIGTERM", shutdown);
|
|
1605
|
+
await new Promise(() => {});
|
|
1606
|
+
}
|
|
1607
|
+
/**
|
|
1608
|
+
* Runs and watches a TypeScript entry file using tsx.
|
|
1609
|
+
*/
|
|
1610
|
+
var EntryRunner = class {
|
|
1611
|
+
childProcess = null;
|
|
1612
|
+
watcher = null;
|
|
1613
|
+
isRunning = false;
|
|
1614
|
+
constructor(wrapperPath, entryPath, watch, port) {
|
|
1615
|
+
this.wrapperPath = wrapperPath;
|
|
1616
|
+
this.entryPath = entryPath;
|
|
1617
|
+
this.watch = watch;
|
|
1618
|
+
this.port = port;
|
|
1619
|
+
}
|
|
1620
|
+
async start() {
|
|
1621
|
+
await this.runProcess();
|
|
1622
|
+
if (this.watch) {
|
|
1623
|
+
const watchDir = (0, node_path.dirname)(this.entryPath);
|
|
1624
|
+
this.watcher = chokidar.default.watch(watchDir, {
|
|
1625
|
+
ignored: /(^|[/\\])\../,
|
|
1626
|
+
persistent: true,
|
|
1627
|
+
ignoreInitial: true
|
|
1628
|
+
});
|
|
1629
|
+
let restartTimeout = null;
|
|
1630
|
+
this.watcher.on("change", (path) => {
|
|
1631
|
+
logger$8.log(`š File changed: ${path}`);
|
|
1632
|
+
if (restartTimeout) clearTimeout(restartTimeout);
|
|
1633
|
+
restartTimeout = setTimeout(async () => {
|
|
1634
|
+
logger$8.log("š Restarting...");
|
|
1635
|
+
await this.restart();
|
|
1636
|
+
}, 300);
|
|
1637
|
+
});
|
|
1638
|
+
logger$8.log(`š Watching for changes in: ${watchDir}`);
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
async runProcess() {
|
|
1642
|
+
const env = {
|
|
1643
|
+
...process.env,
|
|
1644
|
+
PORT: String(this.port)
|
|
1645
|
+
};
|
|
1646
|
+
this.childProcess = (0, node_child_process.spawn)("npx", ["tsx", this.wrapperPath], {
|
|
1647
|
+
stdio: "inherit",
|
|
1648
|
+
env,
|
|
1649
|
+
detached: true
|
|
1650
|
+
});
|
|
1651
|
+
this.isRunning = true;
|
|
1652
|
+
this.childProcess.on("error", (error) => {
|
|
1653
|
+
logger$8.error("ā Process error:", error);
|
|
1654
|
+
});
|
|
1655
|
+
this.childProcess.on("exit", (code) => {
|
|
1656
|
+
if (code !== null && code !== 0 && code !== 143) logger$8.error(`ā Process exited with code ${code}`);
|
|
1657
|
+
this.isRunning = false;
|
|
1658
|
+
});
|
|
1659
|
+
await new Promise((resolve$1) => setTimeout(resolve$1, 500));
|
|
1660
|
+
if (this.isRunning) logger$8.log(`\nš Running at http://localhost:${this.port}`);
|
|
1661
|
+
}
|
|
1662
|
+
async restart() {
|
|
1663
|
+
this.stopProcess();
|
|
1664
|
+
await new Promise((resolve$1) => setTimeout(resolve$1, 500));
|
|
1665
|
+
await this.runProcess();
|
|
1666
|
+
}
|
|
1667
|
+
stop() {
|
|
1668
|
+
this.watcher?.close();
|
|
1669
|
+
this.stopProcess();
|
|
1670
|
+
}
|
|
1671
|
+
stopProcess() {
|
|
1672
|
+
if (this.childProcess && this.isRunning) {
|
|
1673
|
+
const pid = this.childProcess.pid;
|
|
1674
|
+
if (pid) try {
|
|
1675
|
+
process.kill(-pid, "SIGTERM");
|
|
1676
|
+
} catch {
|
|
1677
|
+
try {
|
|
1678
|
+
process.kill(pid, "SIGTERM");
|
|
1679
|
+
} catch {}
|
|
1680
|
+
}
|
|
1681
|
+
this.childProcess = null;
|
|
1682
|
+
this.isRunning = false;
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
};
|
|
1526
1686
|
var DevServer = class {
|
|
1527
1687
|
serverProcess = null;
|
|
1528
1688
|
isRunning = false;
|
|
@@ -1616,9 +1776,9 @@ var DevServer = class {
|
|
|
1616
1776
|
}
|
|
1617
1777
|
async createServerEntry() {
|
|
1618
1778
|
const { writeFile: fsWriteFile } = await import("node:fs/promises");
|
|
1619
|
-
const { relative: relative$7, dirname: dirname$
|
|
1779
|
+
const { relative: relative$7, dirname: dirname$8 } = await import("node:path");
|
|
1620
1780
|
const serverPath = (0, node_path.join)(this.appRoot, ".gkm", this.provider, "server.ts");
|
|
1621
|
-
const relativeAppPath = relative$7(dirname$
|
|
1781
|
+
const relativeAppPath = relative$7(dirname$8(serverPath), (0, node_path.join)(dirname$8(serverPath), "app.js"));
|
|
1622
1782
|
const credentialsInjection = this.secretsJsonPath ? `import { Credentials } from '@geekmidas/envkit/credentials';
|
|
1623
1783
|
import { existsSync, readFileSync } from 'node:fs';
|
|
1624
1784
|
|
|
@@ -4333,7 +4493,7 @@ const GEEKMIDAS_VERSIONS = {
|
|
|
4333
4493
|
"@geekmidas/constructs": "~0.6.0",
|
|
4334
4494
|
"@geekmidas/db": "~0.3.0",
|
|
4335
4495
|
"@geekmidas/emailkit": "~0.2.0",
|
|
4336
|
-
"@geekmidas/envkit": "~0.
|
|
4496
|
+
"@geekmidas/envkit": "~0.5.0",
|
|
4337
4497
|
"@geekmidas/errors": "~0.1.0",
|
|
4338
4498
|
"@geekmidas/events": "~0.2.0",
|
|
4339
4499
|
"@geekmidas/logger": "~0.4.0",
|
|
@@ -7219,14 +7379,16 @@ program.command("build").description("Build handlers from endpoints, functions,
|
|
|
7219
7379
|
process.exit(1);
|
|
7220
7380
|
}
|
|
7221
7381
|
});
|
|
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) => {
|
|
7382
|
+
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
7383
|
try {
|
|
7224
7384
|
const globalOptions = program.opts();
|
|
7225
7385
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
7226
7386
|
await devCommand({
|
|
7227
7387
|
port: options.port ? Number.parseInt(options.port, 10) : 3e3,
|
|
7228
7388
|
portExplicit: !!options.port,
|
|
7229
|
-
enableOpenApi: options.enableOpenapi ?? true
|
|
7389
|
+
enableOpenApi: options.enableOpenapi ?? true,
|
|
7390
|
+
entry: options.entry,
|
|
7391
|
+
watch: options.watch
|
|
7230
7392
|
});
|
|
7231
7393
|
} catch (error) {
|
|
7232
7394
|
console.error(error instanceof Error ? error.message : "Command failed");
|