@geekmidas/cli 10.0.0-alpha.10 → 10.0.0-alpha.12
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/README.md +13 -40
- package/dist/{CachedStateProvider-CqPdW7CB.cjs → CachedStateProvider-BEBl2wQT.cjs} +1 -1
- package/dist/{CachedStateProvider-CqPdW7CB.cjs.map → CachedStateProvider-BEBl2wQT.cjs.map} +1 -1
- package/dist/CachedStateProvider-C-8wtlto.mjs +3 -0
- package/dist/{CachedStateProvider-DTniehMP.mjs → CachedStateProvider-Co4n5Wvs.mjs} +1 -1
- package/dist/{CachedStateProvider-DTniehMP.mjs.map → CachedStateProvider-Co4n5Wvs.mjs.map} +1 -1
- package/dist/{CachedStateProvider-BLo7zv7S.cjs → CachedStateProvider-DqhGpzsm.cjs} +1 -1
- package/dist/{HostingerProvider-h3EbEp-Z.cjs → HostingerProvider-BOvsgATO.cjs} +1 -1
- package/dist/{HostingerProvider-h3EbEp-Z.cjs.map → HostingerProvider-BOvsgATO.cjs.map} +1 -1
- package/dist/{HostingerProvider-Cft7lEYr.mjs → HostingerProvider-BcyFRbqV.mjs} +1 -1
- package/dist/{HostingerProvider-Cft7lEYr.mjs.map → HostingerProvider-BcyFRbqV.mjs.map} +1 -1
- package/dist/{LocalStateProvider-Bn6tS-oL.mjs → LocalStateProvider-Bq-0C0sg.mjs} +1 -1
- package/dist/{LocalStateProvider-Bn6tS-oL.mjs.map → LocalStateProvider-Bq-0C0sg.mjs.map} +1 -1
- package/dist/{LocalStateProvider-Db9SEjyH.cjs → LocalStateProvider-yjXZXZDM.cjs} +1 -1
- package/dist/{LocalStateProvider-Db9SEjyH.cjs.map → LocalStateProvider-yjXZXZDM.cjs.map} +1 -1
- package/dist/{Route53Provider-RdpUDJQk.mjs → Route53Provider-C0xmer8U.mjs} +1 -1
- package/dist/{Route53Provider-RdpUDJQk.mjs.map → Route53Provider-C0xmer8U.mjs.map} +1 -1
- package/dist/{Route53Provider-di2fMM5N.cjs → Route53Provider-CH9XH7bz.cjs} +1 -1
- package/dist/{Route53Provider-di2fMM5N.cjs.map → Route53Provider-CH9XH7bz.cjs.map} +1 -1
- package/dist/{SSMStateProvider-DyX8s9HI.cjs → SSMStateProvider-CXtNgPfy.cjs} +1 -1
- package/dist/{SSMStateProvider-DyX8s9HI.cjs.map → SSMStateProvider-CXtNgPfy.cjs.map} +1 -1
- package/dist/{SSMStateProvider-BRt2py-A.mjs → SSMStateProvider-DOZifZfE.mjs} +1 -1
- package/dist/{SSMStateProvider-BRt2py-A.mjs.map → SSMStateProvider-DOZifZfE.mjs.map} +1 -1
- package/dist/{backup-provisioner-Bgj5Vi_L.mjs → backup-provisioner-DXXg6FIL.mjs} +1 -1
- package/dist/{backup-provisioner-Bgj5Vi_L.mjs.map → backup-provisioner-DXXg6FIL.mjs.map} +1 -1
- package/dist/{backup-provisioner-BrsHwYG0.cjs → backup-provisioner-cE2PuCTb.cjs} +1 -1
- package/dist/{backup-provisioner-BrsHwYG0.cjs.map → backup-provisioner-cE2PuCTb.cjs.map} +1 -1
- package/dist/{bundler-BY1OmV1e.cjs → bundler-BMekgx7a.cjs} +4 -19
- package/dist/bundler-BMekgx7a.cjs.map +1 -0
- package/dist/{bundler-Be8Zg4Wd.mjs → bundler-Bux2iF-v.mjs} +4 -19
- package/dist/bundler-Bux2iF-v.mjs.map +1 -0
- package/dist/{config-DVVph9yz.mjs → config-DTsVjKZD.mjs} +3 -3
- package/dist/{config-DVVph9yz.mjs.map → config-DTsVjKZD.mjs.map} +1 -1
- package/dist/{config-BZ1F0WuA.cjs → config-RYR9j6EF.cjs} +3 -3
- package/dist/{config-BZ1F0WuA.cjs.map → config-RYR9j6EF.cjs.map} +1 -1
- package/dist/config.cjs +3 -3
- package/dist/config.d.cts +2 -2
- package/dist/config.d.mts +2 -2
- package/dist/config.mjs +3 -3
- package/dist/{credentials-Xamqe_NJ.cjs → credentials-DQRvPz2S.cjs} +1166 -1078
- package/dist/credentials-DQRvPz2S.cjs.map +1 -0
- package/dist/{credentials-dP8vRVRv.mjs → credentials-jMJC63qs.mjs} +1145 -1069
- package/dist/credentials-jMJC63qs.mjs.map +1 -0
- package/dist/{discover-CAlFgYec.mjs → discover-BYHQwQqu.mjs} +15 -2
- package/dist/discover-BYHQwQqu.mjs.map +1 -0
- package/dist/{discover-DTmFeloL.cjs → discover-DmW9Bscc.cjs} +20 -1
- package/dist/discover-DmW9Bscc.cjs.map +1 -0
- package/dist/dokploy-api-5cK2InBm.cjs +3 -0
- package/dist/dokploy-api-D2Ny4Jrc.mjs +3 -0
- package/dist/{dokploy-api-aTFPz7_4.mjs → dokploy-api-pdaBryoY.mjs} +1 -1
- package/dist/{dokploy-api-aTFPz7_4.mjs.map → dokploy-api-pdaBryoY.mjs.map} +1 -1
- package/dist/{dokploy-api-BsVH0y_s.cjs → dokploy-api-zqKe9lHH.cjs} +1 -1
- package/dist/{dokploy-api-BsVH0y_s.cjs.map → dokploy-api-zqKe9lHH.cjs.map} +1 -1
- package/dist/{emit-CiZS_5Ub.mjs → emit-CIlvnDvV.mjs} +1 -1
- package/dist/{emit-CiZS_5Ub.mjs.map → emit-CIlvnDvV.mjs.map} +1 -1
- package/dist/{emit-CbcRolsa.cjs → emit-CUD1BWls.cjs} +1 -1
- package/dist/{emit-CbcRolsa.cjs.map → emit-CUD1BWls.cjs.map} +1 -1
- package/dist/{encryption-C6jaU0hE.cjs → encryption-D2pL3tHH.cjs} +1 -1
- package/dist/{encryption-C6jaU0hE.cjs.map → encryption-D2pL3tHH.cjs.map} +1 -1
- package/dist/encryption-YDsXXi6w.mjs +3 -0
- package/dist/{encryption-D-abyhZx.mjs → encryption-_4zwjOAR.mjs} +1 -1
- package/dist/{encryption-D-abyhZx.mjs.map → encryption-_4zwjOAR.mjs.map} +1 -1
- package/dist/{encryption-CHzzeR8a.cjs → encryption-sQ2Oxx0d.cjs} +1 -1
- package/dist/{fullstack-secrets-D1o9-G5l.mjs → fullstack-secrets-B6KBHf-g.mjs} +2 -2
- package/dist/{fullstack-secrets-D1o9-G5l.mjs.map → fullstack-secrets-B6KBHf-g.mjs.map} +1 -1
- package/dist/{fullstack-secrets-C-GGohke.cjs → fullstack-secrets-DL8v0y4D.cjs} +1 -13
- package/dist/{fullstack-secrets-C-GGohke.cjs.map → fullstack-secrets-DL8v0y4D.cjs.map} +1 -1
- package/dist/github-BThvbf2W.cjs +197 -0
- package/dist/github-BThvbf2W.cjs.map +1 -0
- package/dist/github-DpMZurEK.mjs +196 -0
- package/dist/github-DpMZurEK.mjs.map +1 -0
- package/dist/{index-CR5urhWX.d.cts → index-BzJ7QJ9c.d.mts} +2 -6
- package/dist/index-BzJ7QJ9c.d.mts.map +1 -0
- package/dist/{index-C6e-ULLx.d.mts → index-CfMjtdVC.d.cts} +2 -6
- package/dist/index-CfMjtdVC.d.cts.map +1 -0
- package/dist/index.cjs +474 -1381
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +474 -1381
- package/dist/index.mjs.map +1 -1
- package/dist/keystore-C9rint2E.cjs +101 -0
- package/dist/keystore-C9rint2E.cjs.map +1 -0
- package/dist/keystore-TxeYM7an.mjs +83 -0
- package/dist/keystore-TxeYM7an.mjs.map +1 -0
- package/dist/{openapi-Xx0A7Wg0.mjs → openapi-Bs3-5q_r.mjs} +3 -3
- package/dist/{openapi-Xx0A7Wg0.mjs.map → openapi-Bs3-5q_r.mjs.map} +1 -1
- package/dist/{openapi-SjsgARie.cjs → openapi-DWLTsYLL.cjs} +3 -3
- package/dist/{openapi-SjsgARie.cjs.map → openapi-DWLTsYLL.cjs.map} +1 -1
- package/dist/openapi.cjs +4 -4
- package/dist/openapi.d.cts +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/openapi.mjs +4 -4
- package/dist/reconcile/public.cjs +2 -2
- package/dist/reconcile/public.d.cts +1 -1
- package/dist/reconcile/public.d.mts +1 -1
- package/dist/reconcile/public.mjs +2 -2
- package/dist/{reconcile-CFs8C_1a.mjs → reconcile-BI0tWkwz.mjs} +2 -2
- package/dist/{reconcile-CFs8C_1a.mjs.map → reconcile-BI0tWkwz.mjs.map} +1 -1
- package/dist/{reconcile-D0pimNiy.cjs → reconcile-Bhw5fmKu.cjs} +2 -2
- package/dist/{reconcile-D0pimNiy.cjs.map → reconcile-Bhw5fmKu.cjs.map} +1 -1
- package/dist/setup-BmLoNrDX.mjs +12 -0
- package/dist/setup-BrR1_xGy.cjs +12 -0
- package/dist/{setup-hC6hI86f.cjs → setup-BsNR2LBG.cjs} +16 -22
- package/dist/setup-BsNR2LBG.cjs.map +1 -0
- package/dist/{setup-C8KOx8_8.mjs → setup-D5-JMknk.mjs} +16 -22
- package/dist/setup-D5-JMknk.mjs.map +1 -0
- package/dist/{storage-Bi24WfoW.mjs → storage-B020InPW.mjs} +4 -79
- package/dist/storage-B020InPW.mjs.map +1 -0
- package/dist/{storage-BJmzYxt8.cjs → storage-CSNplaJn.cjs} +4 -85
- package/dist/storage-CSNplaJn.cjs.map +1 -0
- package/dist/{storage-CYlGwWTj.mjs → storage-DGBzMLZa.mjs} +2 -1
- package/dist/{storage-BM9hGpgv.cjs → storage-DlVol_oj.cjs} +2 -1
- package/dist/{sync-SizaChmC.cjs → sync-0ILCb-1S.cjs} +2 -2
- package/dist/{sync-SizaChmC.cjs.map → sync-0ILCb-1S.cjs.map} +1 -1
- package/dist/{sync-CmEuNZsB.mjs → sync-CzHXiR9c.mjs} +2 -2
- package/dist/{sync-CmEuNZsB.mjs.map → sync-CzHXiR9c.mjs.map} +1 -1
- package/dist/sync-DYENfOt4.cjs +4 -0
- package/dist/{sync-hOqAWHR6.mjs → sync-HAau6h_R.mjs} +1 -1
- package/dist/{types-6pkp2RRj.d.mts → types-DVIx8I1N.d.mts} +4 -274
- package/dist/types-DVIx8I1N.d.mts.map +1 -0
- package/dist/{types-Y3fKQSAK.d.cts → types-PMP_qXRY.d.cts} +4 -274
- package/dist/types-PMP_qXRY.d.cts.map +1 -0
- package/dist/workspace/index.cjs +1 -1
- package/dist/workspace/index.d.cts +3 -3
- package/dist/workspace/index.d.mts +3 -3
- package/dist/workspace/index.mjs +1 -1
- package/dist/workspace-Cji6lE8E.cjs +10 -0
- package/dist/workspace-CkJAtRIX.mjs +9 -0
- package/dist/{workspace-zoqRbihZ.mjs → workspace-CtnZnWsa.mjs} +40 -72
- package/dist/workspace-CtnZnWsa.mjs.map +1 -0
- package/dist/{workspace-C6VJQVtG.cjs → workspace-DPV7FtAv.cjs} +45 -71
- package/dist/workspace-DPV7FtAv.cjs.map +1 -0
- package/package.json +11 -11
- package/dist/CachedStateProvider-BxoAm-B2.mjs +0 -3
- package/dist/bundler-BY1OmV1e.cjs.map +0 -1
- package/dist/bundler-Be8Zg4Wd.mjs.map +0 -1
- package/dist/credentials-Xamqe_NJ.cjs.map +0 -1
- package/dist/credentials-dP8vRVRv.mjs.map +0 -1
- package/dist/discover-CAlFgYec.mjs.map +0 -1
- package/dist/discover-DTmFeloL.cjs.map +0 -1
- package/dist/dokploy-api-BS2xnJLZ.mjs +0 -3
- package/dist/dokploy-api-BuxDRo3l.cjs +0 -3
- package/dist/encryption-CQUncu7v.mjs +0 -3
- package/dist/index-C6e-ULLx.d.mts.map +0 -1
- package/dist/index-CR5urhWX.d.cts.map +0 -1
- package/dist/setup-BA8ciTPU.mjs +0 -11
- package/dist/setup-BsZvZP6f.cjs +0 -11
- package/dist/setup-C8KOx8_8.mjs.map +0 -1
- package/dist/setup-hC6hI86f.cjs.map +0 -1
- package/dist/storage-BJmzYxt8.cjs.map +0 -1
- package/dist/storage-Bi24WfoW.mjs.map +0 -1
- package/dist/sync-D306yOWu.cjs +0 -4
- package/dist/types-6pkp2RRj.d.mts.map +0 -1
- package/dist/types-Y3fKQSAK.d.cts.map +0 -1
- package/dist/workspace-BzrG_2SI.mjs +0 -8
- package/dist/workspace-C6VJQVtG.cjs.map +0 -1
- package/dist/workspace-XXtNhqJl.cjs +0 -9
- package/dist/workspace-zoqRbihZ.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
#!/usr/bin/env -S npx tsx
|
|
2
2
|
import { __require } from "./chunk-Duj1WY3L.mjs";
|
|
3
|
-
import { ConstructGenerator, DEFAULT_CACHE, discover, isPartitionedRoutes } from "./discover-
|
|
4
|
-
import { derivedApps, getAppNameFromCwd, loadAppConfig, loadConfig, loadWorkspaceConfig, parseModuleConfig } from "./config-
|
|
5
|
-
import { allConstructGlobs, assertDeployedStage, deployedProblems, getAppBuildOrder, getAppGkmConfig, getDependencyEnvVars, getDeployTargetError, getPublicEnvPrefix, isDeployTargetSupported, stageProblems, stripPublicPrefix } from "./workspace-
|
|
3
|
+
import { ConstructGenerator, DEFAULT_CACHE, DEFAULT_EMAIL, discover, isPartitionedRoutes } from "./discover-BYHQwQqu.mjs";
|
|
4
|
+
import { derivedApps, getAppNameFromCwd, loadAppConfig, loadConfig, loadWorkspaceConfig, parseModuleConfig } from "./config-DTsVjKZD.mjs";
|
|
5
|
+
import { InvalidStages, allConstructGlobs, assertDeployedStage, deployedProblems, getAppBuildOrder, getAppGkmConfig, getDependencyEnvVars, getDeployTargetError, getPublicEnvPrefix, isDeployTargetSupported, stageProblems, stripPublicPrefix } from "./workspace-CtnZnWsa.mjs";
|
|
6
6
|
import { getCredentialsPath, getDokployCredentials, getDokployRegistryId, getDokployToken, removeDokployCredentials, storeDokployCredentials, storeDokployRegistryId, storeHostingerToken } from "./credentials-B5zRHdAl.mjs";
|
|
7
|
-
import { applyPostgres,
|
|
7
|
+
import { COMPOSE_PATH, applyPostgres, cacheBackendFor, constructGlobs, createCredentialsPreload, createEntryWrapper, eventsBackendFor, findAvailablePort, isPortAvailable, loadEnvFiles, loadSecretsForApp, prepareEntryCredentials, providerOf, reconcileWorkspace, resolveServicePorts, resourceName, rewriteUrlsWithPorts, startWorkspaceServices, storageBackendFor, usesConstructs } from "./credentials-jMJC63qs.mjs";
|
|
8
8
|
import { createStageSecrets, rotateServicePassword } from "./generator-VE2JrFlY.mjs";
|
|
9
|
-
import { getKeyPath
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
9
|
+
import { getKeyPath } from "./keystore-TxeYM7an.mjs";
|
|
10
|
+
import { maskPassword, readStageSecrets, secretsExist, setCustomSecret, toEmbeddableSecrets, writeStageSecrets } from "./storage-B020InPW.mjs";
|
|
11
|
+
import { EndpointGenerator, OPENAPI_OUTPUT_PATH, generateOpenApi, generateOpenApiFrom, normalizeRoutes, openapiCommand, resolveOpenApiConfig, runtimeFor } from "./openapi-Bs3-5q_r.mjs";
|
|
12
|
+
import { withCompute, withRoutes } from "./emit-CIlvnDvV.mjs";
|
|
13
|
+
import { DokployApi } from "./dokploy-api-pdaBryoY.mjs";
|
|
14
|
+
import { encryptSecrets } from "./encryption-_4zwjOAR.mjs";
|
|
15
|
+
import { CachedStateProvider } from "./CachedStateProvider-Co4n5Wvs.mjs";
|
|
16
16
|
import { generateReactQueryCommand } from "./openapi-react-query-Byltvm_7.mjs";
|
|
17
|
-
import "./
|
|
18
|
-
import
|
|
17
|
+
import { generateFullstackCustomSecrets } from "./fullstack-secrets-B6KBHf-g.mjs";
|
|
18
|
+
import "./sync-CzHXiR9c.mjs";
|
|
19
|
+
import { setupCommand, trustCommand } from "./setup-D5-JMknk.mjs";
|
|
19
20
|
import { createRequire } from "node:module";
|
|
20
21
|
import { copyFileSync, existsSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
21
22
|
import { basename, dirname, isAbsolute, join, parse, relative, resolve } from "node:path";
|
|
22
|
-
import {
|
|
23
|
+
import { cacheTable, canonicalId, cloudName, cookieDomain, dependentsOf, provideKey, provisionOrder, scopedName, serviceKey } from "@geekmidas/manifest";
|
|
23
24
|
import fg from "fast-glob";
|
|
24
25
|
import { Command } from "commander";
|
|
25
26
|
import { stdin, stdout } from "node:process";
|
|
@@ -28,9 +29,9 @@ import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
|
28
29
|
import { execSync, spawn } from "node:child_process";
|
|
29
30
|
import chokidar from "chokidar";
|
|
30
31
|
import { parse as parse$1, parseDocument } from "yaml";
|
|
31
|
-
import { Client } from "pg";
|
|
32
32
|
import { createHash, randomBytes } from "node:crypto";
|
|
33
33
|
import { ownerRole, readerRole, roleStatements } from "@geekmidas/db/pg/roles";
|
|
34
|
+
import { Client } from "pg";
|
|
34
35
|
import { cacheTableStatements } from "@geekmidas/cache/postgres";
|
|
35
36
|
import { Cron } from "@geekmidas/constructs/crons";
|
|
36
37
|
import { Function } from "@geekmidas/constructs/functions";
|
|
@@ -43,7 +44,7 @@ import prompts from "prompts";
|
|
|
43
44
|
|
|
44
45
|
//#region package.json
|
|
45
46
|
var name = "@geekmidas/cli";
|
|
46
|
-
var version = "10.0.0-alpha.
|
|
47
|
+
var version = "10.0.0-alpha.12";
|
|
47
48
|
var description = "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs";
|
|
48
49
|
var private$1 = false;
|
|
49
50
|
var type = "module";
|
|
@@ -188,7 +189,7 @@ const logger$11 = console;
|
|
|
188
189
|
* Validate Dokploy token by making a test API call
|
|
189
190
|
*/
|
|
190
191
|
async function validateDokployToken(endpoint, token) {
|
|
191
|
-
const { DokployApi: DokployApi$1 } = await import("./dokploy-api-
|
|
192
|
+
const { DokployApi: DokployApi$1 } = await import("./dokploy-api-D2Ny4Jrc.mjs");
|
|
192
193
|
const api = new DokployApi$1({
|
|
193
194
|
baseUrl: endpoint,
|
|
194
195
|
token
|
|
@@ -1848,7 +1849,7 @@ async function buildServer(config, context, provider, enableOpenApi, appRoot = p
|
|
|
1848
1849
|
...context,
|
|
1849
1850
|
storageDrivers: driversFor({
|
|
1850
1851
|
appRoot,
|
|
1851
|
-
cache: cacheBackendsIn(await declaredConstructs(config, appRoot),
|
|
1852
|
+
cache: cacheBackendsIn(await declaredConstructs(config, appRoot), cacheBackendFor(providerOf(config ?? {})))
|
|
1852
1853
|
})
|
|
1853
1854
|
};
|
|
1854
1855
|
const endpointGenerator = new EndpointGenerator();
|
|
@@ -2458,8 +2459,8 @@ function rootGkmConfig(workspace) {
|
|
|
2458
2459
|
const only = backends.length === 1 ? backends[0] : void 0;
|
|
2459
2460
|
const own = only ? getAppGkmConfig(workspace, only[0]) : void 0;
|
|
2460
2461
|
return own ?? {
|
|
2461
|
-
services: workspace.services,
|
|
2462
2462
|
stages: workspace.stages,
|
|
2463
|
+
...workspace.deploy ? { deploy: workspace.deploy } : {},
|
|
2463
2464
|
constructs: allConstructGlobs(workspace)
|
|
2464
2465
|
};
|
|
2465
2466
|
}
|
|
@@ -2477,8 +2478,7 @@ async function buildCommand(options) {
|
|
|
2477
2478
|
}
|
|
2478
2479
|
const config = loadedConfig.type !== "workspace" ? await loadConfig() : resolve(process.cwd()) === resolve(loadedConfig.workspace.root) ? rootGkmConfig(loadedConfig.workspace) : (await loadAppConfig()).gkmConfig;
|
|
2479
2480
|
const resolved = resolveProviders(config, options);
|
|
2480
|
-
const
|
|
2481
|
-
const cacheBackend = cacheBackendOf(backendConfig?.cache);
|
|
2481
|
+
const cacheBackend = cacheBackendFor(providerOf(loadedConfig.workspace));
|
|
2482
2482
|
const productionConfigFromGkm = getProductionConfigFromGkm(config);
|
|
2483
2483
|
const production = normalizeProductionConfig(options.production ?? false, productionConfigFromGkm);
|
|
2484
2484
|
if (production) logger$7.log(`🏭 Building for PRODUCTION`);
|
|
@@ -2490,16 +2490,6 @@ async function buildCommand(options) {
|
|
|
2490
2490
|
if (studio) logger$7.log(`🗄️ Studio enabled at ${studio.path}`);
|
|
2491
2491
|
const hooks = normalizeHooksConfig(config.hooks);
|
|
2492
2492
|
if (hooks) logger$7.log(`🪝 Server hooks enabled`);
|
|
2493
|
-
const services = config.docker?.compose?.services;
|
|
2494
|
-
const dockerServices = services ? Array.isArray(services) ? {
|
|
2495
|
-
postgres: services.includes("postgres"),
|
|
2496
|
-
redis: services.includes("redis"),
|
|
2497
|
-
rabbitmq: services.includes("rabbitmq")
|
|
2498
|
-
} : {
|
|
2499
|
-
postgres: Boolean(services.postgres),
|
|
2500
|
-
redis: Boolean(services.redis),
|
|
2501
|
-
rabbitmq: Boolean(services.rabbitmq)
|
|
2502
|
-
} : void 0;
|
|
2503
2493
|
const constructGlobs$1 = typeof config.constructs === "string" || Array.isArray(config.constructs) ? config.constructs : void 0;
|
|
2504
2494
|
const constructSources = {};
|
|
2505
2495
|
const declared = constructGlobs$1 ? await discover({
|
|
@@ -2544,10 +2534,9 @@ async function buildCommand(options) {
|
|
|
2544
2534
|
} : studio,
|
|
2545
2535
|
hooks,
|
|
2546
2536
|
production,
|
|
2547
|
-
dockerServices,
|
|
2548
2537
|
constructGlobs: constructGlobs$1,
|
|
2549
2538
|
cacheBackend,
|
|
2550
|
-
emailBackend:
|
|
2539
|
+
emailBackend: DEFAULT_EMAIL,
|
|
2551
2540
|
storageDrivers: driversFor({
|
|
2552
2541
|
appRoot: process.cwd(),
|
|
2553
2542
|
cache: cacheBackendsIn(declared, cacheBackend)
|
|
@@ -2639,7 +2628,7 @@ async function buildForProvider(provider, context, rootOutputDir, endpointGenera
|
|
|
2639
2628
|
let masterKey;
|
|
2640
2629
|
if (context.production?.bundle && !skipBundle) {
|
|
2641
2630
|
logger$7.log(`\n📦 Bundling production server...`);
|
|
2642
|
-
const { bundleServer } = await import("./bundler-
|
|
2631
|
+
const { bundleServer } = await import("./bundler-Bux2iF-v.mjs");
|
|
2643
2632
|
const allConstructs = [
|
|
2644
2633
|
...endpoints.map((e) => e.construct),
|
|
2645
2634
|
...functions.map((f) => f.construct),
|
|
@@ -2647,7 +2636,6 @@ async function buildForProvider(provider, context, rootOutputDir, endpointGenera
|
|
|
2647
2636
|
...subscribers.map((s) => s.construct),
|
|
2648
2637
|
...queues.map((q) => q.construct)
|
|
2649
2638
|
];
|
|
2650
|
-
const dockerServices = context.dockerServices;
|
|
2651
2639
|
const bundleResult = await bundleServer({
|
|
2652
2640
|
entryPoint: join(outputDir, "server.ts"),
|
|
2653
2641
|
outputDir: join(outputDir, "dist"),
|
|
@@ -2655,8 +2643,7 @@ async function buildForProvider(provider, context, rootOutputDir, endpointGenera
|
|
|
2655
2643
|
sourcemap: false,
|
|
2656
2644
|
external: context.production.external,
|
|
2657
2645
|
stage,
|
|
2658
|
-
constructs: allConstructs
|
|
2659
|
-
dockerServices
|
|
2646
|
+
constructs: allConstructs
|
|
2660
2647
|
});
|
|
2661
2648
|
masterKey = bundleResult.masterKey;
|
|
2662
2649
|
logger$7.log(`✅ Bundle complete: .gkm/server/dist/server.mjs`);
|
|
@@ -2733,71 +2720,6 @@ function turboFilters(workspace) {
|
|
|
2733
2720
|
};
|
|
2734
2721
|
}
|
|
2735
2722
|
/**
|
|
2736
|
-
* The directory turbo treats as the repo root: the nearest ancestor with a
|
|
2737
|
-
* `turbo.json`. Its own path is what `$TURBO_ROOT$` resolves to.
|
|
2738
|
-
*/
|
|
2739
|
-
function turboRoot(from) {
|
|
2740
|
-
let dir = resolve(from);
|
|
2741
|
-
for (;;) {
|
|
2742
|
-
if (existsSync(join(dir, "turbo.json"))) return dir;
|
|
2743
|
-
const parent = dirname(dir);
|
|
2744
|
-
if (parent === dir) return void 0;
|
|
2745
|
-
dir = parent;
|
|
2746
|
-
}
|
|
2747
|
-
}
|
|
2748
|
-
/**
|
|
2749
|
-
* Write each app's turbo task config, so a construct edit invalidates the build
|
|
2750
|
-
* that read it.
|
|
2751
|
-
*
|
|
2752
|
-
* Turbo hashes a package's own files. An app's constructs live *above* it, in
|
|
2753
|
-
* the workspace that declares them for everybody — so without this, editing a
|
|
2754
|
-
* database or a surface leaves every cached app build in place and the apps
|
|
2755
|
-
* keep serving a graph that no longer exists. It is a silent wrong answer,
|
|
2756
|
-
* which is the worst kind: the build succeeds and the output is stale.
|
|
2757
|
-
*
|
|
2758
|
-
* Generated rather than documented because the globs are already known here and
|
|
2759
|
-
* a hand-written copy is one more list to keep in step — the same reason the
|
|
2760
|
-
* apps themselves are no longer written down. A file that someone has since
|
|
2761
|
-
* edited by hand is left alone.
|
|
2762
|
-
*/
|
|
2763
|
-
async function writeTurboConfigs(workspace) {
|
|
2764
|
-
const root = turboRoot(workspace.root);
|
|
2765
|
-
if (!root) return;
|
|
2766
|
-
const globs = allConstructGlobs(workspace).map((glob) => `$TURBO_ROOT$/${relative(root, glob)}`);
|
|
2767
|
-
if (globs.length === 0) return;
|
|
2768
|
-
const configPath = join(workspace.root, "gkm.config.ts");
|
|
2769
|
-
const inputs = [
|
|
2770
|
-
"$TURBO_DEFAULT$",
|
|
2771
|
-
...globs,
|
|
2772
|
-
...existsSync(configPath) ? [`$TURBO_ROOT$/${relative(root, configPath)}`] : []
|
|
2773
|
-
];
|
|
2774
|
-
for (const app of Object.values(workspace.apps)) {
|
|
2775
|
-
const dir = join(workspace.root, app.path);
|
|
2776
|
-
if (!existsSync(join(dir, "package.json"))) continue;
|
|
2777
|
-
const file = join(dir, "turbo.json");
|
|
2778
|
-
const existing = existsSync(file) ? readFileSync(file, "utf8") : void 0;
|
|
2779
|
-
if (existing && !existing.includes(GENERATED_BY_GKM)) continue;
|
|
2780
|
-
const content = `{
|
|
2781
|
-
"$schema": "https://turborepo.com/schema.json",
|
|
2782
|
-
${GENERATED_BY_GKM}
|
|
2783
|
-
"extends": ["//"],
|
|
2784
|
-
"tasks": {
|
|
2785
|
-
"build": {
|
|
2786
|
-
"inputs": [
|
|
2787
|
-
${inputs.map((glob) => `\t\t\t\t${JSON.stringify(glob)}`).join(",\n")}
|
|
2788
|
-
],
|
|
2789
|
-
"outputs": [${app.type === "backend" ? "\".gkm/**\"" : "\"dist/**\", \".next/**\", \"!.next/cache/**\""}]
|
|
2790
|
-
}
|
|
2791
|
-
}
|
|
2792
|
-
}
|
|
2793
|
-
`;
|
|
2794
|
-
if (existing === content) continue;
|
|
2795
|
-
await writeFile(file, content);
|
|
2796
|
-
}
|
|
2797
|
-
}
|
|
2798
|
-
/** Marks a turbo config as gkm's to rewrite. Edit the file and it is left alone. */
|
|
2799
|
-
const GENERATED_BY_GKM = "// Generated by `gkm build`. Delete this line to take ownership of the file.";
|
|
2800
|
-
/**
|
|
2801
2723
|
* Build all apps in a workspace using Turbo for dependency-ordered parallel builds.
|
|
2802
2724
|
* @internal Exported for testing
|
|
2803
2725
|
*/
|
|
@@ -2815,7 +2737,6 @@ async function workspaceBuildCommand(workspace, options) {
|
|
|
2815
2737
|
const pm = detectPackageManager$2();
|
|
2816
2738
|
logger$7.log(`\n📦 Using ${pm} with Turbo for parallel builds...\n`);
|
|
2817
2739
|
try {
|
|
2818
|
-
await writeTurboConfigs(workspace);
|
|
2819
2740
|
const { filters, unpackaged } = turboFilters(workspace);
|
|
2820
2741
|
if (unpackaged.length > 0) throw new Error(`No package.json for workspace app(s): ${unpackaged.join(", ")}. Each app needs one — turbo builds packages, and gkm reads the name back to know which app it is building.`);
|
|
2821
2742
|
const turboCommand = getTurboCommand(pm, filters);
|
|
@@ -3308,16 +3229,16 @@ var UnresolvedParent = class extends Error {
|
|
|
3308
3229
|
/** A cache with nowhere to live. */
|
|
3309
3230
|
var CacheNeedsAHome = class extends Error {
|
|
3310
3231
|
constructor(id, backend) {
|
|
3311
|
-
super(`'${id}' is a cache and nothing says where it lives. Declare it from its database — \`orders.cache('${id}')
|
|
3232
|
+
super(`'${id}' is a cache and nothing says where it lives. Declare it from its database — \`orders.cache('${id}')\`.` + (backend ? ` The deploy target's backend is '${backend}'.` : ""));
|
|
3312
3233
|
this.id = id;
|
|
3313
3234
|
this.backend = backend;
|
|
3314
3235
|
this.name = "CacheNeedsAHome";
|
|
3315
3236
|
}
|
|
3316
3237
|
};
|
|
3317
|
-
/** `
|
|
3238
|
+
/** A `'db'` cache (a server target's default) with more than one database. */
|
|
3318
3239
|
var CacheIsAmbiguous = class extends Error {
|
|
3319
3240
|
constructor(databases) {
|
|
3320
|
-
super(`A cache backed by the database needs to know which one, and this app declares ${databases.length}: ${databases.join(", ")}. Declare the cache from its database rather than
|
|
3241
|
+
super(`A cache backed by the database needs to know which one, and this app declares ${databases.length}: ${databases.join(", ")}. Declare the cache from its database rather than as a bare new Cache().`);
|
|
3321
3242
|
this.databases = databases;
|
|
3322
3243
|
this.name = "CacheIsAmbiguous";
|
|
3323
3244
|
}
|
|
@@ -3333,7 +3254,7 @@ var SurfaceHasNoAddress = class extends Error {
|
|
|
3333
3254
|
/** A queue or topic on a backend this target has no primitive for. */
|
|
3334
3255
|
var UnprovisionableCarrier = class extends Error {
|
|
3335
3256
|
constructor(id, backend) {
|
|
3336
|
-
super(`'${id}' is carried by '${backend}', which this target cannot provision. SNS needs a topic ARN that has to exist first and RabbitMQ needs a broker Dokploy has no primitive for; pg-boss is a schema tenant of a database this app already declares, which is why it is the one that works here.
|
|
3257
|
+
super(`'${id}' is carried by '${backend}', which this target cannot provision. SNS needs a topic ARN that has to exist first and RabbitMQ needs a broker Dokploy has no primitive for; pg-boss is a schema tenant of a database this app already declares, which is why it is the one that works here. Deploy this app to a target that has the backend.`);
|
|
3337
3258
|
this.id = id;
|
|
3338
3259
|
this.backend = backend;
|
|
3339
3260
|
this.name = "UnprovisionableCarrier";
|
|
@@ -3342,7 +3263,7 @@ var UnprovisionableCarrier = class extends Error {
|
|
|
3342
3263
|
/** A pg-boss carrier in an app that declares no database to host it. */
|
|
3343
3264
|
var BrokerNeedsADatabase = class extends Error {
|
|
3344
3265
|
constructor(id) {
|
|
3345
|
-
super(`'${id}' is carried by pg-boss, which lives in the database the app declares — and this app declares none. Declare a database, or
|
|
3266
|
+
super(`'${id}' is carried by pg-boss, which lives in the database the app declares — and this app declares none. Declare a database, or deploy to a target whose broker is its own.`);
|
|
3346
3267
|
this.id = id;
|
|
3347
3268
|
this.name = "BrokerNeedsADatabase";
|
|
3348
3269
|
}
|
|
@@ -3350,7 +3271,7 @@ var BrokerNeedsADatabase = class extends Error {
|
|
|
3350
3271
|
/** A bucket on a backend this target has no primitive for. */
|
|
3351
3272
|
var UnprovisionableBucket = class extends Error {
|
|
3352
3273
|
constructor(id, backend) {
|
|
3353
|
-
super(`'${id}' is stored on '${backend}', which this target cannot provision: an S3 or R2 bucket belongs to an account this deploy does not hold, the way an API key does.
|
|
3274
|
+
super(`'${id}' is stored on '${backend}', which this target cannot provision: an S3 or R2 bucket belongs to an account this deploy does not hold, the way an API key does. Supply the bucket's URL.`);
|
|
3354
3275
|
this.id = id;
|
|
3355
3276
|
this.backend = backend;
|
|
3356
3277
|
this.name = "UnprovisionableBucket";
|
|
@@ -3388,9 +3309,9 @@ async function provisionDeclared(options) {
|
|
|
3388
3309
|
environmentId: options.environmentId,
|
|
3389
3310
|
stage: options.stage,
|
|
3390
3311
|
project: workspace.name,
|
|
3391
|
-
cache:
|
|
3392
|
-
|
|
3393
|
-
storage:
|
|
3312
|
+
cache: cacheBackendFor(providerOf(workspace)),
|
|
3313
|
+
events: eventsBackendFor(providerOf(workspace)),
|
|
3314
|
+
storage: storageBackendFor(providerOf(workspace)),
|
|
3394
3315
|
addresses: surfaceAddresses(workspace, manifest, options.appUrls),
|
|
3395
3316
|
...options.secrets ? { secrets: options.secrets } : {},
|
|
3396
3317
|
deferred: [],
|
|
@@ -3592,11 +3513,11 @@ async function createDnsProvider(options) {
|
|
|
3592
3513
|
if (isDnsProvider(config.provider)) return config.provider;
|
|
3593
3514
|
const provider = config.provider;
|
|
3594
3515
|
if (provider === "hostinger") {
|
|
3595
|
-
const { HostingerProvider } = await import("./HostingerProvider-
|
|
3516
|
+
const { HostingerProvider } = await import("./HostingerProvider-BcyFRbqV.mjs");
|
|
3596
3517
|
return new HostingerProvider();
|
|
3597
3518
|
}
|
|
3598
3519
|
if (provider === "route53") {
|
|
3599
|
-
const { Route53Provider } = await import("./Route53Provider-
|
|
3520
|
+
const { Route53Provider } = await import("./Route53Provider-C0xmer8U.mjs");
|
|
3600
3521
|
const route53Config = config;
|
|
3601
3522
|
return new Route53Provider({
|
|
3602
3523
|
region: route53Config.region,
|
|
@@ -4029,563 +3950,6 @@ function getPublicUrlArgNames(app) {
|
|
|
4029
3950
|
return app.dependencies.map((dep) => `${prefix}${dep.toUpperCase()}_URL`);
|
|
4030
3951
|
}
|
|
4031
3952
|
|
|
4032
|
-
//#endregion
|
|
4033
|
-
//#region src/docker/compose.ts
|
|
4034
|
-
/** Default Docker images for services */
|
|
4035
|
-
const DEFAULT_SERVICE_IMAGES = {
|
|
4036
|
-
postgres: "postgres",
|
|
4037
|
-
redis: "redis",
|
|
4038
|
-
rabbitmq: "rabbitmq",
|
|
4039
|
-
minio: "pgsty/minio",
|
|
4040
|
-
mailpit: "axllent/mailpit",
|
|
4041
|
-
localstack: "floci/floci"
|
|
4042
|
-
};
|
|
4043
|
-
/** Default Docker image versions for services */
|
|
4044
|
-
const DEFAULT_SERVICE_VERSIONS = {
|
|
4045
|
-
postgres: `${DEFAULT_POSTGRES_VERSION}-alpine`,
|
|
4046
|
-
redis: "7-alpine",
|
|
4047
|
-
rabbitmq: "3-management-alpine",
|
|
4048
|
-
minio: "RELEASE.2026-08-04T00-00-00Z",
|
|
4049
|
-
mailpit: "latest",
|
|
4050
|
-
localstack: "latest"
|
|
4051
|
-
};
|
|
4052
|
-
/** Get the default full image reference for a service */
|
|
4053
|
-
function getDefaultImage(serviceName$1) {
|
|
4054
|
-
return `${DEFAULT_SERVICE_IMAGES[serviceName$1]}:${DEFAULT_SERVICE_VERSIONS[serviceName$1]}`;
|
|
4055
|
-
}
|
|
4056
|
-
/** Normalize services config to a consistent format - returns Map of service name to full image reference */
|
|
4057
|
-
function normalizeServices(services) {
|
|
4058
|
-
const result = /* @__PURE__ */ new Map();
|
|
4059
|
-
if (Array.isArray(services)) for (const name$1 of services) result.set(name$1, getDefaultImage(name$1));
|
|
4060
|
-
else for (const [name$1, config] of Object.entries(services)) {
|
|
4061
|
-
const serviceName$1 = name$1;
|
|
4062
|
-
if (config === true) result.set(serviceName$1, getDefaultImage(serviceName$1));
|
|
4063
|
-
else if (config && typeof config === "object") {
|
|
4064
|
-
const serviceConfig = config;
|
|
4065
|
-
if (serviceConfig.image) result.set(serviceName$1, serviceConfig.image);
|
|
4066
|
-
else {
|
|
4067
|
-
const version$1 = serviceConfig.version ?? DEFAULT_SERVICE_VERSIONS[serviceName$1];
|
|
4068
|
-
result.set(serviceName$1, `${DEFAULT_SERVICE_IMAGES[serviceName$1]}:${version$1}`);
|
|
4069
|
-
}
|
|
4070
|
-
}
|
|
4071
|
-
}
|
|
4072
|
-
return result;
|
|
4073
|
-
}
|
|
4074
|
-
/**
|
|
4075
|
-
* Generate docker-compose.yml for production deployment
|
|
4076
|
-
*/
|
|
4077
|
-
function generateDockerCompose(options) {
|
|
4078
|
-
const { imageName, registry, port, healthCheckPath, services } = options;
|
|
4079
|
-
const serviceMap = normalizeServices(services);
|
|
4080
|
-
const imageRef = registry ? `\${REGISTRY:-${registry}}/` : "";
|
|
4081
|
-
let yaml = `# Use "gkm dev" or "gkm test" to start services.
|
|
4082
|
-
# Running "docker compose up" directly will not inject secrets or resolve ports.
|
|
4083
|
-
version: '3.8'
|
|
4084
|
-
|
|
4085
|
-
services:
|
|
4086
|
-
api:
|
|
4087
|
-
build:
|
|
4088
|
-
context: ../..
|
|
4089
|
-
dockerfile: .gkm/docker/Dockerfile
|
|
4090
|
-
image: ${imageRef}\${IMAGE_NAME:-${imageName}}:\${TAG:-latest}
|
|
4091
|
-
container_name: ${imageName}
|
|
4092
|
-
restart: unless-stopped
|
|
4093
|
-
ports:
|
|
4094
|
-
- "\${PORT:-${port}}:${port}"
|
|
4095
|
-
environment:
|
|
4096
|
-
- NODE_ENV=production
|
|
4097
|
-
`;
|
|
4098
|
-
if (serviceMap.has("postgres")) yaml += ` - DATABASE_URL=\${DATABASE_URL:-postgresql://\${POSTGRES_USER:-postgres}:\${POSTGRES_PASSWORD:-postgres}@postgres:5432/\${POSTGRES_DB:-app}}
|
|
4099
|
-
`;
|
|
4100
|
-
if (serviceMap.has("redis")) yaml += ` - REDIS_URL=\${REDIS_URL:-redis://redis:6379}
|
|
4101
|
-
`;
|
|
4102
|
-
if (serviceMap.has("rabbitmq")) yaml += ` - RABBITMQ_URL=\${RABBITMQ_URL:-amqp://rabbitmq:5672}
|
|
4103
|
-
`;
|
|
4104
|
-
if (serviceMap.has("minio")) yaml += ` - STORAGE_ENDPOINT=\${STORAGE_ENDPOINT:-http://minio:9000}
|
|
4105
|
-
- STORAGE_ACCESS_KEY_ID=\${STORAGE_ACCESS_KEY_ID:-${imageName}}
|
|
4106
|
-
- STORAGE_SECRET_ACCESS_KEY=\${STORAGE_SECRET_ACCESS_KEY:-${imageName}}
|
|
4107
|
-
- STORAGE_BUCKET=\${STORAGE_BUCKET:-${imageName}}
|
|
4108
|
-
- STORAGE_REGION=\${STORAGE_REGION:-eu-west-1}
|
|
4109
|
-
- STORAGE_FORCE_PATH_STYLE=true
|
|
4110
|
-
`;
|
|
4111
|
-
if (serviceMap.has("mailpit")) yaml += ` - SMTP_HOST=\${SMTP_HOST:-mailpit}
|
|
4112
|
-
- SMTP_PORT=\${SMTP_PORT:-1025}
|
|
4113
|
-
- SMTP_USER=\${SMTP_USER:-${imageName}}
|
|
4114
|
-
- SMTP_PASS=\${SMTP_PASS:-${imageName}}
|
|
4115
|
-
- SMTP_SECURE=\${SMTP_SECURE:-false}
|
|
4116
|
-
- MAIL_FROM=\${MAIL_FROM:-noreply@localhost}
|
|
4117
|
-
`;
|
|
4118
|
-
if (serviceMap.has("localstack")) yaml += ` - AWS_ACCESS_KEY_ID=\${AWS_ACCESS_KEY_ID:-localstack}
|
|
4119
|
-
- AWS_SECRET_ACCESS_KEY=\${AWS_SECRET_ACCESS_KEY:-localstack}
|
|
4120
|
-
- AWS_REGION=\${AWS_REGION:-us-east-1}
|
|
4121
|
-
- AWS_ENDPOINT_URL=\${AWS_ENDPOINT_URL:-http://localstack:4566}
|
|
4122
|
-
`;
|
|
4123
|
-
yaml += ` healthcheck:
|
|
4124
|
-
test: ["CMD", "wget", "-q", "--spider", "http://localhost:${port}${healthCheckPath}"]
|
|
4125
|
-
interval: 30s
|
|
4126
|
-
timeout: 3s
|
|
4127
|
-
retries: 3
|
|
4128
|
-
`;
|
|
4129
|
-
if (serviceMap.size > 0) {
|
|
4130
|
-
yaml += ` depends_on:
|
|
4131
|
-
`;
|
|
4132
|
-
for (const serviceName$1 of serviceMap.keys()) yaml += ` ${serviceName$1}:
|
|
4133
|
-
condition: service_healthy
|
|
4134
|
-
`;
|
|
4135
|
-
}
|
|
4136
|
-
yaml += ` networks:
|
|
4137
|
-
- app-network
|
|
4138
|
-
`;
|
|
4139
|
-
const postgresImage = serviceMap.get("postgres");
|
|
4140
|
-
if (postgresImage) yaml += `
|
|
4141
|
-
postgres:
|
|
4142
|
-
image: ${postgresImage}
|
|
4143
|
-
container_name: postgres
|
|
4144
|
-
restart: unless-stopped
|
|
4145
|
-
environment:
|
|
4146
|
-
POSTGRES_USER: \${POSTGRES_USER:-postgres}
|
|
4147
|
-
POSTGRES_PASSWORD: \${POSTGRES_PASSWORD:-postgres}
|
|
4148
|
-
POSTGRES_DB: \${POSTGRES_DB:-app}
|
|
4149
|
-
volumes:
|
|
4150
|
-
- dbdata:/var/lib/postgresql
|
|
4151
|
-
healthcheck:
|
|
4152
|
-
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER"]
|
|
4153
|
-
interval: 5s
|
|
4154
|
-
timeout: 5s
|
|
4155
|
-
retries: 5
|
|
4156
|
-
networks:
|
|
4157
|
-
- app-network
|
|
4158
|
-
`;
|
|
4159
|
-
const redisImage = serviceMap.get("redis");
|
|
4160
|
-
if (redisImage) yaml += `
|
|
4161
|
-
redis:
|
|
4162
|
-
image: ${redisImage}
|
|
4163
|
-
container_name: redis
|
|
4164
|
-
restart: unless-stopped
|
|
4165
|
-
volumes:
|
|
4166
|
-
- redis_data:/data
|
|
4167
|
-
healthcheck:
|
|
4168
|
-
test: ["CMD", "redis-cli", "ping"]
|
|
4169
|
-
interval: 5s
|
|
4170
|
-
timeout: 5s
|
|
4171
|
-
retries: 5
|
|
4172
|
-
networks:
|
|
4173
|
-
- app-network
|
|
4174
|
-
`;
|
|
4175
|
-
const rabbitmqImage = serviceMap.get("rabbitmq");
|
|
4176
|
-
if (rabbitmqImage) yaml += `
|
|
4177
|
-
rabbitmq:
|
|
4178
|
-
image: ${rabbitmqImage}
|
|
4179
|
-
container_name: rabbitmq
|
|
4180
|
-
restart: unless-stopped
|
|
4181
|
-
environment:
|
|
4182
|
-
RABBITMQ_DEFAULT_USER: \${RABBITMQ_USER:-guest}
|
|
4183
|
-
RABBITMQ_DEFAULT_PASS: \${RABBITMQ_PASSWORD:-guest}
|
|
4184
|
-
ports:
|
|
4185
|
-
- "15672:15672" # Management UI
|
|
4186
|
-
volumes:
|
|
4187
|
-
- rabbitmq_data:/var/lib/rabbitmq
|
|
4188
|
-
healthcheck:
|
|
4189
|
-
test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"]
|
|
4190
|
-
interval: 10s
|
|
4191
|
-
timeout: 5s
|
|
4192
|
-
retries: 5
|
|
4193
|
-
networks:
|
|
4194
|
-
- app-network
|
|
4195
|
-
`;
|
|
4196
|
-
const minioImage = serviceMap.get("minio");
|
|
4197
|
-
if (minioImage) yaml += `
|
|
4198
|
-
minio:
|
|
4199
|
-
image: ${minioImage}
|
|
4200
|
-
container_name: minio
|
|
4201
|
-
restart: unless-stopped
|
|
4202
|
-
entrypoint: sh
|
|
4203
|
-
command: -c 'mkdir -p /data/\${STORAGE_BUCKET:-${imageName}} && /usr/bin/docker-entrypoint.sh server --console-address ":9001" /data'
|
|
4204
|
-
environment:
|
|
4205
|
-
MINIO_ROOT_USER: \${STORAGE_ACCESS_KEY_ID:-${imageName}}
|
|
4206
|
-
MINIO_ROOT_PASSWORD: \${STORAGE_SECRET_ACCESS_KEY:-${imageName}}
|
|
4207
|
-
ports:
|
|
4208
|
-
- "\${MINIO_API_PORT:-9000}:9000"
|
|
4209
|
-
- "\${MINIO_CONSOLE_PORT:-9001}:9001"
|
|
4210
|
-
volumes:
|
|
4211
|
-
- minio_data:/data
|
|
4212
|
-
healthcheck:
|
|
4213
|
-
test: ["CMD", "mc", "ready", "local"]
|
|
4214
|
-
interval: 10s
|
|
4215
|
-
timeout: 5s
|
|
4216
|
-
retries: 5
|
|
4217
|
-
networks:
|
|
4218
|
-
- app-network
|
|
4219
|
-
`;
|
|
4220
|
-
const mailpitImage = serviceMap.get("mailpit");
|
|
4221
|
-
if (mailpitImage) yaml += `
|
|
4222
|
-
mailpit:
|
|
4223
|
-
image: ${mailpitImage}
|
|
4224
|
-
container_name: mailpit
|
|
4225
|
-
restart: unless-stopped
|
|
4226
|
-
environment:
|
|
4227
|
-
MP_SMTP_AUTH: \${SMTP_USER:-${imageName}}:\${SMTP_PASS:-${imageName}}
|
|
4228
|
-
MP_SMTP_AUTH_ALLOW_INSECURE: true
|
|
4229
|
-
ports:
|
|
4230
|
-
- "\${MAILPIT_PORT:-8025}:8025" # Web UI / API
|
|
4231
|
-
- "\${SMTP_PORT:-1025}:1025" # SMTP
|
|
4232
|
-
healthcheck:
|
|
4233
|
-
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8025"]
|
|
4234
|
-
interval: 5s
|
|
4235
|
-
timeout: 5s
|
|
4236
|
-
retries: 5
|
|
4237
|
-
networks:
|
|
4238
|
-
- app-network
|
|
4239
|
-
`;
|
|
4240
|
-
const localstackImage = serviceMap.get("localstack");
|
|
4241
|
-
if (localstackImage) yaml += `
|
|
4242
|
-
localstack:
|
|
4243
|
-
image: ${localstackImage}
|
|
4244
|
-
container_name: localstack
|
|
4245
|
-
restart: unless-stopped
|
|
4246
|
-
environment:
|
|
4247
|
-
AWS_DEFAULT_REGION: \${AWS_REGION:-us-east-1}
|
|
4248
|
-
AWS_ACCESS_KEY_ID: \${AWS_ACCESS_KEY_ID:-localstack}
|
|
4249
|
-
AWS_SECRET_ACCESS_KEY: \${AWS_SECRET_ACCESS_KEY:-localstack}
|
|
4250
|
-
ports:
|
|
4251
|
-
- "\${LOCALSTACK_PORT:-4566}:4566"
|
|
4252
|
-
volumes:
|
|
4253
|
-
- localstack_data:/var/lib/localstack
|
|
4254
|
-
healthcheck:
|
|
4255
|
-
test: ["CMD", "curl", "-f", "http://localhost:4566/_localstack/health"]
|
|
4256
|
-
interval: 10s
|
|
4257
|
-
timeout: 5s
|
|
4258
|
-
retries: 5
|
|
4259
|
-
networks:
|
|
4260
|
-
- app-network
|
|
4261
|
-
`;
|
|
4262
|
-
yaml += `
|
|
4263
|
-
volumes:
|
|
4264
|
-
`;
|
|
4265
|
-
if (serviceMap.has("postgres")) yaml += ` dbdata:
|
|
4266
|
-
`;
|
|
4267
|
-
if (serviceMap.has("redis")) yaml += ` redis_data:
|
|
4268
|
-
`;
|
|
4269
|
-
if (serviceMap.has("rabbitmq")) yaml += ` rabbitmq_data:
|
|
4270
|
-
`;
|
|
4271
|
-
if (serviceMap.has("minio")) yaml += ` minio_data:
|
|
4272
|
-
`;
|
|
4273
|
-
if (serviceMap.has("localstack")) yaml += ` localstack_data:
|
|
4274
|
-
`;
|
|
4275
|
-
yaml += `
|
|
4276
|
-
networks:
|
|
4277
|
-
app-network:
|
|
4278
|
-
driver: bridge
|
|
4279
|
-
`;
|
|
4280
|
-
return yaml;
|
|
4281
|
-
}
|
|
4282
|
-
/**
|
|
4283
|
-
* Generate a minimal docker-compose.yml for API only
|
|
4284
|
-
*/
|
|
4285
|
-
function generateMinimalDockerCompose(options) {
|
|
4286
|
-
const { imageName, registry, port, healthCheckPath } = options;
|
|
4287
|
-
const imageRef = registry ? `\${REGISTRY:-${registry}}/` : "";
|
|
4288
|
-
return `# Use "gkm dev" or "gkm test" to start services.
|
|
4289
|
-
# Running "docker compose up" directly will not inject secrets or resolve ports.
|
|
4290
|
-
version: '3.8'
|
|
4291
|
-
|
|
4292
|
-
services:
|
|
4293
|
-
api:
|
|
4294
|
-
build:
|
|
4295
|
-
context: ../..
|
|
4296
|
-
dockerfile: .gkm/docker/Dockerfile
|
|
4297
|
-
image: ${imageRef}\${IMAGE_NAME:-${imageName}}:\${TAG:-latest}
|
|
4298
|
-
container_name: ${imageName}
|
|
4299
|
-
restart: unless-stopped
|
|
4300
|
-
ports:
|
|
4301
|
-
- "\${PORT:-${port}}:${port}"
|
|
4302
|
-
environment:
|
|
4303
|
-
- NODE_ENV=production
|
|
4304
|
-
healthcheck:
|
|
4305
|
-
test: ["CMD", "wget", "-q", "--spider", "http://localhost:${port}${healthCheckPath}"]
|
|
4306
|
-
interval: 30s
|
|
4307
|
-
timeout: 3s
|
|
4308
|
-
retries: 3
|
|
4309
|
-
networks:
|
|
4310
|
-
- app-network
|
|
4311
|
-
|
|
4312
|
-
networks:
|
|
4313
|
-
app-network:
|
|
4314
|
-
driver: bridge
|
|
4315
|
-
`;
|
|
4316
|
-
}
|
|
4317
|
-
/**
|
|
4318
|
-
* Generate docker-compose.yml for a workspace with all apps as services.
|
|
4319
|
-
* Apps can communicate with each other via service names.
|
|
4320
|
-
* @internal Exported for testing
|
|
4321
|
-
*/
|
|
4322
|
-
function generateWorkspaceCompose(workspace, options = {}) {
|
|
4323
|
-
const { registry } = options;
|
|
4324
|
-
const apps = Object.entries(workspace.apps);
|
|
4325
|
-
const services = workspace.services;
|
|
4326
|
-
const containers = new Set(options.containers ?? []);
|
|
4327
|
-
const hasPostgres = containers.has("postgres");
|
|
4328
|
-
const hasRedis = containers.has("redis") || containers.has("redis-http");
|
|
4329
|
-
const hasMail = containers.has("mailpit");
|
|
4330
|
-
const hasMinio = containers.has("minio");
|
|
4331
|
-
const hasLocalStack = containers.has("localstack");
|
|
4332
|
-
const hasRabbitMQ = containers.has("rabbitmq");
|
|
4333
|
-
const eventsBackend = services.events;
|
|
4334
|
-
const postgresImage = getInfraServiceImage("postgres", services.images?.postgres);
|
|
4335
|
-
const redisImage = getInfraServiceImage("redis", services.images?.redis);
|
|
4336
|
-
const minioImage = getInfraServiceImage("minio", services.images?.minio);
|
|
4337
|
-
let yaml = `# Docker Compose for ${workspace.name} workspace
|
|
4338
|
-
# Use "gkm dev" or "gkm test" to start services.
|
|
4339
|
-
# Running "docker compose up" directly will not inject secrets or resolve ports.
|
|
4340
|
-
|
|
4341
|
-
services:
|
|
4342
|
-
`;
|
|
4343
|
-
for (const [appName, app] of apps) {
|
|
4344
|
-
if (app.type === "mobile") continue;
|
|
4345
|
-
yaml += generateAppService(appName, app, apps, {
|
|
4346
|
-
registry,
|
|
4347
|
-
projectName: workspace.name,
|
|
4348
|
-
hasPostgres,
|
|
4349
|
-
hasRedis,
|
|
4350
|
-
hasMinio,
|
|
4351
|
-
hasMail,
|
|
4352
|
-
eventsBackend
|
|
4353
|
-
});
|
|
4354
|
-
}
|
|
4355
|
-
if (hasPostgres) yaml += `
|
|
4356
|
-
postgres:
|
|
4357
|
-
image: ${postgresImage}
|
|
4358
|
-
container_name: ${workspace.name}-postgres
|
|
4359
|
-
restart: unless-stopped
|
|
4360
|
-
environment:
|
|
4361
|
-
POSTGRES_USER: \${POSTGRES_USER:-postgres}
|
|
4362
|
-
POSTGRES_PASSWORD: \${POSTGRES_PASSWORD:-postgres}
|
|
4363
|
-
POSTGRES_DB: \${POSTGRES_DB:-app}
|
|
4364
|
-
volumes:
|
|
4365
|
-
- dbdata:/var/lib/postgresql
|
|
4366
|
-
healthcheck:
|
|
4367
|
-
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER"]
|
|
4368
|
-
interval: 5s
|
|
4369
|
-
timeout: 5s
|
|
4370
|
-
retries: 5
|
|
4371
|
-
networks:
|
|
4372
|
-
- workspace-network
|
|
4373
|
-
`;
|
|
4374
|
-
if (hasRedis) yaml += `
|
|
4375
|
-
redis:
|
|
4376
|
-
image: ${redisImage}
|
|
4377
|
-
container_name: ${workspace.name}-redis
|
|
4378
|
-
restart: unless-stopped
|
|
4379
|
-
volumes:
|
|
4380
|
-
- redis_data:/data
|
|
4381
|
-
healthcheck:
|
|
4382
|
-
test: ["CMD", "redis-cli", "ping"]
|
|
4383
|
-
interval: 5s
|
|
4384
|
-
timeout: 5s
|
|
4385
|
-
retries: 5
|
|
4386
|
-
networks:
|
|
4387
|
-
- workspace-network
|
|
4388
|
-
`;
|
|
4389
|
-
if (hasMail) yaml += `
|
|
4390
|
-
mailpit:
|
|
4391
|
-
image: axllent/mailpit:latest
|
|
4392
|
-
container_name: ${workspace.name}-mailpit
|
|
4393
|
-
restart: unless-stopped
|
|
4394
|
-
environment:
|
|
4395
|
-
MP_SMTP_AUTH: \${SMTP_USER:-${workspace.name}}:\${SMTP_PASS:-${workspace.name}}
|
|
4396
|
-
MP_SMTP_AUTH_ALLOW_INSECURE: true
|
|
4397
|
-
ports:
|
|
4398
|
-
- "\${MAILPIT_PORT:-8025}:8025" # Web UI / API
|
|
4399
|
-
- "\${SMTP_PORT:-1025}:1025" # SMTP
|
|
4400
|
-
healthcheck:
|
|
4401
|
-
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8025"]
|
|
4402
|
-
interval: 5s
|
|
4403
|
-
timeout: 5s
|
|
4404
|
-
retries: 5
|
|
4405
|
-
networks:
|
|
4406
|
-
- workspace-network
|
|
4407
|
-
`;
|
|
4408
|
-
if (hasMinio) yaml += `
|
|
4409
|
-
minio:
|
|
4410
|
-
image: ${minioImage}
|
|
4411
|
-
container_name: ${workspace.name}-minio
|
|
4412
|
-
restart: unless-stopped
|
|
4413
|
-
entrypoint: sh
|
|
4414
|
-
command: -c 'mkdir -p /data/\${STORAGE_BUCKET:-${workspace.name}} && /usr/bin/docker-entrypoint.sh server --console-address ":9001" /data'
|
|
4415
|
-
environment:
|
|
4416
|
-
MINIO_ROOT_USER: \${STORAGE_ACCESS_KEY_ID:-${workspace.name}}
|
|
4417
|
-
MINIO_ROOT_PASSWORD: \${STORAGE_SECRET_ACCESS_KEY:-${workspace.name}}
|
|
4418
|
-
ports:
|
|
4419
|
-
- "\${MINIO_API_PORT:-9000}:9000"
|
|
4420
|
-
- "\${MINIO_CONSOLE_PORT:-9001}:9001"
|
|
4421
|
-
volumes:
|
|
4422
|
-
- minio_data:/data
|
|
4423
|
-
healthcheck:
|
|
4424
|
-
test: ["CMD", "mc", "ready", "local"]
|
|
4425
|
-
interval: 10s
|
|
4426
|
-
timeout: 5s
|
|
4427
|
-
retries: 5
|
|
4428
|
-
networks:
|
|
4429
|
-
- workspace-network
|
|
4430
|
-
`;
|
|
4431
|
-
if (hasLocalStack) yaml += `
|
|
4432
|
-
localstack:
|
|
4433
|
-
image: floci/floci:latest
|
|
4434
|
-
container_name: ${workspace.name}-localstack
|
|
4435
|
-
restart: unless-stopped
|
|
4436
|
-
environment:
|
|
4437
|
-
AWS_DEFAULT_REGION: \${AWS_REGION:-us-east-1}
|
|
4438
|
-
AWS_ACCESS_KEY_ID: \${AWS_ACCESS_KEY_ID:-localstack}
|
|
4439
|
-
AWS_SECRET_ACCESS_KEY: \${AWS_SECRET_ACCESS_KEY:-localstack}
|
|
4440
|
-
ports:
|
|
4441
|
-
- "\${LOCALSTACK_PORT:-4566}:4566"
|
|
4442
|
-
volumes:
|
|
4443
|
-
- localstack_data:/var/lib/localstack
|
|
4444
|
-
healthcheck:
|
|
4445
|
-
test: ["CMD", "curl", "-f", "http://localhost:4566/_localstack/health"]
|
|
4446
|
-
interval: 10s
|
|
4447
|
-
timeout: 5s
|
|
4448
|
-
retries: 5
|
|
4449
|
-
networks:
|
|
4450
|
-
- workspace-network
|
|
4451
|
-
`;
|
|
4452
|
-
if (hasRabbitMQ) yaml += `
|
|
4453
|
-
rabbitmq:
|
|
4454
|
-
image: rabbitmq:3-management-alpine
|
|
4455
|
-
container_name: ${workspace.name}-rabbitmq
|
|
4456
|
-
restart: unless-stopped
|
|
4457
|
-
environment:
|
|
4458
|
-
RABBITMQ_DEFAULT_USER: \${RABBITMQ_USER:-guest}
|
|
4459
|
-
RABBITMQ_DEFAULT_PASS: \${RABBITMQ_PASSWORD:-guest}
|
|
4460
|
-
ports:
|
|
4461
|
-
- "\${RABBITMQ_HOST_PORT:-5672}:5672"
|
|
4462
|
-
- "\${RABBITMQ_MGMT_HOST_PORT:-15672}:15672"
|
|
4463
|
-
volumes:
|
|
4464
|
-
- rabbitmq_data:/var/lib/rabbitmq
|
|
4465
|
-
healthcheck:
|
|
4466
|
-
test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"]
|
|
4467
|
-
interval: 10s
|
|
4468
|
-
timeout: 5s
|
|
4469
|
-
retries: 5
|
|
4470
|
-
networks:
|
|
4471
|
-
- workspace-network
|
|
4472
|
-
`;
|
|
4473
|
-
yaml += `
|
|
4474
|
-
volumes:
|
|
4475
|
-
`;
|
|
4476
|
-
if (hasPostgres) yaml += ` dbdata:
|
|
4477
|
-
`;
|
|
4478
|
-
if (hasRedis) yaml += ` redis_data:
|
|
4479
|
-
`;
|
|
4480
|
-
if (hasMinio) yaml += ` minio_data:
|
|
4481
|
-
`;
|
|
4482
|
-
if (hasLocalStack) yaml += ` localstack_data:
|
|
4483
|
-
`;
|
|
4484
|
-
if (hasRabbitMQ) yaml += ` rabbitmq_data:
|
|
4485
|
-
`;
|
|
4486
|
-
yaml += `
|
|
4487
|
-
networks:
|
|
4488
|
-
workspace-network:
|
|
4489
|
-
driver: bridge
|
|
4490
|
-
`;
|
|
4491
|
-
return yaml;
|
|
4492
|
-
}
|
|
4493
|
-
/**
|
|
4494
|
-
* Get infrastructure service image with version.
|
|
4495
|
-
*/
|
|
4496
|
-
function getInfraServiceImage(serviceName$1, pin) {
|
|
4497
|
-
const defaults = {
|
|
4498
|
-
postgres: "postgres:18-alpine",
|
|
4499
|
-
redis: "redis:7-alpine",
|
|
4500
|
-
minio: "pgsty/minio:RELEASE.2026-08-04T00-00-00Z"
|
|
4501
|
-
};
|
|
4502
|
-
return pin || defaults[serviceName$1];
|
|
4503
|
-
}
|
|
4504
|
-
/**
|
|
4505
|
-
* Generate a service definition for an app.
|
|
4506
|
-
*/
|
|
4507
|
-
function generateAppService(appName, app, allApps, options) {
|
|
4508
|
-
const { registry, projectName, hasPostgres, hasRedis, hasMinio, hasMail, eventsBackend } = options;
|
|
4509
|
-
const imageRef = registry ? `\${REGISTRY:-${registry}}/` : "";
|
|
4510
|
-
const healthCheckPath = app.type === "web" ? "/" : "/health";
|
|
4511
|
-
const healthCheckCmd = app.type === "web" ? `["CMD", "wget", "-q", "--spider", "http://localhost:${app.port}/"]` : `["CMD", "wget", "-q", "--spider", "http://localhost:${app.port}${healthCheckPath}"]`;
|
|
4512
|
-
let yaml = `
|
|
4513
|
-
${appName}:
|
|
4514
|
-
build:
|
|
4515
|
-
context: .
|
|
4516
|
-
dockerfile: .gkm/docker/Dockerfile.${appName}
|
|
4517
|
-
image: ${imageRef}\${${appName.toUpperCase()}_IMAGE:-${appName}}:\${TAG:-latest}
|
|
4518
|
-
container_name: ${appName}
|
|
4519
|
-
restart: unless-stopped
|
|
4520
|
-
ports:
|
|
4521
|
-
- "\${${appName.toUpperCase()}_PORT:-${app.port}}:${app.port}"
|
|
4522
|
-
environment:
|
|
4523
|
-
- NODE_ENV=production
|
|
4524
|
-
- PORT=${app.port}
|
|
4525
|
-
`;
|
|
4526
|
-
for (const dep of app.dependencies) {
|
|
4527
|
-
const depApp = allApps.find(([name$1]) => name$1 === dep)?.[1];
|
|
4528
|
-
if (depApp) yaml += ` - ${dep.toUpperCase()}_URL=http://${dep}:${depApp.port}
|
|
4529
|
-
`;
|
|
4530
|
-
}
|
|
4531
|
-
if (app.type === "backend") {
|
|
4532
|
-
if (hasPostgres) yaml += ` - DATABASE_URL=\${DATABASE_URL:-postgresql://\${POSTGRES_USER:-postgres}:\${POSTGRES_PASSWORD:-postgres}@postgres:5432/\${POSTGRES_DB:-app}}
|
|
4533
|
-
`;
|
|
4534
|
-
if (hasRedis) yaml += ` - REDIS_URL=\${REDIS_URL:-redis://redis:6379}
|
|
4535
|
-
`;
|
|
4536
|
-
if (hasMinio) yaml += ` - STORAGE_ENDPOINT=\${STORAGE_ENDPOINT:-http://minio:9000}
|
|
4537
|
-
- STORAGE_ACCESS_KEY_ID=\${STORAGE_ACCESS_KEY_ID:-${projectName}}
|
|
4538
|
-
- STORAGE_SECRET_ACCESS_KEY=\${STORAGE_SECRET_ACCESS_KEY:-${projectName}}
|
|
4539
|
-
- STORAGE_BUCKET=\${STORAGE_BUCKET:-${projectName}}
|
|
4540
|
-
- STORAGE_REGION=\${STORAGE_REGION:-eu-west-1}
|
|
4541
|
-
- STORAGE_FORCE_PATH_STYLE=true
|
|
4542
|
-
`;
|
|
4543
|
-
if (hasMail) yaml += ` - SMTP_HOST=\${SMTP_HOST:-mailpit}
|
|
4544
|
-
- SMTP_PORT=\${SMTP_PORT:-1025}
|
|
4545
|
-
- SMTP_USER=\${SMTP_USER:-${projectName}}
|
|
4546
|
-
- SMTP_PASS=\${SMTP_PASS:-${projectName}}
|
|
4547
|
-
- SMTP_SECURE=\${SMTP_SECURE:-false}
|
|
4548
|
-
- MAIL_FROM=\${MAIL_FROM:-noreply@localhost}
|
|
4549
|
-
`;
|
|
4550
|
-
if (eventsBackend) {
|
|
4551
|
-
yaml += ` - EVENT_PUBLISHER_CONNECTION_STRING=\${EVENT_PUBLISHER_CONNECTION_STRING}
|
|
4552
|
-
- EVENT_SUBSCRIBER_CONNECTION_STRING=\${EVENT_SUBSCRIBER_CONNECTION_STRING}
|
|
4553
|
-
`;
|
|
4554
|
-
if (eventsBackend === "sns") yaml += ` - AWS_ACCESS_KEY_ID=\${AWS_ACCESS_KEY_ID:-localstack}
|
|
4555
|
-
- AWS_SECRET_ACCESS_KEY=\${AWS_SECRET_ACCESS_KEY:-localstack}
|
|
4556
|
-
- AWS_REGION=\${AWS_REGION:-us-east-1}
|
|
4557
|
-
- AWS_ENDPOINT_URL=\${AWS_ENDPOINT_URL:-http://localstack:4566}
|
|
4558
|
-
`;
|
|
4559
|
-
}
|
|
4560
|
-
}
|
|
4561
|
-
yaml += ` healthcheck:
|
|
4562
|
-
test: ${healthCheckCmd}
|
|
4563
|
-
interval: 30s
|
|
4564
|
-
timeout: 3s
|
|
4565
|
-
retries: 3
|
|
4566
|
-
`;
|
|
4567
|
-
const dependencies$1 = [...app.dependencies];
|
|
4568
|
-
if (app.type === "backend") {
|
|
4569
|
-
if (hasPostgres) dependencies$1.push("postgres");
|
|
4570
|
-
if (hasRedis) dependencies$1.push("redis");
|
|
4571
|
-
if (hasMinio) dependencies$1.push("minio");
|
|
4572
|
-
if (hasMail) dependencies$1.push("mailpit");
|
|
4573
|
-
if (eventsBackend === "sns") dependencies$1.push("localstack");
|
|
4574
|
-
if (eventsBackend === "rabbitmq") dependencies$1.push("rabbitmq");
|
|
4575
|
-
}
|
|
4576
|
-
if (dependencies$1.length > 0) {
|
|
4577
|
-
yaml += ` depends_on:
|
|
4578
|
-
`;
|
|
4579
|
-
for (const dep of dependencies$1) yaml += ` ${dep}:
|
|
4580
|
-
condition: service_healthy
|
|
4581
|
-
`;
|
|
4582
|
-
}
|
|
4583
|
-
yaml += ` networks:
|
|
4584
|
-
- workspace-network
|
|
4585
|
-
`;
|
|
4586
|
-
return yaml;
|
|
4587
|
-
}
|
|
4588
|
-
|
|
4589
3953
|
//#endregion
|
|
4590
3954
|
//#region src/docker/templates.ts
|
|
4591
3955
|
const LOCKFILES = [
|
|
@@ -5016,8 +4380,7 @@ function resolveDockerConfig(config) {
|
|
|
5016
4380
|
registry: docker.registry ?? "",
|
|
5017
4381
|
imageName: docker.imageName ?? defaultImageName,
|
|
5018
4382
|
baseImage: docker.baseImage ?? "node:22-alpine",
|
|
5019
|
-
port: docker.port ?? 3e3
|
|
5020
|
-
compose: docker.compose
|
|
4383
|
+
port: docker.port ?? 3e3
|
|
5021
4384
|
};
|
|
5022
4385
|
}
|
|
5023
4386
|
/**
|
|
@@ -5459,7 +4822,7 @@ async function dockerCommand(options) {
|
|
|
5459
4822
|
const loadedConfig = await loadWorkspaceConfig();
|
|
5460
4823
|
if (loadedConfig.type === "workspace") {
|
|
5461
4824
|
logger$5.log("📦 Detected workspace configuration");
|
|
5462
|
-
return workspaceDockerCommand(loadedConfig.workspace
|
|
4825
|
+
return workspaceDockerCommand(loadedConfig.workspace);
|
|
5463
4826
|
}
|
|
5464
4827
|
const config = await loadConfig();
|
|
5465
4828
|
const dockerConfig = resolveDockerConfig(config);
|
|
@@ -5504,18 +4867,7 @@ async function dockerCommand(options) {
|
|
|
5504
4867
|
const dockerfilePath = join(dockerDir, "Dockerfile");
|
|
5505
4868
|
await writeFile(dockerfilePath, dockerfile);
|
|
5506
4869
|
logger$5.log(`Generated: .gkm/docker/Dockerfile (${dockerMode}, ${packageManager})`);
|
|
5507
|
-
const
|
|
5508
|
-
imageName: dockerConfig.imageName,
|
|
5509
|
-
registry: options.registry ?? dockerConfig.registry,
|
|
5510
|
-
port: dockerConfig.port,
|
|
5511
|
-
healthCheckPath,
|
|
5512
|
-
services: dockerConfig.compose?.services ?? {}
|
|
5513
|
-
};
|
|
5514
|
-
const hasServices = Array.isArray(composeOptions.services) ? composeOptions.services.length > 0 : Object.keys(composeOptions.services).length > 0;
|
|
5515
|
-
const dockerCompose = hasServices ? generateDockerCompose(composeOptions) : generateMinimalDockerCompose(composeOptions);
|
|
5516
|
-
const composePath = join(dockerDir, "docker-compose.yml");
|
|
5517
|
-
await writeFile(composePath, dockerCompose);
|
|
5518
|
-
logger$5.log("Generated: .gkm/docker/docker-compose.yml");
|
|
4870
|
+
const composePath = await writeConstructsCompose(loadedConfig.workspace);
|
|
5519
4871
|
const dockerignore = generateDockerignore();
|
|
5520
4872
|
const dockerignorePath = join(process.cwd(), ".dockerignore");
|
|
5521
4873
|
await writeFile(dockerignorePath, dockerignore);
|
|
@@ -5620,7 +4972,7 @@ function getAppPackageName(appPath) {
|
|
|
5620
4972
|
* Generate Dockerfiles for all apps in a workspace.
|
|
5621
4973
|
* @internal Exported for testing
|
|
5622
4974
|
*/
|
|
5623
|
-
async function workspaceDockerCommand(workspace
|
|
4975
|
+
async function workspaceDockerCommand(workspace) {
|
|
5624
4976
|
const results = [];
|
|
5625
4977
|
const apps = Object.entries(workspace.apps);
|
|
5626
4978
|
logger$5.log(`\n🐳 Generating Dockerfiles for workspace: ${workspace.name}`);
|
|
@@ -5695,27 +5047,41 @@ async function workspaceDockerCommand(workspace, options) {
|
|
|
5695
5047
|
const dockerignorePath = join(workspace.root, ".dockerignore");
|
|
5696
5048
|
await writeFile(dockerignorePath, dockerignore);
|
|
5697
5049
|
logger$5.log(`\n Generated: .dockerignore (workspace root)`);
|
|
5698
|
-
const
|
|
5699
|
-
|
|
5700
|
-
containers: await derivedContainers(workspace, workspace.stages.local)
|
|
5701
|
-
});
|
|
5702
|
-
const composePath = join(dockerDir, "docker-compose.yml");
|
|
5703
|
-
await writeFile(composePath, dockerCompose);
|
|
5704
|
-
logger$5.log(` Generated: .gkm/docker/docker-compose.yml`);
|
|
5705
|
-
logger$5.log(`\n✅ Generated ${results.length} Dockerfile(s) + docker-compose.yml`);
|
|
5050
|
+
const composePath = await writeConstructsCompose(workspace);
|
|
5051
|
+
logger$5.log(`\n✅ Generated ${results.length} Dockerfile(s) + ${COMPOSE_PATH}`);
|
|
5706
5052
|
logger$5.log("\n📋 Build commands:");
|
|
5707
5053
|
for (const result of results) {
|
|
5708
5054
|
const icon = result.type === "backend" ? "⚙️" : result.type === "mobile" ? "📱" : "🌐";
|
|
5709
5055
|
logger$5.log(` ${icon} docker build -f .gkm/docker/Dockerfile.${result.appName} -t ${result.imageName} .`);
|
|
5710
5056
|
}
|
|
5711
|
-
|
|
5712
|
-
logger$5.log(" docker compose -f .gkm/docker/docker-compose.yml up --build");
|
|
5057
|
+
printRunInstructions(workspace);
|
|
5713
5058
|
return {
|
|
5714
5059
|
apps: results,
|
|
5715
5060
|
dockerCompose: composePath,
|
|
5716
5061
|
dockerignore: dockerignorePath
|
|
5717
5062
|
};
|
|
5718
5063
|
}
|
|
5064
|
+
/**
|
|
5065
|
+
* Write `docker-compose.constructs.yml` — the same file `gkm dev` writes, with
|
|
5066
|
+
* the apps beside the containers their constructs derive.
|
|
5067
|
+
*
|
|
5068
|
+
* Through reconcile rather than a second generator, so what `gkm docker`
|
|
5069
|
+
* describes and what `gkm dev` runs cannot disagree. Nothing is started.
|
|
5070
|
+
*/
|
|
5071
|
+
async function writeConstructsCompose(workspace) {
|
|
5072
|
+
await reconcileWorkspace(workspace, {
|
|
5073
|
+
stage: workspace.stages.local,
|
|
5074
|
+
start: false
|
|
5075
|
+
});
|
|
5076
|
+
logger$5.log(` Generated: ${COMPOSE_PATH} (project root)`);
|
|
5077
|
+
return join(workspace.root, COMPOSE_PATH);
|
|
5078
|
+
}
|
|
5079
|
+
function printRunInstructions(workspace) {
|
|
5080
|
+
const project = existsSync(join(workspace.root, "docker-compose.yml")) ? " -f docker-compose.yml" : "";
|
|
5081
|
+
logger$5.log("\n📋 Run everything (the apps sit behind the \"apps\" profile):");
|
|
5082
|
+
logger$5.log(" gkm setup # starts the containers and creates what they hold");
|
|
5083
|
+
logger$5.log(` docker compose -f ${COMPOSE_PATH}${project} --profile apps up --build`);
|
|
5084
|
+
}
|
|
5719
5085
|
|
|
5720
5086
|
//#endregion
|
|
5721
5087
|
//#region src/deploy/docker.ts
|
|
@@ -5923,20 +5289,20 @@ function isStateProvider(value) {
|
|
|
5923
5289
|
async function createStateProvider(options) {
|
|
5924
5290
|
const { config, workspaceRoot, workspaceName } = options;
|
|
5925
5291
|
if (!config) {
|
|
5926
|
-
const { LocalStateProvider } = await import("./LocalStateProvider-
|
|
5292
|
+
const { LocalStateProvider } = await import("./LocalStateProvider-Bq-0C0sg.mjs");
|
|
5927
5293
|
return new LocalStateProvider(workspaceRoot);
|
|
5928
5294
|
}
|
|
5929
5295
|
if (isStateProvider(config.provider)) return config.provider;
|
|
5930
5296
|
const provider = config.provider;
|
|
5931
5297
|
if (provider === "local") {
|
|
5932
|
-
const { LocalStateProvider } = await import("./LocalStateProvider-
|
|
5298
|
+
const { LocalStateProvider } = await import("./LocalStateProvider-Bq-0C0sg.mjs");
|
|
5933
5299
|
return new LocalStateProvider(workspaceRoot);
|
|
5934
5300
|
}
|
|
5935
5301
|
if (provider === "ssm") {
|
|
5936
5302
|
if (!workspaceName) throw new Error("Workspace name is required for SSM state provider. Set \"name\" in gkm.config.ts.");
|
|
5937
|
-
const { LocalStateProvider } = await import("./LocalStateProvider-
|
|
5938
|
-
const { SSMStateProvider } = await import("./SSMStateProvider-
|
|
5939
|
-
const { CachedStateProvider: CachedStateProvider$1 } = await import("./CachedStateProvider-
|
|
5303
|
+
const { LocalStateProvider } = await import("./LocalStateProvider-Bq-0C0sg.mjs");
|
|
5304
|
+
const { SSMStateProvider } = await import("./SSMStateProvider-DOZifZfE.mjs");
|
|
5305
|
+
const { CachedStateProvider: CachedStateProvider$1 } = await import("./CachedStateProvider-C-8wtlto.mjs");
|
|
5940
5306
|
const ssmConfig = config;
|
|
5941
5307
|
const local = new LocalStateProvider(workspaceRoot);
|
|
5942
5308
|
const ssm = SSMStateProvider.create({
|
|
@@ -6812,7 +6178,7 @@ async function workspaceDeployCommand(configured, options) {
|
|
|
6812
6178
|
const backupCluster = Object.values(declaredClusters)[0];
|
|
6813
6179
|
if (workspace.deploy?.backups && backupCluster) {
|
|
6814
6180
|
logger$3.log("\n💾 Provisioning backup destination...");
|
|
6815
|
-
const { provisionBackupDestination } = await import("./backup-provisioner-
|
|
6181
|
+
const { provisionBackupDestination } = await import("./backup-provisioner-DXXg6FIL.mjs");
|
|
6816
6182
|
const backupState = await provisionBackupDestination({
|
|
6817
6183
|
api,
|
|
6818
6184
|
projectId: project.projectId,
|
|
@@ -7697,7 +7063,8 @@ the stale one is always the copy nobody opened.
|
|
|
7697
7063
|
`;
|
|
7698
7064
|
}
|
|
7699
7065
|
function agentsContent(options, template) {
|
|
7700
|
-
const { name: name$1, monorepo,
|
|
7066
|
+
const { name: name$1, monorepo, telescope, studio, packageManager } = options;
|
|
7067
|
+
const { database } = options.constructs;
|
|
7701
7068
|
const constructsGlob = monorepo ? WORKSPACE_CONSTRUCTS_GLOB : CONSTRUCTS_GLOB;
|
|
7702
7069
|
const isWorker = template.name === "worker";
|
|
7703
7070
|
const isServerless = template.name === "serverless";
|
|
@@ -7763,7 +7130,7 @@ ${run} gkm secrets:show
|
|
|
7763
7130
|
environment and fail in a way that looks like a config bug. Go through
|
|
7764
7131
|
\`gkm exec --\`, which is what injects them.`);
|
|
7765
7132
|
if (!isWorker) sections.push(endpointSection(options));
|
|
7766
|
-
if (isWorker || isServerless
|
|
7133
|
+
if (isWorker || isServerless) sections.push(backgroundSection(template));
|
|
7767
7134
|
if (database) sections.push(databaseSection());
|
|
7768
7135
|
sections.push(`## Environment
|
|
7769
7136
|
|
|
@@ -7811,7 +7178,8 @@ reaches — and neither shows up until something reads the graph.`);
|
|
|
7811
7178
|
return `${sections.join("\n\n")}\n`;
|
|
7812
7179
|
}
|
|
7813
7180
|
function layoutBlock(options, template) {
|
|
7814
|
-
const { monorepo, apiPath
|
|
7181
|
+
const { monorepo, apiPath } = options;
|
|
7182
|
+
const { database } = options.constructs;
|
|
7815
7183
|
const isWorker = template.name === "worker";
|
|
7816
7184
|
if (monorepo) return `constructs/ # every construct: the surface, the database, topics
|
|
7817
7185
|
${apiPath}/ # the API app
|
|
@@ -7827,7 +7195,7 @@ gkm.config.ts # name, constructs glob, secrets`;
|
|
|
7827
7195
|
return lines.join("\n");
|
|
7828
7196
|
}
|
|
7829
7197
|
function endpointSection(options) {
|
|
7830
|
-
const { database } = options;
|
|
7198
|
+
const { database } = options.constructs;
|
|
7831
7199
|
return `## Adding an endpoint
|
|
7832
7200
|
|
|
7833
7201
|
An endpoint is built **from the surface that serves it**. That is where its
|
|
@@ -7983,26 +7351,110 @@ Run tests through \`gkm test\`, not \`vitest\` directly${database ? " — it is
|
|
|
7983
7351
|
//#endregion
|
|
7984
7352
|
//#region src/init/dependencies.ts
|
|
7985
7353
|
/**
|
|
7986
|
-
*
|
|
7354
|
+
* Every third-party range `gkm init` writes, in one place.
|
|
7987
7355
|
*
|
|
7988
|
-
*
|
|
7989
|
-
*
|
|
7990
|
-
*
|
|
7991
|
-
*
|
|
7356
|
+
* Templates used to pin their own copies, and those drifted: below the peer
|
|
7357
|
+
* ranges the `@geekmidas` packages declare (`kysely ~0.28` against a `~0.29.6`
|
|
7358
|
+
* peer is two copies of Kysely, and a `Kysely<DB>` from one is not assignable
|
|
7359
|
+
* to the other's), and below what the rest of the scaffold assumed (`turbo
|
|
7360
|
+
* ~2.3.0` ignores `$TURBO_ROOT$`, which the root `turbo.json` relies on). No
|
|
7361
|
+
* generator writes a version literal; a scan test holds them to that.
|
|
7992
7362
|
*
|
|
7993
7363
|
* Hand-maintained, unlike `versions.ts`, which `sync-versions` rewrites whole.
|
|
7364
|
+
* Current as of 2026-09-27, inside every `@geekmidas` peer range.
|
|
7994
7365
|
*/
|
|
7995
7366
|
const DEPENDENCY_VERSIONS = {
|
|
7996
|
-
"
|
|
7997
|
-
|
|
7998
|
-
hono: "~
|
|
7367
|
+
"better-auth": "~1.7.6",
|
|
7368
|
+
hono: "~4.13.9",
|
|
7369
|
+
"@hono/node-server": "~1.19.17",
|
|
7999
7370
|
kysely: "~0.29.6",
|
|
8000
7371
|
"kysely-ctl": "~0.21.0",
|
|
8001
7372
|
pg: "~8.23.0",
|
|
8002
7373
|
pino: "~10.3.1",
|
|
7374
|
+
zod: "~4.6.5",
|
|
8003
7375
|
sst: "~4.17.1",
|
|
8004
|
-
|
|
7376
|
+
next: "~16.3.6",
|
|
7377
|
+
react: "~19.3.0",
|
|
7378
|
+
"react-dom": "~19.3.0",
|
|
7379
|
+
"@tanstack/react-query": "~5.104.0",
|
|
7380
|
+
"@tanstack/react-router": "~1.170.40",
|
|
7381
|
+
"@tanstack/react-start": "~1.168.59",
|
|
7382
|
+
"@tanstack/router-plugin": "~1.168.41",
|
|
7383
|
+
tailwindcss: "~4.3.3",
|
|
7384
|
+
"@tailwindcss/postcss": "~4.3.3",
|
|
7385
|
+
"@tailwindcss/vite": "~4.3.3",
|
|
7386
|
+
"@radix-ui/react-dialog": "~1.1.23",
|
|
7387
|
+
"@radix-ui/react-label": "~2.1.15",
|
|
7388
|
+
"@radix-ui/react-separator": "~1.1.15",
|
|
7389
|
+
"@radix-ui/react-slot": "~1.3.3",
|
|
7390
|
+
"@radix-ui/react-tabs": "~1.1.21",
|
|
7391
|
+
"@radix-ui/react-tooltip": "~1.2.16",
|
|
7392
|
+
"class-variance-authority": "~0.7.1",
|
|
7393
|
+
clsx: "~2.1.1",
|
|
7394
|
+
"lucide-react": "~0.577.0",
|
|
7395
|
+
"tailwind-merge": "~3.7.0",
|
|
7396
|
+
"vite-tsconfig-paths": "~5.1.4",
|
|
7397
|
+
"@biomejs/biome": "~2.5.14",
|
|
7398
|
+
"@faker-js/faker": "~10.6.0",
|
|
7399
|
+
turbo: "~2.11.4",
|
|
7400
|
+
"@types/node": "~22.20.4",
|
|
7401
|
+
"@types/pg": "~8.23.1",
|
|
7402
|
+
"@types/react": "~19.3.0",
|
|
7403
|
+
"@types/react-dom": "~19.3.0",
|
|
7404
|
+
"@types/aws-lambda": "~8.10.163"
|
|
7405
|
+
};
|
|
7406
|
+
/**
|
|
7407
|
+
* The build and test toolchain, at the versions it was already on.
|
|
7408
|
+
*
|
|
7409
|
+
* Held here so it moves in one place, but not moved yet: it changes how every
|
|
7410
|
+
* scaffold compiles and runs, and tranche 2 (#40) moves it together with the
|
|
7411
|
+
* repository's own toolchain.
|
|
7412
|
+
*/
|
|
7413
|
+
const TOOLCHAIN_VERSIONS = {
|
|
7414
|
+
typescript: "~5.8.2",
|
|
7415
|
+
tsx: "~4.20.0",
|
|
7416
|
+
esbuild: "~0.27.0",
|
|
7417
|
+
vitest: "~4.0.0",
|
|
7418
|
+
vite: "^7.0.0",
|
|
7419
|
+
"@vitejs/plugin-react": "^4.3.4",
|
|
7420
|
+
storybook: "^8.4.7",
|
|
7421
|
+
"@storybook/addon-a11y": "^8.4.7",
|
|
7422
|
+
"@storybook/addon-essentials": "^8.4.7",
|
|
7423
|
+
"@storybook/addon-interactions": "^8.4.7",
|
|
7424
|
+
"@storybook/react": "^8.4.7",
|
|
7425
|
+
"@storybook/react-vite": "^8.4.7",
|
|
7426
|
+
"vite@storybook": "^6.0.0"
|
|
7427
|
+
};
|
|
7428
|
+
/**
|
|
7429
|
+
* The Expo app's packages, which track an Expo SDK release (55) rather than
|
|
7430
|
+
* their own latest: SDK 55 is tested against exactly these, React Native and
|
|
7431
|
+
* React included, so they move when the SDK does.
|
|
7432
|
+
*/
|
|
7433
|
+
const EXPO_VERSIONS = {
|
|
7434
|
+
expo: "~55.0.0",
|
|
7435
|
+
"expo-constants": "~55.0.0",
|
|
7436
|
+
"expo-dev-client": "~55.0.0",
|
|
7437
|
+
"expo-linking": "~55.0.0",
|
|
7438
|
+
"expo-router": "~55.0.0",
|
|
7439
|
+
"expo-splash-screen": "~55.0.0",
|
|
7440
|
+
"expo-status-bar": "~55.0.0",
|
|
7441
|
+
"expo-secure-store": "~14.2.0",
|
|
7442
|
+
react: "19.2.0",
|
|
7443
|
+
"react-dom": "19.2.0",
|
|
7444
|
+
"react-native": "0.83.6",
|
|
7445
|
+
"react-native-gesture-handler": "~2.30.0",
|
|
7446
|
+
"react-native-reanimated": "~4.2.0",
|
|
7447
|
+
"react-native-safe-area-context": "~5.6.0",
|
|
7448
|
+
"react-native-screens": "~4.23.0",
|
|
7449
|
+
"react-native-web": "~0.21.0",
|
|
7450
|
+
"@react-navigation/native": "^7.1.0",
|
|
7451
|
+
"@babel/core": "^7.25.0",
|
|
7452
|
+
"@types/react": "~19.0.0",
|
|
7453
|
+
nativewind: "~4.2.0",
|
|
7454
|
+
tailwindcss: "~3.4.0"
|
|
8005
7455
|
};
|
|
7456
|
+
/** The Biome config schema matching the Biome the scaffold installs. */
|
|
7457
|
+
const BIOME_SCHEMA = `https://biomejs.dev/schemas/${DEPENDENCY_VERSIONS["@biomejs/biome"].replace(/^[~^]/, "")}/schema.json`;
|
|
8006
7458
|
|
|
8007
7459
|
//#endregion
|
|
8008
7460
|
//#region src/init/versions.ts
|
|
@@ -8026,26 +7478,26 @@ const CLI_VERSION = `~${pkg.version}`;
|
|
|
8026
7478
|
* Run: pnpm --filter @geekmidas/cli sync-versions
|
|
8027
7479
|
*/
|
|
8028
7480
|
const GEEKMIDAS_VERSIONS = {
|
|
8029
|
-
"@geekmidas/audit": "~10.0.0-alpha.
|
|
8030
|
-
"@geekmidas/auth": "~10.0.0-alpha.
|
|
8031
|
-
"@geekmidas/cache": "~10.0.0-alpha.
|
|
8032
|
-
"@geekmidas/client": "~10.0.0-alpha.
|
|
8033
|
-
"@geekmidas/cloud": "~10.0.0-alpha.
|
|
8034
|
-
"@geekmidas/constructs": "~10.0.0-alpha.
|
|
8035
|
-
"@geekmidas/db": "~10.0.0-alpha.
|
|
8036
|
-
"@geekmidas/emailkit": "~10.0.0-alpha.
|
|
8037
|
-
"@geekmidas/envkit": "~10.0.0-alpha.
|
|
8038
|
-
"@geekmidas/errors": "~10.0.0-alpha.
|
|
8039
|
-
"@geekmidas/events": "~10.0.0-alpha.
|
|
8040
|
-
"@geekmidas/logger": "~10.0.0-alpha.
|
|
8041
|
-
"@geekmidas/manifest": "~10.0.0-alpha.
|
|
8042
|
-
"@geekmidas/rate-limit": "~10.0.0-alpha.
|
|
8043
|
-
"@geekmidas/schema": "~10.0.0-alpha.
|
|
8044
|
-
"@geekmidas/services": "~10.0.0-alpha.
|
|
8045
|
-
"@geekmidas/storage": "~10.0.0-alpha.
|
|
8046
|
-
"@geekmidas/studio": "~10.0.0-alpha.
|
|
8047
|
-
"@geekmidas/telescope": "~10.0.0-alpha.
|
|
8048
|
-
"@geekmidas/testkit": "~10.0.0-alpha.
|
|
7481
|
+
"@geekmidas/audit": "~10.0.0-alpha.12",
|
|
7482
|
+
"@geekmidas/auth": "~10.0.0-alpha.12",
|
|
7483
|
+
"@geekmidas/cache": "~10.0.0-alpha.12",
|
|
7484
|
+
"@geekmidas/client": "~10.0.0-alpha.12",
|
|
7485
|
+
"@geekmidas/cloud": "~10.0.0-alpha.12",
|
|
7486
|
+
"@geekmidas/constructs": "~10.0.0-alpha.12",
|
|
7487
|
+
"@geekmidas/db": "~10.0.0-alpha.12",
|
|
7488
|
+
"@geekmidas/emailkit": "~10.0.0-alpha.12",
|
|
7489
|
+
"@geekmidas/envkit": "~10.0.0-alpha.12",
|
|
7490
|
+
"@geekmidas/errors": "~10.0.0-alpha.12",
|
|
7491
|
+
"@geekmidas/events": "~10.0.0-alpha.12",
|
|
7492
|
+
"@geekmidas/logger": "~10.0.0-alpha.12",
|
|
7493
|
+
"@geekmidas/manifest": "~10.0.0-alpha.12",
|
|
7494
|
+
"@geekmidas/rate-limit": "~10.0.0-alpha.12",
|
|
7495
|
+
"@geekmidas/schema": "~10.0.0-alpha.12",
|
|
7496
|
+
"@geekmidas/services": "~10.0.0-alpha.12",
|
|
7497
|
+
"@geekmidas/storage": "~10.0.0-alpha.12",
|
|
7498
|
+
"@geekmidas/studio": "~10.0.0-alpha.12",
|
|
7499
|
+
"@geekmidas/telescope": "~10.0.0-alpha.12",
|
|
7500
|
+
"@geekmidas/testkit": "~10.0.0-alpha.12",
|
|
8049
7501
|
"@geekmidas/cli": CLI_VERSION
|
|
8050
7502
|
};
|
|
8051
7503
|
|
|
@@ -8077,7 +7529,7 @@ function generateAuthAppFiles(options) {
|
|
|
8077
7529
|
"@geekmidas/db": GEEKMIDAS_VERSIONS["@geekmidas/db"],
|
|
8078
7530
|
"@geekmidas/envkit": GEEKMIDAS_VERSIONS["@geekmidas/envkit"],
|
|
8079
7531
|
"@geekmidas/logger": GEEKMIDAS_VERSIONS["@geekmidas/logger"],
|
|
8080
|
-
"@hono/node-server": "
|
|
7532
|
+
"@hono/node-server": DEPENDENCY_VERSIONS["@hono/node-server"],
|
|
8081
7533
|
"better-auth": DEPENDENCY_VERSIONS["better-auth"],
|
|
8082
7534
|
hono: DEPENDENCY_VERSIONS.hono,
|
|
8083
7535
|
kysely: DEPENDENCY_VERSIONS.kysely,
|
|
@@ -8085,10 +7537,10 @@ function generateAuthAppFiles(options) {
|
|
|
8085
7537
|
},
|
|
8086
7538
|
devDependencies: {
|
|
8087
7539
|
"@geekmidas/cli": GEEKMIDAS_VERSIONS["@geekmidas/cli"],
|
|
8088
|
-
"@types/node": "
|
|
8089
|
-
"@types/pg": "
|
|
8090
|
-
tsx: "
|
|
8091
|
-
typescript: "
|
|
7540
|
+
"@types/node": DEPENDENCY_VERSIONS["@types/node"],
|
|
7541
|
+
"@types/pg": DEPENDENCY_VERSIONS["@types/pg"],
|
|
7542
|
+
tsx: TOOLCHAIN_VERSIONS["tsx"],
|
|
7543
|
+
typescript: TOOLCHAIN_VERSIONS["typescript"]
|
|
8092
7544
|
}
|
|
8093
7545
|
};
|
|
8094
7546
|
const tsConfig = {
|
|
@@ -8195,21 +7647,6 @@ export default defineConfig({${stagesBlock(options.stages)}${options.monorepo ?
|
|
|
8195
7647
|
logger: './src/config/logger#logger',`;
|
|
8196
7648
|
if (isServerless || hasWorker) gkmConfig += `
|
|
8197
7649
|
functions: './src/functions/**/*.ts',`;
|
|
8198
|
-
if (!options.monorepo) {
|
|
8199
|
-
const { cache, mail, events } = options.services;
|
|
8200
|
-
if (cache || mail || events) {
|
|
8201
|
-
gkmConfig += `
|
|
8202
|
-
services: {`;
|
|
8203
|
-
if (cache) gkmConfig += `
|
|
8204
|
-
cache: true,`;
|
|
8205
|
-
if (mail) gkmConfig += `
|
|
8206
|
-
mail: true,`;
|
|
8207
|
-
if (events) gkmConfig += `
|
|
8208
|
-
events: '${events}',`;
|
|
8209
|
-
gkmConfig += `
|
|
8210
|
-
},`;
|
|
8211
|
-
}
|
|
8212
|
-
}
|
|
8213
7650
|
if (hasWorker) gkmConfig += `
|
|
8214
7651
|
crons: './src/crons/**/*.ts',
|
|
8215
7652
|
subscribers: './src/subscribers/**/*.ts',`;
|
|
@@ -8254,14 +7691,14 @@ export default defineConfig({${stagesBlock(options.stages)}${options.monorepo ?
|
|
|
8254
7691
|
path: "tsconfig.json",
|
|
8255
7692
|
content: `${JSON.stringify(tsConfig, null, 2)}\n`
|
|
8256
7693
|
}];
|
|
8257
|
-
if (options.database) files$2.push({
|
|
7694
|
+
if (options.constructs.database) files$2.push({
|
|
8258
7695
|
path: "vitest.config.ts",
|
|
8259
7696
|
content: vitestConfigContent
|
|
8260
7697
|
});
|
|
8261
7698
|
return files$2;
|
|
8262
7699
|
}
|
|
8263
7700
|
const biomeConfig = {
|
|
8264
|
-
$schema:
|
|
7701
|
+
$schema: BIOME_SCHEMA,
|
|
8265
7702
|
vcs: {
|
|
8266
7703
|
enabled: true,
|
|
8267
7704
|
clientKind: "git",
|
|
@@ -8345,7 +7782,7 @@ export default defineConfig({${stagesBlock(options.stages)}${options.monorepo ?
|
|
|
8345
7782
|
content: `${JSON.stringify(turboConfig, null, 2)}\n`
|
|
8346
7783
|
}
|
|
8347
7784
|
];
|
|
8348
|
-
if (options.database) files$1.push({
|
|
7785
|
+
if (options.constructs.database) files$1.push({
|
|
8349
7786
|
path: "vitest.config.ts",
|
|
8350
7787
|
content: vitestConfigContent
|
|
8351
7788
|
});
|
|
@@ -8382,325 +7819,13 @@ function generateSingleAppConfigFiles(options, _template, _helpers) {
|
|
|
8382
7819
|
path: "tsconfig.json",
|
|
8383
7820
|
content: `${JSON.stringify(tsConfig, null, 2)}\n`
|
|
8384
7821
|
}];
|
|
8385
|
-
if (options.database) files$1.push({
|
|
7822
|
+
if (options.constructs.database) files$1.push({
|
|
8386
7823
|
path: "vitest.config.ts",
|
|
8387
7824
|
content: vitestConfigContent
|
|
8388
7825
|
});
|
|
8389
7826
|
return files$1;
|
|
8390
7827
|
}
|
|
8391
7828
|
|
|
8392
|
-
//#endregion
|
|
8393
|
-
//#region src/init/generators/docker.ts
|
|
8394
|
-
/**
|
|
8395
|
-
* Generate docker-compose.yml based on template and options
|
|
8396
|
-
*/
|
|
8397
|
-
function generateDockerFiles(options, template, dbApps) {
|
|
8398
|
-
const { database } = options;
|
|
8399
|
-
const isServerless = template.name === "serverless";
|
|
8400
|
-
const hasWorker = template.name === "worker";
|
|
8401
|
-
const isFullstack = options.template === "fullstack";
|
|
8402
|
-
const services = [];
|
|
8403
|
-
const volumes = [];
|
|
8404
|
-
const files$1 = [];
|
|
8405
|
-
if (database) {
|
|
8406
|
-
const initVolume = isFullstack && dbApps?.length ? `
|
|
8407
|
-
- ./docker/postgres/init.sh:/docker-entrypoint-initdb.d/init.sh:ro` : "";
|
|
8408
|
-
const envFile = isFullstack && dbApps?.length ? `
|
|
8409
|
-
env_file:
|
|
8410
|
-
- ./docker/.env` : "";
|
|
8411
|
-
services.push(` postgres:
|
|
8412
|
-
image: postgres:18-alpine
|
|
8413
|
-
container_name: ${options.name}-postgres
|
|
8414
|
-
restart: unless-stopped${envFile}
|
|
8415
|
-
environment:
|
|
8416
|
-
POSTGRES_USER: \${POSTGRES_USER:-postgres}
|
|
8417
|
-
POSTGRES_PASSWORD: \${POSTGRES_PASSWORD:-postgres}
|
|
8418
|
-
POSTGRES_DB: \${POSTGRES_DB:-${options.name.replace(/-/g, "_")}_dev}
|
|
8419
|
-
ports:
|
|
8420
|
-
- '\${POSTGRES_HOST_PORT:-5432}:5432'
|
|
8421
|
-
volumes:
|
|
8422
|
-
- dbdata:/var/lib/postgresql${initVolume}
|
|
8423
|
-
healthcheck:
|
|
8424
|
-
test: ['CMD-SHELL', 'pg_isready -U $$POSTGRES_USER']
|
|
8425
|
-
interval: 5s
|
|
8426
|
-
timeout: 5s
|
|
8427
|
-
retries: 5`);
|
|
8428
|
-
volumes.push(" dbdata:");
|
|
8429
|
-
if (isFullstack && dbApps?.length) {
|
|
8430
|
-
files$1.push({
|
|
8431
|
-
path: "docker/postgres/init.sh",
|
|
8432
|
-
content: generatePostgresInitScript(dbApps, options.services?.events)
|
|
8433
|
-
});
|
|
8434
|
-
files$1.push({
|
|
8435
|
-
path: "docker/.env",
|
|
8436
|
-
content: generateDockerEnv(dbApps, options.services?.events)
|
|
8437
|
-
});
|
|
8438
|
-
}
|
|
8439
|
-
}
|
|
8440
|
-
if (isServerless) {
|
|
8441
|
-
services.push(` redis:
|
|
8442
|
-
image: redis:7-alpine
|
|
8443
|
-
container_name: ${options.name}-redis
|
|
8444
|
-
restart: unless-stopped
|
|
8445
|
-
ports:
|
|
8446
|
-
- '\${REDIS_HOST_PORT:-6379}:6379'
|
|
8447
|
-
volumes:
|
|
8448
|
-
- redis_data:/data
|
|
8449
|
-
healthcheck:
|
|
8450
|
-
test: ['CMD', 'redis-cli', 'ping']
|
|
8451
|
-
interval: 5s
|
|
8452
|
-
timeout: 5s
|
|
8453
|
-
retries: 5
|
|
8454
|
-
|
|
8455
|
-
serverless-redis:
|
|
8456
|
-
image: hiett/serverless-redis-http:latest
|
|
8457
|
-
container_name: ${options.name}-serverless-redis
|
|
8458
|
-
restart: unless-stopped
|
|
8459
|
-
ports:
|
|
8460
|
-
- '\${SRH_HOST_PORT:-8079}:80'
|
|
8461
|
-
environment:
|
|
8462
|
-
SRH_MODE: env
|
|
8463
|
-
SRH_TOKEN: local_dev_token
|
|
8464
|
-
SRH_CONNECTION_STRING: redis://redis:6379
|
|
8465
|
-
depends_on:
|
|
8466
|
-
redis:
|
|
8467
|
-
condition: service_healthy`);
|
|
8468
|
-
volumes.push(" redis_data:");
|
|
8469
|
-
} else {
|
|
8470
|
-
services.push(` redis:
|
|
8471
|
-
image: redis:7-alpine
|
|
8472
|
-
container_name: ${options.name}-redis
|
|
8473
|
-
restart: unless-stopped
|
|
8474
|
-
ports:
|
|
8475
|
-
- '\${REDIS_HOST_PORT:-6379}:6379'
|
|
8476
|
-
volumes:
|
|
8477
|
-
- redis_data:/data
|
|
8478
|
-
healthcheck:
|
|
8479
|
-
test: ['CMD', 'redis-cli', 'ping']
|
|
8480
|
-
interval: 5s
|
|
8481
|
-
timeout: 5s
|
|
8482
|
-
retries: 5`);
|
|
8483
|
-
volumes.push(" redis_data:");
|
|
8484
|
-
}
|
|
8485
|
-
if (hasWorker) {
|
|
8486
|
-
services.push(` rabbitmq:
|
|
8487
|
-
image: rabbitmq:3-management-alpine
|
|
8488
|
-
container_name: ${options.name}-rabbitmq
|
|
8489
|
-
restart: unless-stopped
|
|
8490
|
-
ports:
|
|
8491
|
-
- '\${RABBITMQ_HOST_PORT:-5672}:5672'
|
|
8492
|
-
- '\${RABBITMQ_MGMT_HOST_PORT:-15672}:15672'
|
|
8493
|
-
environment:
|
|
8494
|
-
RABBITMQ_DEFAULT_USER: guest
|
|
8495
|
-
RABBITMQ_DEFAULT_PASS: guest
|
|
8496
|
-
volumes:
|
|
8497
|
-
- rabbitmq_data:/var/lib/rabbitmq
|
|
8498
|
-
healthcheck:
|
|
8499
|
-
test: ['CMD', 'rabbitmq-diagnostics', 'check_running']
|
|
8500
|
-
interval: 10s
|
|
8501
|
-
timeout: 5s
|
|
8502
|
-
retries: 5`);
|
|
8503
|
-
volumes.push(" rabbitmq_data:");
|
|
8504
|
-
}
|
|
8505
|
-
if (options.services?.mail) services.push(` mailpit:
|
|
8506
|
-
image: axllent/mailpit:latest
|
|
8507
|
-
container_name: ${options.name}-mailpit
|
|
8508
|
-
restart: unless-stopped
|
|
8509
|
-
ports:
|
|
8510
|
-
- '\${SMTP_PORT:-1025}:1025'
|
|
8511
|
-
- '\${MAILPIT_PORT:-8025}:8025'
|
|
8512
|
-
environment:
|
|
8513
|
-
MP_SMTP_AUTH: \${SMTP_USER:-${options.name}}:\${SMTP_PASS:-${options.name}}`);
|
|
8514
|
-
if (options.services?.storage) {
|
|
8515
|
-
services.push(` minio:
|
|
8516
|
-
image: pgsty/minio:RELEASE.2026-08-04T00-00-00Z
|
|
8517
|
-
container_name: ${options.name}-minio
|
|
8518
|
-
restart: unless-stopped
|
|
8519
|
-
entrypoint: sh
|
|
8520
|
-
command: -c 'mkdir -p /data/\${STORAGE_BUCKET:-${options.name}} && /usr/bin/docker-entrypoint.sh server --console-address ":9001" /data'
|
|
8521
|
-
environment:
|
|
8522
|
-
MINIO_ROOT_USER: \${STORAGE_ACCESS_KEY_ID:-${options.name}}
|
|
8523
|
-
MINIO_ROOT_PASSWORD: \${STORAGE_SECRET_ACCESS_KEY:-${options.name}}
|
|
8524
|
-
ports:
|
|
8525
|
-
- '\${MINIO_API_HOST_PORT:-9000}:9000'
|
|
8526
|
-
- '\${MINIO_CONSOLE_HOST_PORT:-9001}:9001'
|
|
8527
|
-
volumes:
|
|
8528
|
-
- minio_data:/data
|
|
8529
|
-
healthcheck:
|
|
8530
|
-
test: ['CMD', 'mc', 'ready', 'local']
|
|
8531
|
-
interval: 10s
|
|
8532
|
-
timeout: 5s
|
|
8533
|
-
retries: 5`);
|
|
8534
|
-
volumes.push(" minio_data:");
|
|
8535
|
-
}
|
|
8536
|
-
if (options.services?.events === "sns") {
|
|
8537
|
-
services.push(` localstack:
|
|
8538
|
-
image: floci/floci:latest
|
|
8539
|
-
container_name: ${options.name}-localstack
|
|
8540
|
-
restart: unless-stopped
|
|
8541
|
-
environment:
|
|
8542
|
-
AWS_DEFAULT_REGION: \${AWS_REGION:-us-east-1}
|
|
8543
|
-
AWS_ACCESS_KEY_ID: \${AWS_ACCESS_KEY_ID:-localstack}
|
|
8544
|
-
AWS_SECRET_ACCESS_KEY: \${AWS_SECRET_ACCESS_KEY:-localstack}
|
|
8545
|
-
ports:
|
|
8546
|
-
- '\${LOCALSTACK_PORT:-4566}:4566'
|
|
8547
|
-
volumes:
|
|
8548
|
-
- localstack_data:/var/lib/localstack
|
|
8549
|
-
healthcheck:
|
|
8550
|
-
test: ['CMD', 'curl', '-f', 'http://localhost:4566/_localstack/health']
|
|
8551
|
-
interval: 10s
|
|
8552
|
-
timeout: 5s
|
|
8553
|
-
retries: 5`);
|
|
8554
|
-
volumes.push(" localstack_data:");
|
|
8555
|
-
}
|
|
8556
|
-
if (options.services?.events === "rabbitmq" && !hasWorker) {
|
|
8557
|
-
services.push(` rabbitmq:
|
|
8558
|
-
image: rabbitmq:3-management-alpine
|
|
8559
|
-
container_name: ${options.name}-rabbitmq
|
|
8560
|
-
restart: unless-stopped
|
|
8561
|
-
ports:
|
|
8562
|
-
- '\${RABBITMQ_HOST_PORT:-5672}:5672'
|
|
8563
|
-
- '\${RABBITMQ_MGMT_HOST_PORT:-15672}:15672'
|
|
8564
|
-
environment:
|
|
8565
|
-
RABBITMQ_DEFAULT_USER: guest
|
|
8566
|
-
RABBITMQ_DEFAULT_PASS: guest
|
|
8567
|
-
volumes:
|
|
8568
|
-
- rabbitmq_data:/var/lib/rabbitmq
|
|
8569
|
-
healthcheck:
|
|
8570
|
-
test: ['CMD', 'rabbitmq-diagnostics', 'check_running']
|
|
8571
|
-
interval: 10s
|
|
8572
|
-
timeout: 5s
|
|
8573
|
-
retries: 5`);
|
|
8574
|
-
if (!volumes.includes(" rabbitmq_data:")) volumes.push(" rabbitmq_data:");
|
|
8575
|
-
}
|
|
8576
|
-
let dockerCompose = `# Use "gkm dev" or "gkm test" to start services.
|
|
8577
|
-
# Running "docker compose up" directly will not inject secrets or resolve ports.
|
|
8578
|
-
services:
|
|
8579
|
-
${services.join("\n\n")}
|
|
8580
|
-
`;
|
|
8581
|
-
if (volumes.length > 0) dockerCompose += `
|
|
8582
|
-
volumes:
|
|
8583
|
-
${volumes.join("\n")}
|
|
8584
|
-
`;
|
|
8585
|
-
files$1.push({
|
|
8586
|
-
path: "docker-compose.yml",
|
|
8587
|
-
content: dockerCompose
|
|
8588
|
-
});
|
|
8589
|
-
return files$1;
|
|
8590
|
-
}
|
|
8591
|
-
/**
|
|
8592
|
-
* Generate .env file for docker-compose with database passwords
|
|
8593
|
-
*/
|
|
8594
|
-
function generateDockerEnv(apps, _eventsBackend) {
|
|
8595
|
-
const envVars = apps.map((app) => {
|
|
8596
|
-
const envVar = `${app.name.toUpperCase()}_DB_PASSWORD`;
|
|
8597
|
-
return `${envVar}=${app.password}`;
|
|
8598
|
-
});
|
|
8599
|
-
envVars.push(`PGBOSS_DB_PASSWORD=pgboss-dev-password`);
|
|
8600
|
-
return `# Auto-generated docker environment file
|
|
8601
|
-
# Contains database passwords for docker-compose postgres init
|
|
8602
|
-
# This file is gitignored - do not commit to version control
|
|
8603
|
-
${envVars.join("\n")}
|
|
8604
|
-
`;
|
|
8605
|
-
}
|
|
8606
|
-
/**
|
|
8607
|
-
* Generate PostgreSQL init shell script that creates per-app users with separate schemas.
|
|
8608
|
-
* Uses idempotent DO blocks so the script can be re-run safely.
|
|
8609
|
-
* - api user: uses public schema
|
|
8610
|
-
* - auth user: uses auth schema with search_path=auth
|
|
8611
|
-
* - pgboss user: uses pgboss schema (when events === 'pgboss')
|
|
8612
|
-
*/
|
|
8613
|
-
function generatePostgresInitScript(apps, _eventsBackend) {
|
|
8614
|
-
const userCreations = apps.map((app) => {
|
|
8615
|
-
const userName = app.name.replace(/-/g, "_");
|
|
8616
|
-
const envVar = `${app.name.toUpperCase()}_DB_PASSWORD`;
|
|
8617
|
-
const isApi = app.name === "api";
|
|
8618
|
-
const schemaName = isApi ? "public" : userName;
|
|
8619
|
-
if (isApi) return `
|
|
8620
|
-
# Create ${app.name} user (uses public schema) - idempotent
|
|
8621
|
-
echo "Creating user ${userName}..."
|
|
8622
|
-
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
|
8623
|
-
DO \\$\\$
|
|
8624
|
-
BEGIN
|
|
8625
|
-
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '${userName}') THEN
|
|
8626
|
-
CREATE USER ${userName} WITH PASSWORD '$${envVar}';
|
|
8627
|
-
ELSE
|
|
8628
|
-
ALTER USER ${userName} WITH PASSWORD '$${envVar}';
|
|
8629
|
-
END IF;
|
|
8630
|
-
END
|
|
8631
|
-
\\$\\$;
|
|
8632
|
-
GRANT ALL ON SCHEMA public TO ${userName};
|
|
8633
|
-
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO ${userName};
|
|
8634
|
-
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO ${userName};
|
|
8635
|
-
EOSQL
|
|
8636
|
-
`;
|
|
8637
|
-
return `
|
|
8638
|
-
# Create ${app.name} user with dedicated schema - idempotent
|
|
8639
|
-
echo "Creating user ${userName} with schema ${schemaName}..."
|
|
8640
|
-
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
|
8641
|
-
DO \\$\\$
|
|
8642
|
-
BEGIN
|
|
8643
|
-
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '${userName}') THEN
|
|
8644
|
-
CREATE USER ${userName} WITH PASSWORD '$${envVar}';
|
|
8645
|
-
ELSE
|
|
8646
|
-
ALTER USER ${userName} WITH PASSWORD '$${envVar}';
|
|
8647
|
-
END IF;
|
|
8648
|
-
END
|
|
8649
|
-
\\$\\$;
|
|
8650
|
-
CREATE SCHEMA IF NOT EXISTS ${schemaName} AUTHORIZATION ${userName};
|
|
8651
|
-
ALTER USER ${userName} SET search_path TO ${schemaName};
|
|
8652
|
-
GRANT USAGE ON SCHEMA ${schemaName} TO ${userName};
|
|
8653
|
-
GRANT ALL ON ALL TABLES IN SCHEMA ${schemaName} TO ${userName};
|
|
8654
|
-
GRANT ALL ON ALL SEQUENCES IN SCHEMA ${schemaName} TO ${userName};
|
|
8655
|
-
ALTER DEFAULT PRIVILEGES IN SCHEMA ${schemaName} GRANT ALL ON TABLES TO ${userName};
|
|
8656
|
-
ALTER DEFAULT PRIVILEGES IN SCHEMA ${schemaName} GRANT ALL ON SEQUENCES TO ${userName};
|
|
8657
|
-
EOSQL
|
|
8658
|
-
`;
|
|
8659
|
-
});
|
|
8660
|
-
const pgbossBlock = `
|
|
8661
|
-
# Create pgboss user with dedicated schema - idempotent
|
|
8662
|
-
echo "Creating pgboss user and schema..."
|
|
8663
|
-
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
|
8664
|
-
DO \\$\\$
|
|
8665
|
-
BEGIN
|
|
8666
|
-
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'pgboss') THEN
|
|
8667
|
-
CREATE USER pgboss WITH PASSWORD '$PGBOSS_DB_PASSWORD';
|
|
8668
|
-
ELSE
|
|
8669
|
-
ALTER USER pgboss WITH PASSWORD '$PGBOSS_DB_PASSWORD';
|
|
8670
|
-
END IF;
|
|
8671
|
-
END
|
|
8672
|
-
\\$\\$;
|
|
8673
|
-
CREATE SCHEMA IF NOT EXISTS pgboss AUTHORIZATION pgboss;
|
|
8674
|
-
ALTER USER pgboss SET search_path TO pgboss;
|
|
8675
|
-
GRANT USAGE ON SCHEMA pgboss TO pgboss;
|
|
8676
|
-
GRANT ALL ON ALL TABLES IN SCHEMA pgboss TO pgboss;
|
|
8677
|
-
GRANT ALL ON ALL SEQUENCES IN SCHEMA pgboss TO pgboss;
|
|
8678
|
-
ALTER DEFAULT PRIVILEGES IN SCHEMA pgboss GRANT ALL ON TABLES TO pgboss;
|
|
8679
|
-
ALTER DEFAULT PRIVILEGES IN SCHEMA pgboss GRANT ALL ON SEQUENCES TO pgboss;
|
|
8680
|
-
EOSQL
|
|
8681
|
-
`;
|
|
8682
|
-
const extensions = `
|
|
8683
|
-
# Create extensions
|
|
8684
|
-
echo "Creating extensions..."
|
|
8685
|
-
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
|
8686
|
-
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
|
8687
|
-
CREATE EXTENSION IF NOT EXISTS citext;
|
|
8688
|
-
EOSQL
|
|
8689
|
-
`;
|
|
8690
|
-
return `#!/bin/bash
|
|
8691
|
-
set -e
|
|
8692
|
-
|
|
8693
|
-
# Auto-generated PostgreSQL init script (idempotent - safe to re-run)
|
|
8694
|
-
# Creates per-app users with separate schemas in a single database
|
|
8695
|
-
# - api: uses public schema
|
|
8696
|
-
# - auth: uses auth schema (search_path=auth)
|
|
8697
|
-
# - pgboss: uses pgboss schema for event processing
|
|
8698
|
-
${extensions}
|
|
8699
|
-
${userCreations.join("\n")}${pgbossBlock}
|
|
8700
|
-
echo "Database initialization complete!"
|
|
8701
|
-
`;
|
|
8702
|
-
}
|
|
8703
|
-
|
|
8704
7829
|
//#endregion
|
|
8705
7830
|
//#region src/init/generators/env.ts
|
|
8706
7831
|
/**
|
|
@@ -8719,6 +7844,9 @@ node_modules/
|
|
|
8719
7844
|
dist/
|
|
8720
7845
|
.gkm/
|
|
8721
7846
|
|
|
7847
|
+
# Written by gkm from the constructs; your own docker-compose.yml is not
|
|
7848
|
+
docker-compose.constructs.yml
|
|
7849
|
+
|
|
8722
7850
|
# Environment (legacy - use gkm secrets instead)
|
|
8723
7851
|
.env
|
|
8724
7852
|
.env.local
|
|
@@ -9110,32 +8238,32 @@ function generateExpoAppFiles(options) {
|
|
|
9110
8238
|
"@better-auth/expo": DEPENDENCY_VERSIONS["better-auth"],
|
|
9111
8239
|
"@geekmidas/client": GEEKMIDAS_VERSIONS["@geekmidas/client"],
|
|
9112
8240
|
"@geekmidas/envkit": GEEKMIDAS_VERSIONS["@geekmidas/envkit"],
|
|
9113
|
-
"@react-navigation/native": "
|
|
8241
|
+
"@react-navigation/native": EXPO_VERSIONS["@react-navigation/native"],
|
|
9114
8242
|
"@tanstack/react-query": DEPENDENCY_VERSIONS["@tanstack/react-query"],
|
|
9115
8243
|
"better-auth": DEPENDENCY_VERSIONS["better-auth"],
|
|
9116
|
-
expo: "
|
|
9117
|
-
"expo-constants": "
|
|
9118
|
-
"expo-dev-client": "
|
|
9119
|
-
"expo-linking": "
|
|
9120
|
-
"expo-router": "
|
|
9121
|
-
"expo-secure-store": "
|
|
9122
|
-
"expo-splash-screen": "
|
|
9123
|
-
"expo-status-bar": "
|
|
9124
|
-
nativewind: "
|
|
9125
|
-
react: "
|
|
9126
|
-
"react-dom": "
|
|
9127
|
-
"react-native": "
|
|
9128
|
-
"react-native-gesture-handler": "
|
|
9129
|
-
"react-native-reanimated": "
|
|
9130
|
-
"react-native-safe-area-context": "
|
|
9131
|
-
"react-native-screens": "
|
|
9132
|
-
"react-native-web": "
|
|
9133
|
-
tailwindcss: "
|
|
8244
|
+
expo: EXPO_VERSIONS["expo"],
|
|
8245
|
+
"expo-constants": EXPO_VERSIONS["expo-constants"],
|
|
8246
|
+
"expo-dev-client": EXPO_VERSIONS["expo-dev-client"],
|
|
8247
|
+
"expo-linking": EXPO_VERSIONS["expo-linking"],
|
|
8248
|
+
"expo-router": EXPO_VERSIONS["expo-router"],
|
|
8249
|
+
"expo-secure-store": EXPO_VERSIONS["expo-secure-store"],
|
|
8250
|
+
"expo-splash-screen": EXPO_VERSIONS["expo-splash-screen"],
|
|
8251
|
+
"expo-status-bar": EXPO_VERSIONS["expo-status-bar"],
|
|
8252
|
+
nativewind: EXPO_VERSIONS["nativewind"],
|
|
8253
|
+
react: EXPO_VERSIONS["react"],
|
|
8254
|
+
"react-dom": EXPO_VERSIONS["react-dom"],
|
|
8255
|
+
"react-native": EXPO_VERSIONS["react-native"],
|
|
8256
|
+
"react-native-gesture-handler": EXPO_VERSIONS["react-native-gesture-handler"],
|
|
8257
|
+
"react-native-reanimated": EXPO_VERSIONS["react-native-reanimated"],
|
|
8258
|
+
"react-native-safe-area-context": EXPO_VERSIONS["react-native-safe-area-context"],
|
|
8259
|
+
"react-native-screens": EXPO_VERSIONS["react-native-screens"],
|
|
8260
|
+
"react-native-web": EXPO_VERSIONS["react-native-web"],
|
|
8261
|
+
tailwindcss: EXPO_VERSIONS["tailwindcss"]
|
|
9134
8262
|
},
|
|
9135
8263
|
devDependencies: {
|
|
9136
|
-
"@babel/core": "
|
|
9137
|
-
"@types/react": "
|
|
9138
|
-
typescript: "
|
|
8264
|
+
"@babel/core": EXPO_VERSIONS["@babel/core"],
|
|
8265
|
+
"@types/react": EXPO_VERSIONS["@types/react"],
|
|
8266
|
+
typescript: TOOLCHAIN_VERSIONS["typescript"]
|
|
9139
8267
|
}
|
|
9140
8268
|
};
|
|
9141
8269
|
const appConfig = `import type { ConfigContext, ExpoConfig } from '@expo/config';
|
|
@@ -9689,7 +8817,7 @@ function generateModelsPackage(options) {
|
|
|
9689
8817
|
exports: { "./*": "./src/*.ts" },
|
|
9690
8818
|
scripts: { typecheck: "tsc --noEmit" },
|
|
9691
8819
|
dependencies: {},
|
|
9692
|
-
devDependencies: { typescript: "
|
|
8820
|
+
devDependencies: { typescript: TOOLCHAIN_VERSIONS["typescript"] }
|
|
9693
8821
|
};
|
|
9694
8822
|
const tsConfig = {
|
|
9695
8823
|
extends: "../../tsconfig.json",
|
|
@@ -9842,14 +8970,14 @@ function deployPackage(options) {
|
|
|
9842
8970
|
}
|
|
9843
8971
|
function generateDeployFiles(options) {
|
|
9844
8972
|
if (options.deployTarget !== "sst") return [];
|
|
9845
|
-
if (!options.region) throw new
|
|
8973
|
+
if (!options.region) throw new SstNeedsRegion();
|
|
9846
8974
|
const db = databaseFor();
|
|
9847
8975
|
const mail = emailFor();
|
|
9848
|
-
const hasDatabase = options.
|
|
8976
|
+
const hasDatabase = options.constructs.database;
|
|
9849
8977
|
const inputs = [...hasDatabase ? [` // RDS needs a network. Created here rather than assumed, because a
|
|
9850
8978
|
// VPC with NAT is a monthly cost in an account that may already
|
|
9851
8979
|
// have one — replace it with \`sst.aws.Vpc.get(…)\` if so.
|
|
9852
|
-
${db.id}: { vpc },`] : [], ...options.
|
|
8980
|
+
${db.id}: { vpc },`] : [], ...options.constructs.mail ? [` // Every provider rejects an unverified sender, so there is no
|
|
9853
8981
|
// default; the synth stops with \`EmailNeedsSender\` until it is set.
|
|
9854
8982
|
${mail.id}: { from: process.env.MAIL_FROM as string },`] : []];
|
|
9855
8983
|
return [{
|
|
@@ -9910,6 +9038,16 @@ ${inputs.join("\n")}
|
|
|
9910
9038
|
`
|
|
9911
9039
|
}];
|
|
9912
9040
|
}
|
|
9041
|
+
/**
|
|
9042
|
+
* An SST scaffold asked for without a region. `init` always resolves one —
|
|
9043
|
+
* asked, `--region`, or eu-west-1 under `--yes` — so this is a caller's bug.
|
|
9044
|
+
*/
|
|
9045
|
+
var SstNeedsRegion = class extends Error {
|
|
9046
|
+
constructor() {
|
|
9047
|
+
super("An SST deploy needs a region: pass --region, e.g. eu-west-1.");
|
|
9048
|
+
this.name = "SstNeedsRegion";
|
|
9049
|
+
}
|
|
9050
|
+
};
|
|
9913
9051
|
|
|
9914
9052
|
//#endregion
|
|
9915
9053
|
//#region src/init/generators/monorepo.ts
|
|
@@ -9929,9 +9067,9 @@ function rootConstructDependencies(options) {
|
|
|
9929
9067
|
"@geekmidas/db": v["@geekmidas/db"],
|
|
9930
9068
|
kysely: DEPENDENCY_VERSIONS.kysely,
|
|
9931
9069
|
pg: DEPENDENCY_VERSIONS.pg,
|
|
9932
|
-
...options.
|
|
9933
|
-
...options.
|
|
9934
|
-
...options.
|
|
9070
|
+
...options.constructs.uploads ? { "@geekmidas/storage": v["@geekmidas/storage"] } : {},
|
|
9071
|
+
...options.constructs.mail ? { "@geekmidas/emailkit": v["@geekmidas/emailkit"] } : {},
|
|
9072
|
+
...options.constructs.cache ? { "@geekmidas/cache": v["@geekmidas/cache"] } : {}
|
|
9935
9073
|
};
|
|
9936
9074
|
}
|
|
9937
9075
|
/**
|
|
@@ -9966,13 +9104,13 @@ function generateMonorepoFiles(options, _template) {
|
|
|
9966
9104
|
...deploy$1.dependencies
|
|
9967
9105
|
},
|
|
9968
9106
|
devDependencies: {
|
|
9969
|
-
"@biomejs/biome": "
|
|
9107
|
+
"@biomejs/biome": DEPENDENCY_VERSIONS["@biomejs/biome"],
|
|
9970
9108
|
"@geekmidas/cli": GEEKMIDAS_VERSIONS["@geekmidas/cli"],
|
|
9971
|
-
esbuild: "
|
|
9972
|
-
tsx: "
|
|
9973
|
-
turbo: "
|
|
9974
|
-
typescript: "
|
|
9975
|
-
vitest: "
|
|
9109
|
+
esbuild: TOOLCHAIN_VERSIONS["esbuild"],
|
|
9110
|
+
tsx: TOOLCHAIN_VERSIONS["tsx"],
|
|
9111
|
+
turbo: DEPENDENCY_VERSIONS["turbo"],
|
|
9112
|
+
typescript: TOOLCHAIN_VERSIONS["typescript"],
|
|
9113
|
+
vitest: TOOLCHAIN_VERSIONS["vitest"],
|
|
9976
9114
|
...deploy$1.devDependencies
|
|
9977
9115
|
}
|
|
9978
9116
|
};
|
|
@@ -9983,7 +9121,7 @@ function generateMonorepoFiles(options, _template) {
|
|
|
9983
9121
|
- 'packages/*'
|
|
9984
9122
|
`;
|
|
9985
9123
|
const biomeConfig = {
|
|
9986
|
-
$schema:
|
|
9124
|
+
$schema: BIOME_SCHEMA,
|
|
9987
9125
|
vcs: {
|
|
9988
9126
|
enabled: true,
|
|
9989
9127
|
clientKind: "git",
|
|
@@ -10028,7 +9166,17 @@ function generateMonorepoFiles(options, _template) {
|
|
|
10028
9166
|
tasks: {
|
|
10029
9167
|
build: {
|
|
10030
9168
|
dependsOn: ["^build"],
|
|
10031
|
-
|
|
9169
|
+
inputs: [
|
|
9170
|
+
"$TURBO_DEFAULT$",
|
|
9171
|
+
"$TURBO_ROOT$/constructs/**/*.ts",
|
|
9172
|
+
"$TURBO_ROOT$/gkm.config.ts"
|
|
9173
|
+
],
|
|
9174
|
+
outputs: [
|
|
9175
|
+
"dist/**",
|
|
9176
|
+
".gkm/**",
|
|
9177
|
+
".next/**",
|
|
9178
|
+
"!.next/cache/**"
|
|
9179
|
+
]
|
|
10032
9180
|
},
|
|
10033
9181
|
dev: {
|
|
10034
9182
|
cache: false,
|
|
@@ -10057,6 +9205,9 @@ node_modules/
|
|
|
10057
9205
|
dist/
|
|
10058
9206
|
.gkm/
|
|
10059
9207
|
|
|
9208
|
+
# Written by gkm from the constructs; your own docker-compose.yml is not
|
|
9209
|
+
docker-compose.constructs.yml
|
|
9210
|
+
|
|
10060
9211
|
# Environment
|
|
10061
9212
|
.env
|
|
10062
9213
|
.env.local
|
|
@@ -10222,7 +9373,7 @@ export default defineConfig({
|
|
|
10222
9373
|
* table in the database that is already there, storage is MinIO, mail is SES.
|
|
10223
9374
|
*/
|
|
10224
9375
|
function generateWorkspaceConfig(options) {
|
|
10225
|
-
|
|
9376
|
+
const config = `import { defineWorkspace } from '@geekmidas/cli/config';
|
|
10226
9377
|
|
|
10227
9378
|
export default defineWorkspace({
|
|
10228
9379
|
// The scope every physical name is built from: \`Database\` becomes
|
|
@@ -10245,11 +9396,6 @@ ${workspaceConstructsGlobs(options.routesStructure, dirname(options.apiPath)).ma
|
|
|
10245
9396
|
},
|
|
10246
9397
|
});
|
|
10247
9398
|
`;
|
|
10248
|
-
if (options.services.events && options.services.events !== "pgboss") config = config.replace(" secrets: {", ` services: {
|
|
10249
|
-
events: '${options.services.events}',
|
|
10250
|
-
},
|
|
10251
|
-
|
|
10252
|
-
secrets: {`);
|
|
10253
9399
|
return config;
|
|
10254
9400
|
}
|
|
10255
9401
|
/**
|
|
@@ -10266,7 +9412,8 @@ ${workspaceConstructsGlobs(options.routesStructure, dirname(options.apiPath)).ma
|
|
|
10266
9412
|
*/
|
|
10267
9413
|
function generateRootConstructs(options) {
|
|
10268
9414
|
if (!options.monorepo || options.template !== "fullstack") return [];
|
|
10269
|
-
const { name: name$1,
|
|
9415
|
+
const { name: name$1, frontendFramework } = options;
|
|
9416
|
+
const { cache, uploads, mail } = options.constructs;
|
|
10270
9417
|
const db = databaseFor();
|
|
10271
9418
|
const files$1 = [];
|
|
10272
9419
|
files$1.push({
|
|
@@ -10381,7 +9528,7 @@ import { auth } from './auth.ts';
|
|
|
10381
9528
|
export const web = new StaticSite('Web', ${variant}).dependsOn([api, auth]);
|
|
10382
9529
|
`
|
|
10383
9530
|
});
|
|
10384
|
-
if (
|
|
9531
|
+
if (uploads) {
|
|
10385
9532
|
const bucket = storageFor();
|
|
10386
9533
|
files$1.push({
|
|
10387
9534
|
path: "constructs/storage.ts",
|
|
@@ -10392,18 +9539,18 @@ export const uploads = new ObjectStorage('${bucket.id}');
|
|
|
10392
9539
|
`
|
|
10393
9540
|
});
|
|
10394
9541
|
}
|
|
10395
|
-
if (
|
|
10396
|
-
const mail = emailFor();
|
|
9542
|
+
if (mail) {
|
|
9543
|
+
const mail$1 = emailFor();
|
|
10397
9544
|
files$1.push({
|
|
10398
9545
|
path: "constructs/email.ts",
|
|
10399
9546
|
content: `import { Email } from '@geekmidas/constructs/email';
|
|
10400
9547
|
|
|
10401
9548
|
/** Outbound mail — Mailpit locally, SES deployed. */
|
|
10402
|
-
export const email = new Email('${mail.id}', { templates: {} });
|
|
9549
|
+
export const email = new Email('${mail$1.id}', { templates: {} });
|
|
10403
9550
|
`
|
|
10404
9551
|
});
|
|
10405
9552
|
}
|
|
10406
|
-
if (
|
|
9553
|
+
if (cache) {
|
|
10407
9554
|
const kv = cacheFor();
|
|
10408
9555
|
files$1.push({
|
|
10409
9556
|
path: "constructs/cache.ts",
|
|
@@ -10436,20 +9583,20 @@ const apiTemplate = {
|
|
|
10436
9583
|
"@geekmidas/services": GEEKMIDAS_VERSIONS["@geekmidas/services"],
|
|
10437
9584
|
"@geekmidas/errors": GEEKMIDAS_VERSIONS["@geekmidas/errors"],
|
|
10438
9585
|
"@geekmidas/auth": GEEKMIDAS_VERSIONS["@geekmidas/auth"],
|
|
10439
|
-
"@hono/node-server": "
|
|
9586
|
+
"@hono/node-server": DEPENDENCY_VERSIONS["@hono/node-server"],
|
|
10440
9587
|
hono: DEPENDENCY_VERSIONS.hono,
|
|
10441
9588
|
pino: DEPENDENCY_VERSIONS.pino,
|
|
10442
9589
|
zod: DEPENDENCY_VERSIONS.zod
|
|
10443
9590
|
},
|
|
10444
9591
|
devDependencies: {
|
|
10445
|
-
"@biomejs/biome": "
|
|
9592
|
+
"@biomejs/biome": DEPENDENCY_VERSIONS["@biomejs/biome"],
|
|
10446
9593
|
"@geekmidas/cli": GEEKMIDAS_VERSIONS["@geekmidas/cli"],
|
|
10447
|
-
"@types/node": "
|
|
10448
|
-
esbuild: "
|
|
10449
|
-
tsx: "
|
|
10450
|
-
turbo: "
|
|
10451
|
-
typescript: "
|
|
10452
|
-
vitest: "
|
|
9594
|
+
"@types/node": DEPENDENCY_VERSIONS["@types/node"],
|
|
9595
|
+
esbuild: TOOLCHAIN_VERSIONS["esbuild"],
|
|
9596
|
+
tsx: TOOLCHAIN_VERSIONS["tsx"],
|
|
9597
|
+
turbo: DEPENDENCY_VERSIONS["turbo"],
|
|
9598
|
+
typescript: TOOLCHAIN_VERSIONS["typescript"],
|
|
9599
|
+
vitest: TOOLCHAIN_VERSIONS["vitest"]
|
|
10453
9600
|
},
|
|
10454
9601
|
scripts: {
|
|
10455
9602
|
dev: "gkm dev",
|
|
@@ -10462,7 +9609,8 @@ const apiTemplate = {
|
|
|
10462
9609
|
"fmt:check": "biome format ."
|
|
10463
9610
|
},
|
|
10464
9611
|
files: (options) => {
|
|
10465
|
-
const { loggerType, routesStructure, monorepo, name: name$1
|
|
9612
|
+
const { loggerType, routesStructure, monorepo, name: name$1 } = options;
|
|
9613
|
+
const { cache, uploads, mail: sendsMail } = options.constructs;
|
|
10466
9614
|
const bucket = storageFor();
|
|
10467
9615
|
const kv = cacheFor();
|
|
10468
9616
|
const db = databaseFor();
|
|
@@ -10572,7 +9720,7 @@ import { router } from '${src("router.ts")}';
|
|
|
10572
9720
|
export const listUsersEndpoint = router
|
|
10573
9721
|
.get('/users')
|
|
10574
9722
|
.output(ListUsersResponseSchema)
|
|
10575
|
-
${options.database ? ` // \`db\` is here because the router named the database construct.
|
|
9723
|
+
${options.constructs.database ? ` // \`db\` is here because the router named the database construct.
|
|
10576
9724
|
.handle(async ({ db }) => ({
|
|
10577
9725
|
users: await db.selectFrom('users').select(['id', 'name']).execute(),
|
|
10578
9726
|
}));
|
|
@@ -10595,7 +9743,7 @@ export const listUsersEndpoint = router
|
|
|
10595
9743
|
.output(z.object({
|
|
10596
9744
|
users: z.array(UserSchema),
|
|
10597
9745
|
}))
|
|
10598
|
-
${options.database ? ` // \`db\` is here because the router named the database construct.
|
|
9746
|
+
${options.constructs.database ? ` // \`db\` is here because the router named the database construct.
|
|
10599
9747
|
.handle(async ({ db }) => ({
|
|
10600
9748
|
users: await db.selectFrom('users').select(['id', 'name']).execute(),
|
|
10601
9749
|
}));
|
|
@@ -10687,18 +9835,18 @@ export const authService = {
|
|
|
10687
9835
|
files$1.push({
|
|
10688
9836
|
path: "src/router.ts",
|
|
10689
9837
|
content: `import { UnauthorizedError } from '@geekmidas/errors';
|
|
10690
|
-
import { api } from '${constructsImport("api")}';${options.database ? `
|
|
9838
|
+
import { api } from '${constructsImport("api")}';${options.constructs.database ? `
|
|
10691
9839
|
import { database } from '${constructsImport("database")}';` : ""}
|
|
10692
9840
|
import { authService, type Session } from './services/auth.ts';
|
|
10693
9841
|
|
|
10694
9842
|
/**
|
|
10695
9843
|
* The shared endpoint factory — no session required.
|
|
10696
|
-
*${options.database ? `
|
|
9844
|
+
*${options.constructs.database ? `
|
|
10697
9845
|
* Naming the database construct is what puts \`db\` in every handler built from
|
|
10698
9846
|
* this router. Depend on other constructs per endpoint with \`.dependsOn([…])\`.` : `
|
|
10699
9847
|
* Depend on constructs per endpoint with \`.dependsOn([…])\`.`}
|
|
10700
9848
|
*/
|
|
10701
|
-
export const router = api.endpoints${options.database ? ".database(database)" : ""};
|
|
9849
|
+
export const router = api.endpoints${options.constructs.database ? ".database(database)" : ""};
|
|
10702
9850
|
|
|
10703
9851
|
// The auth client available, but the session not enforced.
|
|
10704
9852
|
export const r = router.services([authService]);
|
|
@@ -10734,22 +9882,22 @@ export const profileEndpoint = sessionRouter
|
|
|
10734
9882
|
}
|
|
10735
9883
|
if (!monorepo) files$1.push({
|
|
10736
9884
|
path: "src/router.ts",
|
|
10737
|
-
content: `import { api } from '${constructsImport("api")}';${options.database ? `
|
|
9885
|
+
content: `import { api } from '${constructsImport("api")}';${options.constructs.database ? `
|
|
10738
9886
|
import { database } from '${constructsImport("database")}';` : ""}
|
|
10739
9887
|
|
|
10740
9888
|
/**
|
|
10741
9889
|
* The shared endpoint factory.
|
|
10742
|
-
*${options.database ? `
|
|
9890
|
+
*${options.constructs.database ? `
|
|
10743
9891
|
* Naming the database construct is what puts \`db\` in every handler built from
|
|
10744
9892
|
* this router. Depend on other constructs per endpoint with \`.dependsOn([…])\`.` : `
|
|
10745
9893
|
* Depend on constructs per endpoint with \`.dependsOn([…])\`.`}
|
|
10746
9894
|
*/
|
|
10747
|
-
export const router = api.endpoints${options.database ? ".database(database)" : ""};
|
|
9895
|
+
export const router = api.endpoints${options.constructs.database ? ".database(database)" : ""};
|
|
10748
9896
|
`
|
|
10749
9897
|
});
|
|
10750
|
-
if (options.database && declares) files$1.push(...databaseFiles());
|
|
10751
|
-
else if (options.database) files$1.push(usersMigration());
|
|
10752
|
-
if (
|
|
9898
|
+
if (options.constructs.database && declares) files$1.push(...databaseFiles());
|
|
9899
|
+
else if (options.constructs.database) files$1.push(usersMigration());
|
|
9900
|
+
if (uploads && declares) files$1.push({
|
|
10753
9901
|
path: "src/constructs/storage.ts",
|
|
10754
9902
|
content: `import { ObjectStorage } from '@geekmidas/constructs/object-storage';
|
|
10755
9903
|
|
|
@@ -10763,7 +9911,7 @@ export const router = api.endpoints${options.database ? ".database(database)" :
|
|
|
10763
9911
|
export const uploads = new ObjectStorage('${bucket.id}');
|
|
10764
9912
|
`
|
|
10765
9913
|
});
|
|
10766
|
-
if (
|
|
9914
|
+
if (sendsMail && declares) files$1.push({
|
|
10767
9915
|
path: "src/constructs/email.ts",
|
|
10768
9916
|
content: `import { Email } from '@geekmidas/constructs/email';
|
|
10769
9917
|
|
|
@@ -10772,13 +9920,13 @@ export const uploads = new ObjectStorage('${bucket.id}');
|
|
|
10772
9920
|
*
|
|
10773
9921
|
* Add React templates to \`templates\` and \`sendTemplate\` becomes typed
|
|
10774
9922
|
* against them. Reach it with \`.dependsOn([email])\` for
|
|
10775
|
-
* \`services.${mail.service}
|
|
10776
|
-
* \`
|
|
9923
|
+
* \`services.${mail.service}\`. Who delivers it is whichever provider the
|
|
9924
|
+
* stage's \`${mail.urlKey}\` points at — every one of them speaks SMTP.
|
|
10777
9925
|
*/
|
|
10778
9926
|
export const email = new Email('${mail.id}', { templates: {} });
|
|
10779
9927
|
`
|
|
10780
9928
|
});
|
|
10781
|
-
if (
|
|
9929
|
+
if (cache && declares) files$1.push({
|
|
10782
9930
|
path: "src/constructs/cache.ts",
|
|
10783
9931
|
content: `import { Cache } from '@geekmidas/constructs/cache';
|
|
10784
9932
|
|
|
@@ -10786,18 +9934,18 @@ export const email = new Email('${mail.id}', { templates: {} });
|
|
|
10786
9934
|
* A cache, declared once.
|
|
10787
9935
|
*
|
|
10788
9936
|
* Reach it with \`.dependsOn([cache])\` for \`services.${kv.service}\`. This
|
|
10789
|
-
* form says the app caches and leaves *where* to the deployment:
|
|
10790
|
-
*
|
|
10791
|
-
*
|
|
9937
|
+
* form says the app caches and leaves *where* to the deployment: Upstash on
|
|
9938
|
+
* AWS, a table in the database on a server — the same code caches into
|
|
9939
|
+
* either.
|
|
10792
9940
|
*
|
|
10793
9941
|
* To say it caches in a particular database instead, declare it from that
|
|
10794
9942
|
* database — \`database.cache()\` — and entries become a table in it, in its
|
|
10795
|
-
* schema and reached by its role,
|
|
9943
|
+
* schema and reached by its role, whatever the target.
|
|
10796
9944
|
*/
|
|
10797
9945
|
export const cache = new Cache('${kv.id}');
|
|
10798
9946
|
`
|
|
10799
9947
|
});
|
|
10800
|
-
if (options.database && !declares) files$1.push({
|
|
9948
|
+
if (options.constructs.database && !declares) files$1.push({
|
|
10801
9949
|
path: "src/services/database.ts",
|
|
10802
9950
|
content: `import type { Service, ServiceRegisterOptions } from '@geekmidas/services';
|
|
10803
9951
|
import { Kysely, PostgresDialect } from 'kysely';
|
|
@@ -10848,7 +9996,7 @@ export const telescope = new Telescope({
|
|
|
10848
9996
|
});
|
|
10849
9997
|
`
|
|
10850
9998
|
});
|
|
10851
|
-
if (options.studio && options.database) files$1.push({
|
|
9999
|
+
if (options.studio && options.constructs.database) files$1.push({
|
|
10852
10000
|
path: "src/config/studio.ts",
|
|
10853
10001
|
content: `import { Direction, InMemoryMonitoringStorage, Studio } from '@geekmidas/studio';
|
|
10854
10002
|
import { Kysely, PostgresDialect } from 'kysely';
|
|
@@ -10897,20 +10045,20 @@ const minimalTemplate = {
|
|
|
10897
10045
|
"@geekmidas/logger": GEEKMIDAS_VERSIONS["@geekmidas/logger"],
|
|
10898
10046
|
"@geekmidas/rate-limit": GEEKMIDAS_VERSIONS["@geekmidas/rate-limit"],
|
|
10899
10047
|
"@geekmidas/schema": GEEKMIDAS_VERSIONS["@geekmidas/schema"],
|
|
10900
|
-
"@hono/node-server": "
|
|
10048
|
+
"@hono/node-server": DEPENDENCY_VERSIONS["@hono/node-server"],
|
|
10901
10049
|
hono: DEPENDENCY_VERSIONS.hono,
|
|
10902
10050
|
pino: DEPENDENCY_VERSIONS.pino,
|
|
10903
10051
|
zod: DEPENDENCY_VERSIONS.zod
|
|
10904
10052
|
},
|
|
10905
10053
|
devDependencies: {
|
|
10906
|
-
"@biomejs/biome": "
|
|
10054
|
+
"@biomejs/biome": DEPENDENCY_VERSIONS["@biomejs/biome"],
|
|
10907
10055
|
"@geekmidas/cli": GEEKMIDAS_VERSIONS["@geekmidas/cli"],
|
|
10908
|
-
"@types/node": "
|
|
10909
|
-
esbuild: "
|
|
10910
|
-
tsx: "
|
|
10911
|
-
turbo: "
|
|
10912
|
-
typescript: "
|
|
10913
|
-
vitest: "
|
|
10056
|
+
"@types/node": DEPENDENCY_VERSIONS["@types/node"],
|
|
10057
|
+
esbuild: TOOLCHAIN_VERSIONS["esbuild"],
|
|
10058
|
+
tsx: TOOLCHAIN_VERSIONS["tsx"],
|
|
10059
|
+
turbo: DEPENDENCY_VERSIONS["turbo"],
|
|
10060
|
+
typescript: TOOLCHAIN_VERSIONS["typescript"],
|
|
10061
|
+
vitest: TOOLCHAIN_VERSIONS["vitest"]
|
|
10914
10062
|
},
|
|
10915
10063
|
scripts: {
|
|
10916
10064
|
dev: "gkm dev",
|
|
@@ -10996,7 +10144,7 @@ export const healthEndpoint = api
|
|
|
10996
10144
|
`
|
|
10997
10145
|
}
|
|
10998
10146
|
];
|
|
10999
|
-
if (options.database) files$1.push(...databaseFiles());
|
|
10147
|
+
if (options.constructs.database) files$1.push(...databaseFiles());
|
|
11000
10148
|
if (options.telescope) files$1.push({
|
|
11001
10149
|
path: "src/config/telescope.ts",
|
|
11002
10150
|
content: `import { Telescope } from '@geekmidas/telescope';
|
|
@@ -11008,7 +10156,7 @@ export const telescope = new Telescope({
|
|
|
11008
10156
|
});
|
|
11009
10157
|
`
|
|
11010
10158
|
});
|
|
11011
|
-
if (options.studio && options.database) files$1.push({
|
|
10159
|
+
if (options.studio && options.constructs.database) files$1.push({
|
|
11012
10160
|
path: "src/config/studio.ts",
|
|
11013
10161
|
content: `import { Direction, InMemoryMonitoringStorage, Studio } from '@geekmidas/studio';
|
|
11014
10162
|
import { Kysely, PostgresDialect } from 'kysely';
|
|
@@ -11059,19 +10207,19 @@ const serverlessTemplate = {
|
|
|
11059
10207
|
"@geekmidas/cloud": GEEKMIDAS_VERSIONS["@geekmidas/cloud"],
|
|
11060
10208
|
"@geekmidas/rate-limit": GEEKMIDAS_VERSIONS["@geekmidas/rate-limit"],
|
|
11061
10209
|
"@geekmidas/schema": GEEKMIDAS_VERSIONS["@geekmidas/schema"],
|
|
11062
|
-
"@hono/node-server": "
|
|
10210
|
+
"@hono/node-server": DEPENDENCY_VERSIONS["@hono/node-server"],
|
|
11063
10211
|
hono: DEPENDENCY_VERSIONS.hono,
|
|
11064
10212
|
pino: DEPENDENCY_VERSIONS.pino
|
|
11065
10213
|
},
|
|
11066
10214
|
devDependencies: {
|
|
11067
|
-
"@biomejs/biome": "
|
|
10215
|
+
"@biomejs/biome": DEPENDENCY_VERSIONS["@biomejs/biome"],
|
|
11068
10216
|
"@geekmidas/cli": GEEKMIDAS_VERSIONS["@geekmidas/cli"],
|
|
11069
|
-
"@types/aws-lambda": "
|
|
11070
|
-
"@types/node": "
|
|
11071
|
-
tsx: "
|
|
11072
|
-
turbo: "
|
|
11073
|
-
typescript: "
|
|
11074
|
-
vitest: "
|
|
10217
|
+
"@types/aws-lambda": DEPENDENCY_VERSIONS["@types/aws-lambda"],
|
|
10218
|
+
"@types/node": DEPENDENCY_VERSIONS["@types/node"],
|
|
10219
|
+
tsx: TOOLCHAIN_VERSIONS["tsx"],
|
|
10220
|
+
turbo: DEPENDENCY_VERSIONS["turbo"],
|
|
10221
|
+
typescript: TOOLCHAIN_VERSIONS["typescript"],
|
|
10222
|
+
vitest: TOOLCHAIN_VERSIONS["vitest"]
|
|
11075
10223
|
},
|
|
11076
10224
|
scripts: {
|
|
11077
10225
|
dev: "gkm dev",
|
|
@@ -11179,7 +10327,7 @@ export const worker = new Worker('Jobs', { logger });
|
|
|
11179
10327
|
`
|
|
11180
10328
|
}
|
|
11181
10329
|
];
|
|
11182
|
-
if (options.database) files$1.push(...databaseFiles());
|
|
10330
|
+
if (options.constructs.database) files$1.push(...databaseFiles());
|
|
11183
10331
|
if (options.telescope) files$1.push({
|
|
11184
10332
|
path: "src/config/telescope.ts",
|
|
11185
10333
|
content: `import { Telescope } from '@geekmidas/telescope';
|
|
@@ -11227,13 +10375,13 @@ const workerTemplate = {
|
|
|
11227
10375
|
pino: DEPENDENCY_VERSIONS.pino
|
|
11228
10376
|
},
|
|
11229
10377
|
devDependencies: {
|
|
11230
|
-
"@biomejs/biome": "
|
|
10378
|
+
"@biomejs/biome": DEPENDENCY_VERSIONS["@biomejs/biome"],
|
|
11231
10379
|
"@geekmidas/cli": GEEKMIDAS_VERSIONS["@geekmidas/cli"],
|
|
11232
|
-
"@types/node": "
|
|
11233
|
-
tsx: "
|
|
11234
|
-
turbo: "
|
|
11235
|
-
typescript: "
|
|
11236
|
-
vitest: "
|
|
10380
|
+
"@types/node": DEPENDENCY_VERSIONS["@types/node"],
|
|
10381
|
+
tsx: TOOLCHAIN_VERSIONS["tsx"],
|
|
10382
|
+
turbo: DEPENDENCY_VERSIONS["turbo"],
|
|
10383
|
+
typescript: TOOLCHAIN_VERSIONS["typescript"],
|
|
10384
|
+
vitest: TOOLCHAIN_VERSIONS["vitest"]
|
|
11237
10385
|
},
|
|
11238
10386
|
scripts: {
|
|
11239
10387
|
dev: "gkm dev",
|
|
@@ -11246,7 +10394,8 @@ const workerTemplate = {
|
|
|
11246
10394
|
"fmt:check": "biome format ."
|
|
11247
10395
|
},
|
|
11248
10396
|
files: (options) => {
|
|
11249
|
-
const { loggerType, name: name$1
|
|
10397
|
+
const { loggerType, name: name$1 } = options;
|
|
10398
|
+
const { database } = options.constructs;
|
|
11250
10399
|
const loggerContent = `import { createLogger } from '@geekmidas/logger/${loggerType}';
|
|
11251
10400
|
|
|
11252
10401
|
export const logger = createLogger();
|
|
@@ -11362,7 +10511,7 @@ export const cleanup = worker
|
|
|
11362
10511
|
});
|
|
11363
10512
|
`
|
|
11364
10513
|
});
|
|
11365
|
-
if (options.database) files$1.push(...databaseFiles());
|
|
10514
|
+
if (options.constructs.database) files$1.push(...databaseFiles());
|
|
11366
10515
|
if (options.telescope) files$1.push({
|
|
11367
10516
|
path: "src/config/telescope.ts",
|
|
11368
10517
|
content: `import { Telescope } from '@geekmidas/telescope';
|
|
@@ -11507,60 +10656,32 @@ const deployTargetChoices = [
|
|
|
11507
10656
|
}
|
|
11508
10657
|
];
|
|
11509
10658
|
/**
|
|
11510
|
-
*
|
|
11511
|
-
*/
|
|
11512
|
-
/**
|
|
11513
|
-
* What the project declares — and therefore which containers it runs.
|
|
10659
|
+
* Which constructs to declare.
|
|
11514
10660
|
*
|
|
11515
|
-
*
|
|
11516
|
-
*
|
|
11517
|
-
*
|
|
10661
|
+
* The containers that run them locally and the backends that serve them
|
|
10662
|
+
* deployed are derived from these, so the prompt names only what the code
|
|
10663
|
+
* will say.
|
|
11518
10664
|
*/
|
|
11519
|
-
const
|
|
10665
|
+
const constructChoices = [
|
|
11520
10666
|
{
|
|
11521
|
-
title: "
|
|
11522
|
-
value: "
|
|
11523
|
-
description: "
|
|
10667
|
+
title: "Database",
|
|
10668
|
+
value: "database",
|
|
10669
|
+
description: "KyselyDatabase — Postgres"
|
|
11524
10670
|
},
|
|
11525
10671
|
{
|
|
11526
|
-
title: "
|
|
10672
|
+
title: "Cache",
|
|
11527
10673
|
value: "cache",
|
|
11528
|
-
description: "
|
|
10674
|
+
description: "Cache — backed by what the deploy target implies"
|
|
11529
10675
|
},
|
|
11530
10676
|
{
|
|
11531
|
-
title: "
|
|
11532
|
-
value: "
|
|
11533
|
-
description: "
|
|
10677
|
+
title: "Uploads",
|
|
10678
|
+
value: "uploads",
|
|
10679
|
+
description: "ObjectStorage — a bucket"
|
|
11534
10680
|
},
|
|
11535
10681
|
{
|
|
11536
|
-
title: "
|
|
11537
|
-
value: "
|
|
11538
|
-
description: "
|
|
11539
|
-
}
|
|
11540
|
-
];
|
|
11541
|
-
/**
|
|
11542
|
-
* Event backend choices for prompts
|
|
11543
|
-
*/
|
|
11544
|
-
const eventsBackendChoices = [
|
|
11545
|
-
{
|
|
11546
|
-
title: "pg-boss",
|
|
11547
|
-
value: "pgboss",
|
|
11548
|
-
description: "PostgreSQL-based job queue (reuses postgres, no extra container)"
|
|
11549
|
-
},
|
|
11550
|
-
{
|
|
11551
|
-
title: "SNS/SQS",
|
|
11552
|
-
value: "sns",
|
|
11553
|
-
description: "AWS SNS+SQS via LocalStack for local dev"
|
|
11554
|
-
},
|
|
11555
|
-
{
|
|
11556
|
-
title: "RabbitMQ",
|
|
11557
|
-
value: "rabbitmq",
|
|
11558
|
-
description: "AMQP message broker"
|
|
11559
|
-
},
|
|
11560
|
-
{
|
|
11561
|
-
title: "None",
|
|
11562
|
-
value: void 0,
|
|
11563
|
-
description: "Skip event backend"
|
|
10682
|
+
title: "Mail",
|
|
10683
|
+
value: "mail",
|
|
10684
|
+
description: "Email — outbound mail over SMTP"
|
|
11564
10685
|
}
|
|
11565
10686
|
];
|
|
11566
10687
|
/**
|
|
@@ -11585,26 +10706,27 @@ function isFullstackTemplate(name$1) {
|
|
|
11585
10706
|
* Generate package.json with dependencies based on template and options
|
|
11586
10707
|
*/
|
|
11587
10708
|
function generatePackageJson(options, template) {
|
|
11588
|
-
const { name: name$1, telescope,
|
|
10709
|
+
const { name: name$1, telescope, studio, monorepo } = options;
|
|
10710
|
+
const { database, cache, uploads, mail } = options.constructs;
|
|
11589
10711
|
const dependencies$1 = { ...template.dependencies };
|
|
11590
10712
|
const devDependencies$1 = { ...template.devDependencies };
|
|
11591
10713
|
const scripts$1 = { ...template.scripts };
|
|
11592
10714
|
if (telescope) dependencies$1["@geekmidas/telescope"] = GEEKMIDAS_VERSIONS["@geekmidas/telescope"];
|
|
11593
10715
|
if (studio) dependencies$1["@geekmidas/studio"] = GEEKMIDAS_VERSIONS["@geekmidas/studio"];
|
|
11594
10716
|
const declares = !monorepo;
|
|
11595
|
-
if (declares && (database ||
|
|
11596
|
-
if (declares &&
|
|
11597
|
-
if (declares &&
|
|
11598
|
-
if (declares &&
|
|
10717
|
+
if (declares && (database || uploads || cache || mail)) dependencies$1["@geekmidas/manifest"] = GEEKMIDAS_VERSIONS["@geekmidas/manifest"];
|
|
10718
|
+
if (declares && uploads) dependencies$1["@geekmidas/storage"] = GEEKMIDAS_VERSIONS["@geekmidas/storage"];
|
|
10719
|
+
if (declares && cache) dependencies$1["@geekmidas/cache"] = GEEKMIDAS_VERSIONS["@geekmidas/cache"];
|
|
10720
|
+
if (declares && mail) dependencies$1["@geekmidas/emailkit"] = GEEKMIDAS_VERSIONS["@geekmidas/emailkit"];
|
|
11599
10721
|
if (database) {
|
|
11600
10722
|
dependencies$1["@geekmidas/db"] = GEEKMIDAS_VERSIONS["@geekmidas/db"];
|
|
11601
10723
|
dependencies$1.kysely = DEPENDENCY_VERSIONS.kysely;
|
|
11602
10724
|
dependencies$1.pg = DEPENDENCY_VERSIONS.pg;
|
|
11603
|
-
devDependencies$1["@types/pg"] = "
|
|
10725
|
+
devDependencies$1["@types/pg"] = DEPENDENCY_VERSIONS["@types/pg"];
|
|
11604
10726
|
devDependencies$1["@geekmidas/testkit"] = GEEKMIDAS_VERSIONS["@geekmidas/testkit"];
|
|
11605
|
-
devDependencies$1["@faker-js/faker"] = "
|
|
10727
|
+
devDependencies$1["@faker-js/faker"] = DEPENDENCY_VERSIONS["@faker-js/faker"];
|
|
11606
10728
|
devDependencies$1["kysely-ctl"] = DEPENDENCY_VERSIONS["kysely-ctl"];
|
|
11607
|
-
devDependencies$1["vite-tsconfig-paths"] = "
|
|
10729
|
+
devDependencies$1["vite-tsconfig-paths"] = DEPENDENCY_VERSIONS["vite-tsconfig-paths"];
|
|
11608
10730
|
}
|
|
11609
10731
|
if (monorepo) {
|
|
11610
10732
|
delete devDependencies$1["@biomejs/biome"];
|
|
@@ -11661,7 +10783,7 @@ function generateSourceFiles(options, template) {
|
|
|
11661
10783
|
* factory system with builders/seeds, and an example spec.
|
|
11662
10784
|
*/
|
|
11663
10785
|
function generateTestFiles(options, _template) {
|
|
11664
|
-
if (!options.database) return [];
|
|
10786
|
+
if (!options.constructs.database) return [];
|
|
11665
10787
|
const db = databaseFor();
|
|
11666
10788
|
const declares = !options.monorepo;
|
|
11667
10789
|
const schema = declares ? "../src/constructs/database.ts" : "~/services/database.ts";
|
|
@@ -11829,32 +10951,32 @@ function generateUiPackageFiles(options) {
|
|
|
11829
10951
|
"build:storybook": "storybook build -o dist/storybook"
|
|
11830
10952
|
},
|
|
11831
10953
|
dependencies: {
|
|
11832
|
-
"@radix-ui/react-dialog": "
|
|
11833
|
-
"@radix-ui/react-label": "
|
|
11834
|
-
"@radix-ui/react-separator": "
|
|
11835
|
-
"@radix-ui/react-slot": "
|
|
11836
|
-
"@radix-ui/react-tabs": "
|
|
11837
|
-
"@radix-ui/react-tooltip": "
|
|
11838
|
-
"class-variance-authority": "
|
|
11839
|
-
clsx: "
|
|
11840
|
-
"lucide-react": "
|
|
11841
|
-
"tailwind-merge": "
|
|
10954
|
+
"@radix-ui/react-dialog": DEPENDENCY_VERSIONS["@radix-ui/react-dialog"],
|
|
10955
|
+
"@radix-ui/react-label": DEPENDENCY_VERSIONS["@radix-ui/react-label"],
|
|
10956
|
+
"@radix-ui/react-separator": DEPENDENCY_VERSIONS["@radix-ui/react-separator"],
|
|
10957
|
+
"@radix-ui/react-slot": DEPENDENCY_VERSIONS["@radix-ui/react-slot"],
|
|
10958
|
+
"@radix-ui/react-tabs": DEPENDENCY_VERSIONS["@radix-ui/react-tabs"],
|
|
10959
|
+
"@radix-ui/react-tooltip": DEPENDENCY_VERSIONS["@radix-ui/react-tooltip"],
|
|
10960
|
+
"class-variance-authority": DEPENDENCY_VERSIONS["class-variance-authority"],
|
|
10961
|
+
clsx: DEPENDENCY_VERSIONS["clsx"],
|
|
10962
|
+
"lucide-react": DEPENDENCY_VERSIONS["lucide-react"],
|
|
10963
|
+
"tailwind-merge": DEPENDENCY_VERSIONS["tailwind-merge"]
|
|
11842
10964
|
},
|
|
11843
10965
|
devDependencies: {
|
|
11844
|
-
"@storybook/addon-a11y": "
|
|
11845
|
-
"@storybook/addon-essentials": "
|
|
11846
|
-
"@storybook/addon-interactions": "
|
|
11847
|
-
"@storybook/react": "
|
|
11848
|
-
"@storybook/react-vite": "
|
|
11849
|
-
"@tailwindcss/vite": "
|
|
11850
|
-
"@types/react": "
|
|
11851
|
-
"@types/react-dom": "
|
|
11852
|
-
react: "
|
|
11853
|
-
"react-dom": "
|
|
11854
|
-
storybook: "
|
|
11855
|
-
tailwindcss: "
|
|
11856
|
-
typescript: "
|
|
11857
|
-
vite: "
|
|
10966
|
+
"@storybook/addon-a11y": TOOLCHAIN_VERSIONS["@storybook/addon-a11y"],
|
|
10967
|
+
"@storybook/addon-essentials": TOOLCHAIN_VERSIONS["@storybook/addon-essentials"],
|
|
10968
|
+
"@storybook/addon-interactions": TOOLCHAIN_VERSIONS["@storybook/addon-interactions"],
|
|
10969
|
+
"@storybook/react": TOOLCHAIN_VERSIONS["@storybook/react"],
|
|
10970
|
+
"@storybook/react-vite": TOOLCHAIN_VERSIONS["@storybook/react-vite"],
|
|
10971
|
+
"@tailwindcss/vite": DEPENDENCY_VERSIONS["@tailwindcss/vite"],
|
|
10972
|
+
"@types/react": DEPENDENCY_VERSIONS["@types/react"],
|
|
10973
|
+
"@types/react-dom": DEPENDENCY_VERSIONS["@types/react-dom"],
|
|
10974
|
+
react: DEPENDENCY_VERSIONS["react"],
|
|
10975
|
+
"react-dom": DEPENDENCY_VERSIONS["react-dom"],
|
|
10976
|
+
storybook: TOOLCHAIN_VERSIONS["storybook"],
|
|
10977
|
+
tailwindcss: DEPENDENCY_VERSIONS["tailwindcss"],
|
|
10978
|
+
typescript: TOOLCHAIN_VERSIONS["typescript"],
|
|
10979
|
+
vite: TOOLCHAIN_VERSIONS["vite@storybook"]
|
|
11858
10980
|
},
|
|
11859
10981
|
peerDependencies: {
|
|
11860
10982
|
react: ">=18.0.0",
|
|
@@ -13247,19 +12369,19 @@ function generateWebAppFiles(options) {
|
|
|
13247
12369
|
"@geekmidas/envkit": GEEKMIDAS_VERSIONS["@geekmidas/envkit"],
|
|
13248
12370
|
"@tanstack/react-query": DEPENDENCY_VERSIONS["@tanstack/react-query"],
|
|
13249
12371
|
"better-auth": DEPENDENCY_VERSIONS["better-auth"],
|
|
13250
|
-
next: "
|
|
13251
|
-
react: "
|
|
13252
|
-
"react-dom": "
|
|
12372
|
+
next: DEPENDENCY_VERSIONS["next"],
|
|
12373
|
+
react: DEPENDENCY_VERSIONS["react"],
|
|
12374
|
+
"react-dom": DEPENDENCY_VERSIONS["react-dom"]
|
|
13253
12375
|
},
|
|
13254
12376
|
devDependencies: {
|
|
13255
12377
|
"@geekmidas/cli": GEEKMIDAS_VERSIONS["@geekmidas/cli"],
|
|
13256
|
-
"@tailwindcss/postcss": "
|
|
13257
|
-
"@types/node": "
|
|
13258
|
-
"@types/react": "
|
|
13259
|
-
"@types/react-dom": "
|
|
13260
|
-
tailwindcss: "
|
|
13261
|
-
tsx: "
|
|
13262
|
-
typescript: "
|
|
12378
|
+
"@tailwindcss/postcss": DEPENDENCY_VERSIONS["@tailwindcss/postcss"],
|
|
12379
|
+
"@types/node": DEPENDENCY_VERSIONS["@types/node"],
|
|
12380
|
+
"@types/react": DEPENDENCY_VERSIONS["@types/react"],
|
|
12381
|
+
"@types/react-dom": DEPENDENCY_VERSIONS["@types/react-dom"],
|
|
12382
|
+
tailwindcss: DEPENDENCY_VERSIONS["tailwindcss"],
|
|
12383
|
+
tsx: TOOLCHAIN_VERSIONS["tsx"],
|
|
12384
|
+
typescript: TOOLCHAIN_VERSIONS["typescript"]
|
|
13263
12385
|
}
|
|
13264
12386
|
};
|
|
13265
12387
|
const nextConfig = `import type { NextConfig } from 'next';
|
|
@@ -13579,25 +12701,25 @@ function generateTanStackWebFiles(options) {
|
|
|
13579
12701
|
"@geekmidas/client": GEEKMIDAS_VERSIONS["@geekmidas/client"],
|
|
13580
12702
|
"@geekmidas/envkit": GEEKMIDAS_VERSIONS["@geekmidas/envkit"],
|
|
13581
12703
|
"@tanstack/react-query": DEPENDENCY_VERSIONS["@tanstack/react-query"],
|
|
13582
|
-
"@tanstack/react-router": "
|
|
13583
|
-
"@tanstack/react-start": "
|
|
12704
|
+
"@tanstack/react-router": DEPENDENCY_VERSIONS["@tanstack/react-router"],
|
|
12705
|
+
"@tanstack/react-start": DEPENDENCY_VERSIONS["@tanstack/react-start"],
|
|
13584
12706
|
"better-auth": DEPENDENCY_VERSIONS["better-auth"],
|
|
13585
|
-
react: "
|
|
13586
|
-
"react-dom": "
|
|
12707
|
+
react: DEPENDENCY_VERSIONS["react"],
|
|
12708
|
+
"react-dom": DEPENDENCY_VERSIONS["react-dom"]
|
|
13587
12709
|
},
|
|
13588
12710
|
devDependencies: {
|
|
13589
12711
|
"@geekmidas/cli": GEEKMIDAS_VERSIONS["@geekmidas/cli"],
|
|
13590
|
-
"@tailwindcss/vite": "
|
|
13591
|
-
"@tanstack/router-plugin": "
|
|
13592
|
-
"@types/node": "
|
|
13593
|
-
"@types/react": "
|
|
13594
|
-
"@types/react-dom": "
|
|
13595
|
-
"@vitejs/plugin-react": "
|
|
13596
|
-
tailwindcss: "
|
|
13597
|
-
tsx: "
|
|
13598
|
-
typescript: "
|
|
13599
|
-
vite: "
|
|
13600
|
-
"vite-tsconfig-paths": "
|
|
12712
|
+
"@tailwindcss/vite": DEPENDENCY_VERSIONS["@tailwindcss/vite"],
|
|
12713
|
+
"@tanstack/router-plugin": DEPENDENCY_VERSIONS["@tanstack/router-plugin"],
|
|
12714
|
+
"@types/node": DEPENDENCY_VERSIONS["@types/node"],
|
|
12715
|
+
"@types/react": DEPENDENCY_VERSIONS["@types/react"],
|
|
12716
|
+
"@types/react-dom": DEPENDENCY_VERSIONS["@types/react-dom"],
|
|
12717
|
+
"@vitejs/plugin-react": TOOLCHAIN_VERSIONS["@vitejs/plugin-react"],
|
|
12718
|
+
tailwindcss: DEPENDENCY_VERSIONS["tailwindcss"],
|
|
12719
|
+
tsx: TOOLCHAIN_VERSIONS["tsx"],
|
|
12720
|
+
typescript: TOOLCHAIN_VERSIONS["typescript"],
|
|
12721
|
+
vite: TOOLCHAIN_VERSIONS["vite"],
|
|
12722
|
+
"vite-tsconfig-paths": DEPENDENCY_VERSIONS["vite-tsconfig-paths"]
|
|
13601
12723
|
}
|
|
13602
12724
|
};
|
|
13603
12725
|
const viteConfig = `import { Credentials } from '@geekmidas/envkit/credentials';
|
|
@@ -14091,8 +13213,8 @@ const AWS_REGION = /^[a-z]{2}(-gov)?-[a-z]+-\d$/;
|
|
|
14091
13213
|
async function initCommand(projectName, options = {}) {
|
|
14092
13214
|
const cwd = process.cwd();
|
|
14093
13215
|
const detectedPkgManager = detectPackageManager(cwd);
|
|
14094
|
-
if (options.deploy && !deployTargetChoices.some((choice) => choice.value === options.deploy)) throw new
|
|
14095
|
-
if (options.region && !AWS_REGION.test(options.region)) throw new
|
|
13216
|
+
if (options.deploy && !deployTargetChoices.some((choice) => choice.value === options.deploy)) throw new UnknownDeployTarget(options.deploy, deployTargetChoices.map((c) => c.value));
|
|
13217
|
+
if (options.region && !AWS_REGION.test(options.region)) throw new NotAnAwsRegion(options.region);
|
|
14096
13218
|
if (options.stages || options.localStage || options.protectedStage) {
|
|
14097
13219
|
const deployed = stageList(options.stages ?? "");
|
|
14098
13220
|
const problems = [
|
|
@@ -14103,7 +13225,7 @@ async function initCommand(projectName, options = {}) {
|
|
|
14103
13225
|
}) : [],
|
|
14104
13226
|
...options.protectedStage && !deployed.includes(options.protectedStage) ? [`--protected-stage "${options.protectedStage}" is not in --stages`] : []
|
|
14105
13227
|
];
|
|
14106
|
-
if (problems.length) throw new
|
|
13228
|
+
if (problems.length) throw new InvalidStages([...new Set(problems)]);
|
|
14107
13229
|
}
|
|
14108
13230
|
let deployedSoFar = [];
|
|
14109
13231
|
prompts.override({});
|
|
@@ -14133,21 +13255,14 @@ async function initCommand(projectName, options = {}) {
|
|
|
14133
13255
|
},
|
|
14134
13256
|
{
|
|
14135
13257
|
type: options.yes ? null : "multiselect",
|
|
14136
|
-
name: "
|
|
14137
|
-
message: "
|
|
14138
|
-
choices:
|
|
13258
|
+
name: "constructs",
|
|
13259
|
+
message: "Constructs to declare (space to select, enter to confirm):",
|
|
13260
|
+
choices: constructChoices.map((c) => ({
|
|
14139
13261
|
...c,
|
|
14140
13262
|
selected: true
|
|
14141
13263
|
})),
|
|
14142
13264
|
hint: "- Space to select. Return to submit"
|
|
14143
13265
|
},
|
|
14144
|
-
{
|
|
14145
|
-
type: options.yes ? null : "select",
|
|
14146
|
-
name: "eventsBackend",
|
|
14147
|
-
message: "Event backend:",
|
|
14148
|
-
choices: eventsBackendChoices,
|
|
14149
|
-
initial: 0
|
|
14150
|
-
},
|
|
14151
13266
|
{
|
|
14152
13267
|
type: options.yes ? null : "select",
|
|
14153
13268
|
name: "packageManager",
|
|
@@ -14248,30 +13363,21 @@ async function initCommand(projectName, options = {}) {
|
|
|
14248
13363
|
const template = options.template || answers.template || "api";
|
|
14249
13364
|
const isFullstack = isFullstackTemplate(template);
|
|
14250
13365
|
const monorepo = isFullstack || options.monorepo || false;
|
|
14251
|
-
const
|
|
14252
|
-
|
|
14253
|
-
"
|
|
14254
|
-
"
|
|
14255
|
-
"
|
|
14256
|
-
|
|
14257
|
-
const eventsBackend = options.yes ? "pgboss" : answers.eventsBackend;
|
|
14258
|
-
const services = {
|
|
14259
|
-
db: servicesArray.includes("db"),
|
|
14260
|
-
cache: servicesArray.includes("cache"),
|
|
14261
|
-
mail: servicesArray.includes("mail"),
|
|
14262
|
-
storage: servicesArray.includes("storage"),
|
|
14263
|
-
events: eventsBackend
|
|
13366
|
+
const chosen = options.yes ? constructChoices.map((c) => c.value) : answers.constructs || [];
|
|
13367
|
+
const constructs = {
|
|
13368
|
+
database: isFullstack || chosen.includes("database"),
|
|
13369
|
+
cache: chosen.includes("cache"),
|
|
13370
|
+
uploads: chosen.includes("uploads"),
|
|
13371
|
+
mail: chosen.includes("mail")
|
|
14264
13372
|
};
|
|
14265
|
-
if (services.events === "pgboss") services.db = true;
|
|
14266
13373
|
const pkgManager = options.pm ? options.pm : options.yes ? "pnpm" : answers.packageManager ?? detectedPkgManager;
|
|
14267
13374
|
const deployTarget = options.deploy ?? (options.yes ? "none" : answers.deployTarget ?? "none");
|
|
14268
|
-
const database =
|
|
13375
|
+
const database = constructs.database;
|
|
14269
13376
|
const frontendFramework = isFullstack ? options.yes ? "nextjs" : answers.frontendFramework ?? "nextjs" : void 0;
|
|
14270
13377
|
const templateOptions = {
|
|
14271
13378
|
name: name$1,
|
|
14272
13379
|
template,
|
|
14273
13380
|
telescope: options.yes ? true : answers.telescope ?? true,
|
|
14274
|
-
database,
|
|
14275
13381
|
studio: database,
|
|
14276
13382
|
loggerType: options.yes ? "pino" : answers.loggerType ?? "pino",
|
|
14277
13383
|
routesStructure: options.yes ? "centralized-endpoints" : answers.routesStructure ?? "centralized-endpoints",
|
|
@@ -14281,7 +13387,7 @@ async function initCommand(projectName, options = {}) {
|
|
|
14281
13387
|
deployTarget,
|
|
14282
13388
|
stages: resolveStages(options, answers),
|
|
14283
13389
|
...deployTarget === "sst" ? { region: options.region ?? (options.yes ? "eu-west-1" : answers.region?.trim()) } : {},
|
|
14284
|
-
|
|
13390
|
+
constructs,
|
|
14285
13391
|
frontendFramework
|
|
14286
13392
|
};
|
|
14287
13393
|
const targetDir = join(cwd, name$1);
|
|
@@ -14292,23 +13398,13 @@ async function initCommand(projectName, options = {}) {
|
|
|
14292
13398
|
await mkdir(targetDir, { recursive: true });
|
|
14293
13399
|
const appDir = isMonorepo$1 ? join(targetDir, apiPath) : targetDir;
|
|
14294
13400
|
if (isMonorepo$1) await mkdir(appDir, { recursive: true });
|
|
14295
|
-
const dbApps = [];
|
|
14296
|
-
if (isFullstack && services.db) dbApps.push({
|
|
14297
|
-
name: "api",
|
|
14298
|
-
password: generateDbPassword()
|
|
14299
|
-
}, {
|
|
14300
|
-
name: "auth",
|
|
14301
|
-
password: generateDbPassword()
|
|
14302
|
-
});
|
|
14303
13401
|
const appFiles = baseTemplate ? [
|
|
14304
13402
|
...generatePackageJson(templateOptions, baseTemplate),
|
|
14305
13403
|
...generateConfigFiles(templateOptions, baseTemplate),
|
|
14306
13404
|
...generateEnvFiles(templateOptions, baseTemplate),
|
|
14307
13405
|
...generateSourceFiles(templateOptions, baseTemplate),
|
|
14308
|
-
...generateTestFiles(templateOptions, baseTemplate)
|
|
14309
|
-
...isMonorepo$1 ? [] : generateDockerFiles(templateOptions, baseTemplate, dbApps)
|
|
13406
|
+
...generateTestFiles(templateOptions, baseTemplate)
|
|
14310
13407
|
] : [];
|
|
14311
|
-
const dockerFiles = isMonorepo$1 && baseTemplate ? generateDockerFiles(templateOptions, baseTemplate, dbApps) : [];
|
|
14312
13408
|
const rootFiles = baseTemplate ? [
|
|
14313
13409
|
...generateAgentFiles(templateOptions, baseTemplate),
|
|
14314
13410
|
...generateGithubFiles(templateOptions),
|
|
@@ -14324,11 +13420,6 @@ async function initCommand(projectName, options = {}) {
|
|
|
14324
13420
|
await mkdir(dirname(fullPath), { recursive: true });
|
|
14325
13421
|
await writeFile(fullPath, content);
|
|
14326
13422
|
}
|
|
14327
|
-
for (const { path, content } of dockerFiles) {
|
|
14328
|
-
const fullPath = join(targetDir, path);
|
|
14329
|
-
await mkdir(dirname(fullPath), { recursive: true });
|
|
14330
|
-
await writeFile(fullPath, content);
|
|
14331
|
-
}
|
|
14332
13423
|
for (const { path, content } of appFiles) {
|
|
14333
13424
|
const fullPath = join(appDir, path);
|
|
14334
13425
|
await mkdir(dirname(fullPath), { recursive: true });
|
|
@@ -14350,30 +13441,14 @@ async function initCommand(projectName, options = {}) {
|
|
|
14350
13441
|
await writeFile(fullPath, content);
|
|
14351
13442
|
}
|
|
14352
13443
|
console.log("🔐 Initializing encrypted secrets...\n");
|
|
14353
|
-
const secretServices = [];
|
|
14354
|
-
if (services.db) secretServices.push("postgres");
|
|
14355
|
-
if (services.cache) secretServices.push("redis");
|
|
14356
|
-
if (services.storage) secretServices.push("minio");
|
|
14357
|
-
if (services.mail) secretServices.push("mailpit");
|
|
14358
|
-
if (services.events === "sns") secretServices.push("localstack");
|
|
14359
|
-
if (services.events === "rabbitmq") secretServices.push("rabbitmq");
|
|
14360
13444
|
const local = templateOptions.stages.local;
|
|
14361
|
-
const devSecrets = createStageSecrets(local,
|
|
14362
|
-
projectName: name$1,
|
|
14363
|
-
eventsBackend: services.events
|
|
14364
|
-
});
|
|
13445
|
+
const devSecrets = createStageSecrets(local, [], { projectName: name$1 });
|
|
14365
13446
|
const customSecrets = {
|
|
14366
13447
|
PORT: "3000",
|
|
14367
13448
|
LOG_LEVEL: "debug",
|
|
14368
13449
|
JWT_SECRET: `dev-${Date.now()}-${Math.random().toString(36).slice(2)}`
|
|
14369
13450
|
};
|
|
14370
|
-
if (isFullstack
|
|
14371
|
-
for (const app of dbApps) {
|
|
14372
|
-
const urlKey = `${app.name.toUpperCase()}_DATABASE_URL`;
|
|
14373
|
-
customSecrets[urlKey] = generateDbUrl(app.name, app.password, name$1);
|
|
14374
|
-
const passwordKey = `${app.name.toUpperCase()}_DB_PASSWORD`;
|
|
14375
|
-
customSecrets[passwordKey] = app.password;
|
|
14376
|
-
}
|
|
13451
|
+
if (isFullstack) {
|
|
14377
13452
|
customSecrets.AUTH_PORT = "3002";
|
|
14378
13453
|
customSecrets.AUTH_URL = "http://localhost:3002";
|
|
14379
13454
|
customSecrets.BETTER_AUTH_SECRET = `better-auth-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
@@ -14438,12 +13513,8 @@ function printNextSteps(projectName, options, pkgManager) {
|
|
|
14438
13513
|
console.log("\n✅ Project created successfully!\n");
|
|
14439
13514
|
console.log("Next steps:\n");
|
|
14440
13515
|
console.log(` ${cdCommand}`);
|
|
14441
|
-
if (options.services.db && options.monorepo) {
|
|
14442
|
-
console.log(` # Start PostgreSQL (if not running)`);
|
|
14443
|
-
console.log(` docker compose up -d postgres`);
|
|
14444
|
-
}
|
|
14445
13516
|
console.log(` ${devCommand$1}`);
|
|
14446
|
-
if (!options.monorepo && options.
|
|
13517
|
+
if (!options.monorepo && options.constructs.database) {
|
|
14447
13518
|
console.log("");
|
|
14448
13519
|
console.log(" # Then, once the container is up:");
|
|
14449
13520
|
console.log(` gkm exec -- pnpm kysely migrate:latest`);
|
|
@@ -14489,7 +13560,7 @@ function printNextSteps(projectName, options, pkgManager) {
|
|
|
14489
13560
|
console.log("🚀 Deployment (AWS, through SST):");
|
|
14490
13561
|
for (const stage of deployed) console.log(` ${getRunCommand(pkgManager, `deploy:${stage}`)} # gkm build --provider aws, then sst deploy`);
|
|
14491
13562
|
console.log(` Uses your AWS credentials, in ${options.region}.`);
|
|
14492
|
-
if (options.
|
|
13563
|
+
if (options.constructs.mail) console.log(" Set MAIL_FROM to a sender verified in SES first.");
|
|
14493
13564
|
console.log("");
|
|
14494
13565
|
}
|
|
14495
13566
|
console.log("📚 Documentation: https://geekmidas.github.io/toolbox/");
|
|
@@ -14511,22 +13582,31 @@ function resolveStages(options, answers) {
|
|
|
14511
13582
|
...kept ? { protected: [kept] } : {}
|
|
14512
13583
|
};
|
|
14513
13584
|
const problems = stageProblems(stages);
|
|
14514
|
-
if (problems.length) throw new
|
|
13585
|
+
if (problems.length) throw new InvalidStages(problems);
|
|
14515
13586
|
return stages;
|
|
14516
13587
|
}
|
|
13588
|
+
/** `--deploy` naming a target init has no scaffold for. */
|
|
13589
|
+
var UnknownDeployTarget = class extends Error {
|
|
13590
|
+
constructor(target, known) {
|
|
13591
|
+
super(`Unknown deploy target "${target}". Use ${known.join(", ")}.`);
|
|
13592
|
+
this.target = target;
|
|
13593
|
+
this.known = known;
|
|
13594
|
+
this.name = "UnknownDeployTarget";
|
|
13595
|
+
}
|
|
13596
|
+
};
|
|
13597
|
+
/** `--region` that is not shaped like one, e.g. `europe` for `eu-west-1`. */
|
|
13598
|
+
var NotAnAwsRegion = class extends Error {
|
|
13599
|
+
constructor(region) {
|
|
13600
|
+
super(`"${region}" is not an AWS region. Use one like eu-west-1.`);
|
|
13601
|
+
this.region = region;
|
|
13602
|
+
this.name = "NotAnAwsRegion";
|
|
13603
|
+
}
|
|
13604
|
+
};
|
|
14517
13605
|
|
|
14518
13606
|
//#endregion
|
|
14519
13607
|
//#region src/secrets/index.ts
|
|
14520
13608
|
const logger$1 = console;
|
|
14521
13609
|
/**
|
|
14522
|
-
* Extract service names from compose config.
|
|
14523
|
-
*/
|
|
14524
|
-
function getServicesFromConfig(services) {
|
|
14525
|
-
if (!services) return [];
|
|
14526
|
-
if (Array.isArray(services)) return services;
|
|
14527
|
-
return Object.entries(services).filter(([, config]) => config).map(([name$1]) => name$1);
|
|
14528
|
-
}
|
|
14529
|
-
/**
|
|
14530
13610
|
* Initialize secrets for a stage.
|
|
14531
13611
|
* Generates secure random passwords for configured services.
|
|
14532
13612
|
*/
|
|
@@ -14536,9 +13616,6 @@ async function secretsInitCommand(options) {
|
|
|
14536
13616
|
logger$1.error(`Secrets already exist for stage "${stage}". Use --force to overwrite.`);
|
|
14537
13617
|
process.exit(1);
|
|
14538
13618
|
}
|
|
14539
|
-
const config = await loadConfig();
|
|
14540
|
-
const services = getServicesFromConfig(config.docker?.compose?.services);
|
|
14541
|
-
if (services.length === 0) logger$1.warn("No services configured in docker.compose.services. Creating secrets with empty services.");
|
|
14542
13619
|
let projectName;
|
|
14543
13620
|
let workspaceSecrets;
|
|
14544
13621
|
try {
|
|
@@ -14550,13 +13627,11 @@ async function secretsInitCommand(options) {
|
|
|
14550
13627
|
logger$1.log(" Detected workspace mode — generating per-app secrets");
|
|
14551
13628
|
}
|
|
14552
13629
|
} catch {}
|
|
14553
|
-
const secrets = createStageSecrets(stage,
|
|
13630
|
+
const secrets = createStageSecrets(stage, [], { projectName });
|
|
14554
13631
|
if (workspaceSecrets) secrets.custom = workspaceSecrets;
|
|
14555
13632
|
await writeStageSecrets(secrets);
|
|
14556
13633
|
logger$1.log(`\n✓ Secrets initialized for stage "${stage}"`);
|
|
14557
13634
|
logger$1.log(` Location: .gkm/secrets/${stage}.json`);
|
|
14558
|
-
logger$1.log("\n Generated credentials for:");
|
|
14559
|
-
for (const service of services) logger$1.log(` - ${service}`);
|
|
14560
13635
|
if (secrets.urls.DATABASE_URL) logger$1.log(`\n DATABASE_URL: ${maskUrl(secrets.urls.DATABASE_URL)}`);
|
|
14561
13636
|
if (secrets.urls.REDIS_URL) logger$1.log(` REDIS_URL: ${maskUrl(secrets.urls.REDIS_URL)}`);
|
|
14562
13637
|
if (secrets.urls.RABBITMQ_URL) logger$1.log(` RABBITMQ_URL: ${maskUrl(secrets.urls.RABBITMQ_URL)}`);
|
|
@@ -14726,11 +13801,11 @@ function maskUrl(url) {
|
|
|
14726
13801
|
async function testCommand(options = {}) {
|
|
14727
13802
|
const cwd = process.cwd();
|
|
14728
13803
|
const stage = options.stage ?? await loadWorkspaceConfig(cwd).then((loaded) => loaded.workspace.stages.local).catch(() => void 0);
|
|
14729
|
-
if (!stage) throw new
|
|
13804
|
+
if (!stage) throw new NoStageToTest();
|
|
14730
13805
|
console.log(`\n🧪 Running tests with ${stage} environment...\n`);
|
|
14731
13806
|
const autoSetup = options.autoSetup || Boolean(process.env.GKM_AUTO_SETUP);
|
|
14732
13807
|
if (autoSetup) {
|
|
14733
|
-
const { ensureStageSecrets } = await import("./setup-
|
|
13808
|
+
const { ensureStageSecrets } = await import("./setup-BmLoNrDX.mjs");
|
|
14734
13809
|
const generated = await ensureStageSecrets(stage, cwd);
|
|
14735
13810
|
if (generated) console.log(` 🔐 Generated fresh ${stage} secrets (auto-setup)`);
|
|
14736
13811
|
}
|
|
@@ -14817,6 +13892,13 @@ function rewriteDatabaseUrlForTests(env) {
|
|
|
14817
13892
|
}
|
|
14818
13893
|
return result;
|
|
14819
13894
|
}
|
|
13895
|
+
/** `gkm test` outside a project that declares stages, with none named. */
|
|
13896
|
+
var NoStageToTest = class extends Error {
|
|
13897
|
+
constructor() {
|
|
13898
|
+
super("No stage to test with: add `stages` to gkm.config.ts, or pass --stage.");
|
|
13899
|
+
this.name = "NoStageToTest";
|
|
13900
|
+
}
|
|
13901
|
+
};
|
|
14820
13902
|
|
|
14821
13903
|
//#endregion
|
|
14822
13904
|
//#region src/upgrade/version.ts
|
|
@@ -15359,14 +14441,14 @@ program.command("secrets:push").description("Push secrets to remote provider (SS
|
|
|
15359
14441
|
const globalOptions = program.opts();
|
|
15360
14442
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
15361
14443
|
const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await import("./config.mjs");
|
|
15362
|
-
const { pushSecrets } = await import("./sync-
|
|
15363
|
-
const { reconcileMissingSecrets } = await import("./reconcile-
|
|
15364
|
-
const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1 } = await import("./storage-
|
|
14444
|
+
const { pushSecrets } = await import("./sync-HAau6h_R.mjs");
|
|
14445
|
+
const { reconcileMissingSecrets } = await import("./reconcile-BI0tWkwz.mjs");
|
|
14446
|
+
const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1 } = await import("./storage-DGBzMLZa.mjs");
|
|
15365
14447
|
const { workspace } = await loadWorkspaceConfig$1();
|
|
15366
14448
|
const secrets = await readStageSecrets$1(options.stage, workspace.root);
|
|
15367
14449
|
if (secrets) {
|
|
15368
|
-
const { derivedContainers
|
|
15369
|
-
const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers
|
|
14450
|
+
const { derivedContainers } = await import("./workspace-CkJAtRIX.mjs");
|
|
14451
|
+
const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, options.stage));
|
|
15370
14452
|
if (result) {
|
|
15371
14453
|
await writeStageSecrets$1(result.secrets, workspace.root);
|
|
15372
14454
|
console.log(` Reconciled ${result.addedKeys.length} missing secret(s):`);
|
|
@@ -15385,17 +14467,17 @@ program.command("secrets:pull").description("Pull secrets from remote provider (
|
|
|
15385
14467
|
const globalOptions = program.opts();
|
|
15386
14468
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
15387
14469
|
const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await import("./config.mjs");
|
|
15388
|
-
const { pullSecrets } = await import("./sync-
|
|
15389
|
-
const { writeStageSecrets: writeStageSecrets$1 } = await import("./storage-
|
|
15390
|
-
const { reconcileMissingSecrets } = await import("./reconcile-
|
|
14470
|
+
const { pullSecrets } = await import("./sync-HAau6h_R.mjs");
|
|
14471
|
+
const { writeStageSecrets: writeStageSecrets$1 } = await import("./storage-DGBzMLZa.mjs");
|
|
14472
|
+
const { reconcileMissingSecrets } = await import("./reconcile-BI0tWkwz.mjs");
|
|
15391
14473
|
const { workspace } = await loadWorkspaceConfig$1();
|
|
15392
14474
|
let secrets = await pullSecrets(options.stage, workspace);
|
|
15393
14475
|
if (!secrets) {
|
|
15394
14476
|
console.error(`No remote secrets found for stage "${options.stage}".`);
|
|
15395
14477
|
process.exit(1);
|
|
15396
14478
|
}
|
|
15397
|
-
const { derivedContainers
|
|
15398
|
-
const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers
|
|
14479
|
+
const { derivedContainers } = await import("./workspace-CkJAtRIX.mjs");
|
|
14480
|
+
const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, options.stage));
|
|
15399
14481
|
if (result) {
|
|
15400
14482
|
secrets = result.secrets;
|
|
15401
14483
|
console.log(` Reconciled ${result.addedKeys.length} missing secret(s):`);
|
|
@@ -15413,8 +14495,8 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
|
|
|
15413
14495
|
const globalOptions = program.opts();
|
|
15414
14496
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
15415
14497
|
const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await import("./config.mjs");
|
|
15416
|
-
const { reconcileMissingSecrets } = await import("./reconcile-
|
|
15417
|
-
const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1 } = await import("./storage-
|
|
14498
|
+
const { reconcileMissingSecrets } = await import("./reconcile-BI0tWkwz.mjs");
|
|
14499
|
+
const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1 } = await import("./storage-DGBzMLZa.mjs");
|
|
15418
14500
|
const { workspace } = await loadWorkspaceConfig$1();
|
|
15419
14501
|
const stage = options.stage ?? workspace.stages.local;
|
|
15420
14502
|
const secrets = await readStageSecrets$1(stage, workspace.root);
|
|
@@ -15422,8 +14504,8 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
|
|
|
15422
14504
|
console.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
|
|
15423
14505
|
process.exit(1);
|
|
15424
14506
|
}
|
|
15425
|
-
const { derivedContainers
|
|
15426
|
-
const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers
|
|
14507
|
+
const { derivedContainers } = await import("./workspace-CkJAtRIX.mjs");
|
|
14508
|
+
const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
|
|
15427
14509
|
if (!result) {
|
|
15428
14510
|
console.log(`\n✓ Secrets for stage "${stage}" are up-to-date`);
|
|
15429
14511
|
return;
|
|
@@ -15477,6 +14559,17 @@ program.command("deploy:init").description("Initialize Dokploy deployment (creat
|
|
|
15477
14559
|
process.exit(1);
|
|
15478
14560
|
}
|
|
15479
14561
|
});
|
|
14562
|
+
program.command("deploy:github").description("Let GitHub Actions deploy a stage: OIDC role in the stage's AWS account, and the GitHub environment the deploy workflow uses").requiredOption("--stage <stage>", "A deployed stage from gkm.config.ts").option("--profile <profile>", "AWS profile for the stage's account").option("--repo <owner/name>", "GitHub repository (default: this one)").option("--policy-arn <arn>", "Policy for the deploy role (default: AdministratorAccess)").option("--dry-run", "Show the plan without changing AWS or GitHub").action(async (options) => {
|
|
14563
|
+
try {
|
|
14564
|
+
const globalOptions = program.opts();
|
|
14565
|
+
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
14566
|
+
const { deployGithubCommand } = await import("./github-DpMZurEK.mjs");
|
|
14567
|
+
await deployGithubCommand(options);
|
|
14568
|
+
} catch (error) {
|
|
14569
|
+
console.error(formatError(error));
|
|
14570
|
+
process.exit(1);
|
|
14571
|
+
}
|
|
14572
|
+
});
|
|
15480
14573
|
program.command("deploy:list").description("List Dokploy resources (projects, registries)").option("--endpoint <url>", "Dokploy server URL (uses stored credentials if logged in)").option("--projects", "List projects").option("--registries", "List registries").action(async (options) => {
|
|
15481
14574
|
try {
|
|
15482
14575
|
const globalOptions = program.opts();
|