@geekmidas/cli 0.28.0 → 0.30.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/{config-BhryDQEq.cjs → config-BAE9LFC1.cjs} +2 -2
- package/dist/{config-BhryDQEq.cjs.map → config-BAE9LFC1.cjs.map} +1 -1
- package/dist/{config-C9bdq0l-.mjs → config-BC5n1a2D.mjs} +2 -2
- package/dist/{config-C9bdq0l-.mjs.map → config-BC5n1a2D.mjs.map} +1 -1
- package/dist/config.cjs +2 -2
- package/dist/config.d.cts +1 -1
- package/dist/config.d.mts +1 -1
- package/dist/config.mjs +2 -2
- package/dist/{index-CWN-bgrO.d.mts → index-C7TkoYmt.d.mts} +5 -1
- package/dist/index-C7TkoYmt.d.mts.map +1 -0
- package/dist/{index-DEWYvYvg.d.cts → index-CpchsC9w.d.cts} +5 -1
- package/dist/index-CpchsC9w.d.cts.map +1 -0
- package/dist/index.cjs +139 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +139 -46
- package/dist/index.mjs.map +1 -1
- package/dist/{openapi-BCEFhkLh.mjs → openapi-CjYeF-Tg.mjs} +2 -2
- package/dist/{openapi-BCEFhkLh.mjs.map → openapi-CjYeF-Tg.mjs.map} +1 -1
- package/dist/{openapi-D82bBqG7.cjs → openapi-a-e3Y8WA.cjs} +2 -2
- package/dist/{openapi-D82bBqG7.cjs.map → openapi-a-e3Y8WA.cjs.map} +1 -1
- package/dist/openapi.cjs +3 -3
- package/dist/openapi.mjs +3 -3
- package/dist/workspace/index.cjs +1 -1
- package/dist/workspace/index.d.cts +1 -1
- package/dist/workspace/index.d.mts +1 -1
- package/dist/workspace/index.mjs +1 -1
- package/dist/{workspace-DQjmv9lk.mjs → workspace-DFJ3sWfY.mjs} +19 -3
- package/dist/{workspace-DQjmv9lk.mjs.map → workspace-DFJ3sWfY.mjs.map} +1 -1
- package/dist/{workspace-CiZBOjf9.cjs → workspace-My0A4IRO.cjs} +19 -3
- package/dist/{workspace-CiZBOjf9.cjs.map → workspace-My0A4IRO.cjs.map} +1 -1
- package/package.json +3 -3
- package/src/dev/__tests__/index.spec.ts +223 -0
- package/src/dev/index.ts +83 -4
- package/src/init/__tests__/generators.spec.ts +17 -9
- package/src/init/generators/web.ts +86 -37
- package/src/workspace/__tests__/schema.spec.ts +114 -0
- package/src/workspace/schema.ts +23 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/index-CWN-bgrO.d.mts.map +0 -1
- package/dist/index-DEWYvYvg.d.cts.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env -S npx tsx
|
|
2
|
-
import { __require, getAppBuildOrder, getDependencyEnvVars, getDeployTargetError, isDeployTargetSupported } from "./workspace-
|
|
3
|
-
import { getAppNameFromCwd, loadAppConfig, loadConfig, loadWorkspaceConfig, parseModuleConfig } from "./config-
|
|
4
|
-
import { ConstructGenerator, EndpointGenerator, OPENAPI_OUTPUT_PATH, OpenApiTsGenerator, generateOpenApi, openapiCommand, resolveOpenApiConfig } from "./openapi-
|
|
2
|
+
import { __require, getAppBuildOrder, getDependencyEnvVars, getDeployTargetError, isDeployTargetSupported } from "./workspace-DFJ3sWfY.mjs";
|
|
3
|
+
import { getAppNameFromCwd, loadAppConfig, loadConfig, loadWorkspaceConfig, parseModuleConfig } from "./config-BC5n1a2D.mjs";
|
|
4
|
+
import { ConstructGenerator, EndpointGenerator, OPENAPI_OUTPUT_PATH, OpenApiTsGenerator, generateOpenApi, openapiCommand, resolveOpenApiConfig } from "./openapi-CjYeF-Tg.mjs";
|
|
5
5
|
import { getKeyPath, maskPassword, readStageSecrets, secretsExist, setCustomSecret, toEmbeddableSecrets, writeStageSecrets } from "./storage-Dhst7BhI.mjs";
|
|
6
6
|
import { DokployApi } from "./dokploy-api-B9qR2Yn1.mjs";
|
|
7
7
|
import { generateReactQueryCommand } from "./openapi-react-query-5rSortLH.mjs";
|
|
@@ -26,7 +26,7 @@ import prompts from "prompts";
|
|
|
26
26
|
|
|
27
27
|
//#region package.json
|
|
28
28
|
var name = "@geekmidas/cli";
|
|
29
|
-
var version = "0.
|
|
29
|
+
var version = "0.30.0";
|
|
30
30
|
var description = "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs";
|
|
31
31
|
var private$1 = false;
|
|
32
32
|
var type = "module";
|
|
@@ -1016,10 +1016,14 @@ async function devCommand(options) {
|
|
|
1016
1016
|
const appName = getAppNameFromCwd();
|
|
1017
1017
|
let config$1;
|
|
1018
1018
|
let appRoot = process.cwd();
|
|
1019
|
+
let secretsRoot = process.cwd();
|
|
1020
|
+
let workspaceAppName;
|
|
1019
1021
|
if (appName) try {
|
|
1020
1022
|
const appConfig = await loadAppConfig();
|
|
1021
1023
|
config$1 = appConfig.gkmConfig;
|
|
1022
1024
|
appRoot = appConfig.appRoot;
|
|
1025
|
+
secretsRoot = appConfig.workspaceRoot;
|
|
1026
|
+
workspaceAppName = appConfig.appName;
|
|
1023
1027
|
logger$8.log(`📦 Running app: ${appConfig.appName}`);
|
|
1024
1028
|
} catch {
|
|
1025
1029
|
const loadedConfig = await loadWorkspaceConfig();
|
|
@@ -1072,7 +1076,16 @@ async function devCommand(options) {
|
|
|
1072
1076
|
await buildServer(config$1, buildContext, resolved.providers[0], enableOpenApi, appRoot);
|
|
1073
1077
|
if (enableOpenApi) await generateOpenApi(config$1);
|
|
1074
1078
|
const runtime = config$1.runtime ?? "node";
|
|
1075
|
-
|
|
1079
|
+
let secretsJsonPath;
|
|
1080
|
+
const appSecrets = await loadSecretsForApp(secretsRoot, workspaceAppName);
|
|
1081
|
+
if (Object.keys(appSecrets).length > 0) {
|
|
1082
|
+
const secretsDir = join(secretsRoot, ".gkm");
|
|
1083
|
+
await mkdir(secretsDir, { recursive: true });
|
|
1084
|
+
secretsJsonPath = join(secretsDir, "dev-secrets.json");
|
|
1085
|
+
await writeFile(secretsJsonPath, JSON.stringify(appSecrets, null, 2));
|
|
1086
|
+
logger$8.log(`🔐 Loaded ${Object.keys(appSecrets).length} secret(s)`);
|
|
1087
|
+
}
|
|
1088
|
+
const devServer = new DevServer(resolved.providers[0], options.port || 3e3, options.portExplicit ?? false, enableOpenApi, telescope, studio, runtime, appRoot, secretsJsonPath);
|
|
1076
1089
|
await devServer.start();
|
|
1077
1090
|
const envParserFile = config$1.envParser.split("#")[0] ?? config$1.envParser;
|
|
1078
1091
|
const loggerFile = config$1.logger.split("#")[0] ?? config$1.logger;
|
|
@@ -1244,6 +1257,31 @@ async function loadDevSecrets(workspace) {
|
|
|
1244
1257
|
return {};
|
|
1245
1258
|
}
|
|
1246
1259
|
/**
|
|
1260
|
+
* Load secrets from a path for dev mode.
|
|
1261
|
+
* For single app: returns secrets as-is.
|
|
1262
|
+
* For workspace app: maps {APP}_DATABASE_URL → DATABASE_URL.
|
|
1263
|
+
* @internal Exported for testing
|
|
1264
|
+
*/
|
|
1265
|
+
async function loadSecretsForApp(secretsRoot, appName) {
|
|
1266
|
+
const stages = ["dev", "development"];
|
|
1267
|
+
let secrets = {};
|
|
1268
|
+
for (const stage of stages) if (secretsExist(stage, secretsRoot)) {
|
|
1269
|
+
const stageSecrets = await readStageSecrets(stage, secretsRoot);
|
|
1270
|
+
if (stageSecrets) {
|
|
1271
|
+
logger$8.log(`🔐 Loading secrets from stage: ${stage}`);
|
|
1272
|
+
secrets = toEmbeddableSecrets(stageSecrets);
|
|
1273
|
+
break;
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
if (Object.keys(secrets).length === 0) return {};
|
|
1277
|
+
if (!appName) return secrets;
|
|
1278
|
+
const prefix = appName.toUpperCase();
|
|
1279
|
+
const mapped = { ...secrets };
|
|
1280
|
+
const appDbUrl = secrets[`${prefix}_DATABASE_URL`];
|
|
1281
|
+
if (appDbUrl) mapped.DATABASE_URL = appDbUrl;
|
|
1282
|
+
return mapped;
|
|
1283
|
+
}
|
|
1284
|
+
/**
|
|
1247
1285
|
* Start docker-compose services for the workspace.
|
|
1248
1286
|
* @internal Exported for testing
|
|
1249
1287
|
*/
|
|
@@ -1488,7 +1526,7 @@ var DevServer = class {
|
|
|
1488
1526
|
serverProcess = null;
|
|
1489
1527
|
isRunning = false;
|
|
1490
1528
|
actualPort;
|
|
1491
|
-
constructor(provider, requestedPort, portExplicit, enableOpenApi, telescope, studio, runtime = "node", appRoot = process.cwd()) {
|
|
1529
|
+
constructor(provider, requestedPort, portExplicit, enableOpenApi, telescope, studio, runtime = "node", appRoot = process.cwd(), secretsJsonPath) {
|
|
1492
1530
|
this.provider = provider;
|
|
1493
1531
|
this.requestedPort = requestedPort;
|
|
1494
1532
|
this.portExplicit = portExplicit;
|
|
@@ -1497,6 +1535,7 @@ var DevServer = class {
|
|
|
1497
1535
|
this.studio = studio;
|
|
1498
1536
|
this.runtime = runtime;
|
|
1499
1537
|
this.appRoot = appRoot;
|
|
1538
|
+
this.secretsJsonPath = secretsJsonPath;
|
|
1500
1539
|
this.actualPort = requestedPort;
|
|
1501
1540
|
}
|
|
1502
1541
|
async start() {
|
|
@@ -1575,10 +1614,20 @@ var DevServer = class {
|
|
|
1575
1614
|
await this.start();
|
|
1576
1615
|
}
|
|
1577
1616
|
async createServerEntry() {
|
|
1578
|
-
const { writeFile:
|
|
1617
|
+
const { writeFile: fsWriteFile } = await import("node:fs/promises");
|
|
1579
1618
|
const { relative: relative$1, dirname: dirname$1 } = await import("node:path");
|
|
1580
1619
|
const serverPath = join(this.appRoot, ".gkm", this.provider, "server.ts");
|
|
1581
1620
|
const relativeAppPath = relative$1(dirname$1(serverPath), join(dirname$1(serverPath), "app.js"));
|
|
1621
|
+
const credentialsInjection = this.secretsJsonPath ? `import { Credentials } from '@geekmidas/envkit/credentials';
|
|
1622
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
1623
|
+
|
|
1624
|
+
// Inject dev secrets into Credentials (must happen before app import)
|
|
1625
|
+
const secretsPath = '${this.secretsJsonPath}';
|
|
1626
|
+
if (existsSync(secretsPath)) {
|
|
1627
|
+
Object.assign(Credentials, JSON.parse(readFileSync(secretsPath, 'utf-8')));
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
` : "";
|
|
1582
1631
|
const serveCode = this.runtime === "bun" ? `Bun.serve({
|
|
1583
1632
|
port,
|
|
1584
1633
|
fetch: app.fetch,
|
|
@@ -1598,7 +1647,7 @@ var DevServer = class {
|
|
|
1598
1647
|
* Development server entry point
|
|
1599
1648
|
* This file is auto-generated by 'gkm dev'
|
|
1600
1649
|
*/
|
|
1601
|
-
import { createApp } from './${relativeAppPath.startsWith(".") ? relativeAppPath : `./${relativeAppPath}`}';
|
|
1650
|
+
${credentialsInjection}import { createApp } from './${relativeAppPath.startsWith(".") ? relativeAppPath : `./${relativeAppPath}`}';
|
|
1602
1651
|
|
|
1603
1652
|
const port = process.argv.includes('--port')
|
|
1604
1653
|
? Number.parseInt(process.argv[process.argv.indexOf('--port') + 1])
|
|
@@ -1618,7 +1667,7 @@ start({
|
|
|
1618
1667
|
process.exit(1);
|
|
1619
1668
|
});
|
|
1620
1669
|
`;
|
|
1621
|
-
await
|
|
1670
|
+
await fsWriteFile(serverPath, content);
|
|
1622
1671
|
}
|
|
1623
1672
|
};
|
|
1624
1673
|
|
|
@@ -6298,6 +6347,8 @@ function generateWebAppFiles(options) {
|
|
|
6298
6347
|
},
|
|
6299
6348
|
dependencies: {
|
|
6300
6349
|
[modelsPackage]: "workspace:*",
|
|
6350
|
+
"@geekmidas/client": GEEKMIDAS_VERSIONS["@geekmidas/client"],
|
|
6351
|
+
"@tanstack/react-query": "~5.80.0",
|
|
6301
6352
|
next: "~16.1.0",
|
|
6302
6353
|
react: "~19.2.0",
|
|
6303
6354
|
"react-dom": "~19.2.0"
|
|
@@ -6354,7 +6405,68 @@ export default nextConfig;
|
|
|
6354
6405
|
],
|
|
6355
6406
|
exclude: ["node_modules"]
|
|
6356
6407
|
};
|
|
6408
|
+
const providersTsx = `'use client';
|
|
6409
|
+
|
|
6410
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
6411
|
+
import { useState } from 'react';
|
|
6412
|
+
|
|
6413
|
+
export function Providers({ children }: { children: React.ReactNode }) {
|
|
6414
|
+
const [queryClient] = useState(
|
|
6415
|
+
() =>
|
|
6416
|
+
new QueryClient({
|
|
6417
|
+
defaultOptions: {
|
|
6418
|
+
queries: {
|
|
6419
|
+
staleTime: 60 * 1000,
|
|
6420
|
+
},
|
|
6421
|
+
},
|
|
6422
|
+
}),
|
|
6423
|
+
);
|
|
6424
|
+
|
|
6425
|
+
return (
|
|
6426
|
+
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
|
6427
|
+
);
|
|
6428
|
+
}
|
|
6429
|
+
`;
|
|
6430
|
+
const apiIndexTs = `import { TypedFetcher } from '@geekmidas/client/fetcher';
|
|
6431
|
+
import { createEndpointHooks } from '@geekmidas/client/endpoint-hooks';
|
|
6432
|
+
|
|
6433
|
+
// TODO: Run 'gkm openapi' to generate typed paths from your API
|
|
6434
|
+
// This is a placeholder that will be replaced by the generated openapi.ts
|
|
6435
|
+
interface paths {
|
|
6436
|
+
'/health': {
|
|
6437
|
+
get: {
|
|
6438
|
+
responses: {
|
|
6439
|
+
200: {
|
|
6440
|
+
content: {
|
|
6441
|
+
'application/json': { status: string; timestamp: string };
|
|
6442
|
+
};
|
|
6443
|
+
};
|
|
6444
|
+
};
|
|
6445
|
+
};
|
|
6446
|
+
};
|
|
6447
|
+
'/users': {
|
|
6448
|
+
get: {
|
|
6449
|
+
responses: {
|
|
6450
|
+
200: {
|
|
6451
|
+
content: {
|
|
6452
|
+
'application/json': { users: Array<{ id: string; name: string }> };
|
|
6453
|
+
};
|
|
6454
|
+
};
|
|
6455
|
+
};
|
|
6456
|
+
};
|
|
6457
|
+
};
|
|
6458
|
+
}
|
|
6459
|
+
|
|
6460
|
+
const baseURL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000';
|
|
6461
|
+
|
|
6462
|
+
const fetcher = new TypedFetcher<paths>({ baseURL });
|
|
6463
|
+
|
|
6464
|
+
const hooks = createEndpointHooks<paths>(fetcher.request.bind(fetcher));
|
|
6465
|
+
|
|
6466
|
+
export const api = Object.assign(fetcher.request.bind(fetcher), hooks);
|
|
6467
|
+
`;
|
|
6357
6468
|
const layoutTsx = `import type { Metadata } from 'next';
|
|
6469
|
+
import { Providers } from './providers';
|
|
6358
6470
|
|
|
6359
6471
|
export const metadata: Metadata = {
|
|
6360
6472
|
title: '${options.name}',
|
|
@@ -6368,35 +6480,18 @@ export default function RootLayout({
|
|
|
6368
6480
|
}) {
|
|
6369
6481
|
return (
|
|
6370
6482
|
<html lang="en">
|
|
6371
|
-
<body>
|
|
6483
|
+
<body>
|
|
6484
|
+
<Providers>{children}</Providers>
|
|
6485
|
+
</body>
|
|
6372
6486
|
</html>
|
|
6373
6487
|
);
|
|
6374
6488
|
}
|
|
6375
6489
|
`;
|
|
6376
|
-
const pageTsx = `import
|
|
6490
|
+
const pageTsx = `import { api } from '@/api';
|
|
6377
6491
|
|
|
6378
6492
|
export default async function Home() {
|
|
6379
|
-
//
|
|
6380
|
-
const
|
|
6381
|
-
let health = null;
|
|
6382
|
-
|
|
6383
|
-
try {
|
|
6384
|
-
const response = await fetch(\`\${apiUrl}/health\`, {
|
|
6385
|
-
cache: 'no-store',
|
|
6386
|
-
});
|
|
6387
|
-
health = await response.json();
|
|
6388
|
-
} catch (error) {
|
|
6389
|
-
console.error('Failed to fetch health:', error);
|
|
6390
|
-
}
|
|
6391
|
-
|
|
6392
|
-
// Example: Type-safe model usage
|
|
6393
|
-
const exampleUser: User = {
|
|
6394
|
-
id: '123e4567-e89b-12d3-a456-426614174000',
|
|
6395
|
-
email: 'user@example.com',
|
|
6396
|
-
name: 'Example User',
|
|
6397
|
-
createdAt: new Date(),
|
|
6398
|
-
updatedAt: new Date(),
|
|
6399
|
-
};
|
|
6493
|
+
// Type-safe API call using the generated client
|
|
6494
|
+
const health = await api('GET /health').catch(() => null);
|
|
6400
6495
|
|
|
6401
6496
|
return (
|
|
6402
6497
|
<main style={{ padding: '2rem', fontFamily: 'system-ui' }}>
|
|
@@ -6409,21 +6504,14 @@ export default async function Home() {
|
|
|
6409
6504
|
{JSON.stringify(health, null, 2)}
|
|
6410
6505
|
</pre>
|
|
6411
6506
|
) : (
|
|
6412
|
-
<p>Unable to connect to API
|
|
6507
|
+
<p>Unable to connect to API</p>
|
|
6413
6508
|
)}
|
|
6414
6509
|
</section>
|
|
6415
6510
|
|
|
6416
|
-
<section style={{ marginTop: '2rem' }}>
|
|
6417
|
-
<h2>Shared Models</h2>
|
|
6418
|
-
<p>This user object is typed from @${options.name}/models:</p>
|
|
6419
|
-
<pre style={{ background: '#f0f0f0', padding: '1rem', borderRadius: '8px' }}>
|
|
6420
|
-
{JSON.stringify(exampleUser, null, 2)}
|
|
6421
|
-
</pre>
|
|
6422
|
-
</section>
|
|
6423
|
-
|
|
6424
6511
|
<section style={{ marginTop: '2rem' }}>
|
|
6425
6512
|
<h2>Next Steps</h2>
|
|
6426
6513
|
<ul>
|
|
6514
|
+
<li>Run <code>gkm openapi</code> to generate typed API client</li>
|
|
6427
6515
|
<li>Edit <code>apps/web/src/app/page.tsx</code> to customize this page</li>
|
|
6428
6516
|
<li>Add API routes in <code>apps/api/src/endpoints/</code></li>
|
|
6429
6517
|
<li>Define shared schemas in <code>packages/models/src/</code></li>
|
|
@@ -6433,11 +6521,8 @@ export default async function Home() {
|
|
|
6433
6521
|
);
|
|
6434
6522
|
}
|
|
6435
6523
|
`;
|
|
6436
|
-
const envLocal = `# API URL
|
|
6437
|
-
|
|
6438
|
-
|
|
6439
|
-
# Other environment variables
|
|
6440
|
-
# NEXT_PUBLIC_API_URL=http://localhost:3000
|
|
6524
|
+
const envLocal = `# API URL for client-side requests
|
|
6525
|
+
NEXT_PUBLIC_API_URL=http://localhost:3000
|
|
6441
6526
|
`;
|
|
6442
6527
|
const gitignore = `.next/
|
|
6443
6528
|
node_modules/
|
|
@@ -6461,10 +6546,18 @@ node_modules/
|
|
|
6461
6546
|
path: "apps/web/src/app/layout.tsx",
|
|
6462
6547
|
content: layoutTsx
|
|
6463
6548
|
},
|
|
6549
|
+
{
|
|
6550
|
+
path: "apps/web/src/app/providers.tsx",
|
|
6551
|
+
content: providersTsx
|
|
6552
|
+
},
|
|
6464
6553
|
{
|
|
6465
6554
|
path: "apps/web/src/app/page.tsx",
|
|
6466
6555
|
content: pageTsx
|
|
6467
6556
|
},
|
|
6557
|
+
{
|
|
6558
|
+
path: "apps/web/src/api/index.ts",
|
|
6559
|
+
content: apiIndexTs
|
|
6560
|
+
},
|
|
6468
6561
|
{
|
|
6469
6562
|
path: "apps/web/.env.local",
|
|
6470
6563
|
content: envLocal
|