@geekmidas/cli 10.0.0-alpha.11 → 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/{bundler-CaTGtiaO.cjs → bundler-BMekgx7a.cjs} +2 -17
- package/dist/bundler-BMekgx7a.cjs.map +1 -0
- package/dist/{bundler-S3ek7ea-.mjs → bundler-Bux2iF-v.mjs} +2 -17
- package/dist/bundler-Bux2iF-v.mjs.map +1 -0
- package/dist/{config-B0L2tvvC.mjs → config-DTsVjKZD.mjs} +3 -3
- package/dist/{config-B0L2tvvC.mjs.map → config-DTsVjKZD.mjs.map} +1 -1
- package/dist/{config-HhDpJmLO.cjs → config-RYR9j6EF.cjs} +3 -3
- package/dist/{config-HhDpJmLO.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-B2Pyh3bJ.cjs → credentials-DQRvPz2S.cjs} +1165 -1077
- package/dist/credentials-DQRvPz2S.cjs.map +1 -0
- package/dist/{credentials-CZrys9C5.mjs → credentials-jMJC63qs.mjs} +1144 -1068
- 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/{fullstack-secrets-METaRLS1.mjs → fullstack-secrets-B6KBHf-g.mjs} +2 -2
- package/dist/{fullstack-secrets-METaRLS1.mjs.map → fullstack-secrets-B6KBHf-g.mjs.map} +1 -1
- package/dist/{fullstack-secrets-D6MyHhQP.cjs → fullstack-secrets-DL8v0y4D.cjs} +1 -13
- package/dist/{fullstack-secrets-D6MyHhQP.cjs.map → fullstack-secrets-DL8v0y4D.cjs.map} +1 -1
- package/dist/{github-BrvaPkFf.cjs → github-BThvbf2W.cjs} +4 -4
- package/dist/{github-BrvaPkFf.cjs.map → github-BThvbf2W.cjs.map} +1 -1
- package/dist/{github-CrpsXSfV.mjs → github-DpMZurEK.mjs} +4 -4
- package/dist/{github-CrpsXSfV.mjs.map → github-DpMZurEK.mjs.map} +1 -1
- package/dist/{index-CR5urhWX.d.cts → index-BzJ7QJ9c.d.mts} +2 -6
- package/dist/index-BzJ7QJ9c.d.mts.map +1 -0
- package/dist/{index-Ayxg_DQY.d.mts → index-CfMjtdVC.d.cts} +2 -6
- package/dist/index-CfMjtdVC.d.cts.map +1 -0
- package/dist/index.cjs +403 -1356
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +404 -1357
- package/dist/index.mjs.map +1 -1
- package/dist/{openapi-y1d7Kalt.mjs → openapi-Bs3-5q_r.mjs} +3 -3
- package/dist/{openapi-y1d7Kalt.mjs.map → openapi-Bs3-5q_r.mjs.map} +1 -1
- package/dist/{openapi-DaKlTKxz.cjs → openapi-DWLTsYLL.cjs} +3 -3
- package/dist/{openapi-DaKlTKxz.cjs.map → openapi-DWLTsYLL.cjs.map} +1 -1
- package/dist/{openapi-react-query-DoR5CmzQ.mjs → openapi-react-query-Byltvm_7.mjs} +1 -1
- package/dist/{openapi-react-query-DoR5CmzQ.mjs.map → openapi-react-query-Byltvm_7.mjs.map} +1 -1
- package/dist/{openapi-react-query-BKH334Ug.cjs → openapi-react-query-CmJ1xDbn.cjs} +1 -1
- package/dist/{openapi-react-query-BKH334Ug.cjs.map → openapi-react-query-CmJ1xDbn.cjs.map} +1 -1
- package/dist/openapi-react-query.cjs +1 -1
- package/dist/openapi-react-query.mjs +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 +1 -1
- package/dist/reconcile/public.d.cts +1 -1
- package/dist/reconcile/public.d.mts +1 -1
- package/dist/reconcile/public.mjs +1 -1
- package/dist/{reconcile-C13ERt1U.mjs → reconcile-BI0tWkwz.mjs} +2 -2
- package/dist/{reconcile-C13ERt1U.mjs.map → reconcile-BI0tWkwz.mjs.map} +1 -1
- package/dist/{reconcile-CYMhFSYy.cjs → reconcile-Bhw5fmKu.cjs} +2 -2
- package/dist/{reconcile-CYMhFSYy.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-DOv92pIu.cjs → setup-BsNR2LBG.cjs} +14 -20
- package/dist/setup-BsNR2LBG.cjs.map +1 -0
- package/dist/{setup-DQo_h_yK.mjs → setup-D5-JMknk.mjs} +14 -20
- package/dist/setup-D5-JMknk.mjs.map +1 -0
- package/dist/{types-Y3fKQSAK.d.cts → types-DVIx8I1N.d.mts} +4 -274
- package/dist/types-DVIx8I1N.d.mts.map +1 -0
- package/dist/{types-CL7jPFM2.d.mts → 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-DQxxs7Jk.cjs → workspace-Cji6lE8E.cjs} +4 -4
- package/dist/workspace-CkJAtRIX.mjs +9 -0
- package/dist/{workspace-DaEX7FEg.mjs → workspace-CtnZnWsa.mjs} +16 -68
- package/dist/workspace-CtnZnWsa.mjs.map +1 -0
- package/dist/{workspace-C-a3G_U0.cjs → workspace-DPV7FtAv.cjs} +16 -68
- package/dist/workspace-DPV7FtAv.cjs.map +1 -0
- package/package.json +11 -11
- package/dist/bundler-CaTGtiaO.cjs.map +0 -1
- package/dist/bundler-S3ek7ea-.mjs.map +0 -1
- package/dist/credentials-B2Pyh3bJ.cjs.map +0 -1
- package/dist/credentials-CZrys9C5.mjs.map +0 -1
- package/dist/discover-CAlFgYec.mjs.map +0 -1
- package/dist/discover-DTmFeloL.cjs.map +0 -1
- package/dist/index-Ayxg_DQY.d.mts.map +0 -1
- package/dist/index-CR5urhWX.d.cts.map +0 -1
- package/dist/setup-BWgMs4gg.mjs +0 -12
- package/dist/setup-DOv92pIu.cjs.map +0 -1
- package/dist/setup-DQo_h_yK.mjs.map +0 -1
- package/dist/setup-lCx_DcUo.cjs +0 -12
- package/dist/types-CL7jPFM2.d.mts.map +0 -1
- package/dist/types-Y3fKQSAK.d.cts.map +0 -1
- package/dist/workspace-C-a3G_U0.cjs.map +0 -1
- package/dist/workspace-DaEX7FEg.mjs.map +0 -1
- package/dist/workspace-DbI580qS.mjs +0 -9
package/dist/index.cjs
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env -S npx tsx
|
|
2
2
|
const require_chunk = require('./chunk-CUT6urMc.cjs');
|
|
3
|
-
const require_discover = require('./discover-
|
|
4
|
-
const require_config = require('./config-
|
|
5
|
-
const require_workspace = require('./workspace-
|
|
3
|
+
const require_discover = require('./discover-DmW9Bscc.cjs');
|
|
4
|
+
const require_config = require('./config-RYR9j6EF.cjs');
|
|
5
|
+
const require_workspace = require('./workspace-DPV7FtAv.cjs');
|
|
6
6
|
const require_credentials = require('./credentials-BapzjyV5.cjs');
|
|
7
|
-
const require_credentials$1 = require('./credentials-
|
|
7
|
+
const require_credentials$1 = require('./credentials-DQRvPz2S.cjs');
|
|
8
8
|
const require_generator = require('./generator-CNzBw7g5.cjs');
|
|
9
9
|
const require_keystore = require('./keystore-C9rint2E.cjs');
|
|
10
10
|
const require_storage = require('./storage-CSNplaJn.cjs');
|
|
11
|
-
const require_openapi = require('./openapi-
|
|
11
|
+
const require_openapi = require('./openapi-DWLTsYLL.cjs');
|
|
12
12
|
const require_emit = require('./emit-CUD1BWls.cjs');
|
|
13
13
|
const require_dokploy_api = require('./dokploy-api-zqKe9lHH.cjs');
|
|
14
14
|
const require_encryption = require('./encryption-D2pL3tHH.cjs');
|
|
15
15
|
const require_CachedStateProvider = require('./CachedStateProvider-BEBl2wQT.cjs');
|
|
16
|
-
const
|
|
17
|
-
const
|
|
16
|
+
const require_openapi_react_query = require('./openapi-react-query-CmJ1xDbn.cjs');
|
|
17
|
+
const require_fullstack_secrets = require('./fullstack-secrets-DL8v0y4D.cjs');
|
|
18
18
|
require('./sync-0ILCb-1S.cjs');
|
|
19
|
-
const require_setup = require('./setup-
|
|
19
|
+
const require_setup = require('./setup-BsNR2LBG.cjs');
|
|
20
20
|
const node_fs = require_chunk.__toESM(require("node:fs"));
|
|
21
21
|
const node_path = require_chunk.__toESM(require("node:path"));
|
|
22
22
|
const __geekmidas_manifest = require_chunk.__toESM(require("@geekmidas/manifest"));
|
|
@@ -28,9 +28,9 @@ const node_fs_promises = require_chunk.__toESM(require("node:fs/promises"));
|
|
|
28
28
|
const node_child_process = require_chunk.__toESM(require("node:child_process"));
|
|
29
29
|
const chokidar = require_chunk.__toESM(require("chokidar"));
|
|
30
30
|
const yaml = require_chunk.__toESM(require("yaml"));
|
|
31
|
-
const pg = require_chunk.__toESM(require("pg"));
|
|
32
31
|
const node_crypto = require_chunk.__toESM(require("node:crypto"));
|
|
33
32
|
const __geekmidas_db_pg_roles = require_chunk.__toESM(require("@geekmidas/db/pg/roles"));
|
|
33
|
+
const pg = require_chunk.__toESM(require("pg"));
|
|
34
34
|
const __geekmidas_cache_postgres = require_chunk.__toESM(require("@geekmidas/cache/postgres"));
|
|
35
35
|
const __geekmidas_constructs_crons = require_chunk.__toESM(require("@geekmidas/constructs/crons"));
|
|
36
36
|
const __geekmidas_constructs_functions = require_chunk.__toESM(require("@geekmidas/constructs/functions"));
|
|
@@ -44,7 +44,7 @@ const prompts = require_chunk.__toESM(require("prompts"));
|
|
|
44
44
|
|
|
45
45
|
//#region package.json
|
|
46
46
|
var name = "@geekmidas/cli";
|
|
47
|
-
var version = "10.0.0-alpha.
|
|
47
|
+
var version = "10.0.0-alpha.12";
|
|
48
48
|
var description = "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs";
|
|
49
49
|
var private$1 = false;
|
|
50
50
|
var type = "module";
|
|
@@ -1849,7 +1849,7 @@ async function buildServer(config, context, provider, enableOpenApi, appRoot = p
|
|
|
1849
1849
|
...context,
|
|
1850
1850
|
storageDrivers: driversFor({
|
|
1851
1851
|
appRoot,
|
|
1852
|
-
cache: cacheBackendsIn(await declaredConstructs(config, appRoot), require_credentials$1.
|
|
1852
|
+
cache: cacheBackendsIn(await declaredConstructs(config, appRoot), require_credentials$1.cacheBackendFor(require_credentials$1.providerOf(config ?? {})))
|
|
1853
1853
|
})
|
|
1854
1854
|
};
|
|
1855
1855
|
const endpointGenerator = new require_openapi.EndpointGenerator();
|
|
@@ -2459,8 +2459,8 @@ function rootGkmConfig(workspace) {
|
|
|
2459
2459
|
const only = backends.length === 1 ? backends[0] : void 0;
|
|
2460
2460
|
const own = only ? require_workspace.getAppGkmConfig(workspace, only[0]) : void 0;
|
|
2461
2461
|
return own ?? {
|
|
2462
|
-
services: workspace.services,
|
|
2463
2462
|
stages: workspace.stages,
|
|
2463
|
+
...workspace.deploy ? { deploy: workspace.deploy } : {},
|
|
2464
2464
|
constructs: require_workspace.allConstructGlobs(workspace)
|
|
2465
2465
|
};
|
|
2466
2466
|
}
|
|
@@ -2478,8 +2478,7 @@ async function buildCommand(options) {
|
|
|
2478
2478
|
}
|
|
2479
2479
|
const config = loadedConfig.type !== "workspace" ? await require_config.loadConfig() : (0, node_path.resolve)(process.cwd()) === (0, node_path.resolve)(loadedConfig.workspace.root) ? rootGkmConfig(loadedConfig.workspace) : (await require_config.loadAppConfig()).gkmConfig;
|
|
2480
2480
|
const resolved = resolveProviders(config, options);
|
|
2481
|
-
const
|
|
2482
|
-
const cacheBackend = require_credentials$1.cacheBackendOf(backendConfig?.cache);
|
|
2481
|
+
const cacheBackend = require_credentials$1.cacheBackendFor(require_credentials$1.providerOf(loadedConfig.workspace));
|
|
2483
2482
|
const productionConfigFromGkm = getProductionConfigFromGkm(config);
|
|
2484
2483
|
const production = normalizeProductionConfig(options.production ?? false, productionConfigFromGkm);
|
|
2485
2484
|
if (production) logger$7.log(`🏭 Building for PRODUCTION`);
|
|
@@ -2491,16 +2490,6 @@ async function buildCommand(options) {
|
|
|
2491
2490
|
if (studio) logger$7.log(`🗄️ Studio enabled at ${studio.path}`);
|
|
2492
2491
|
const hooks = normalizeHooksConfig(config.hooks);
|
|
2493
2492
|
if (hooks) logger$7.log(`🪝 Server hooks enabled`);
|
|
2494
|
-
const services = config.docker?.compose?.services;
|
|
2495
|
-
const dockerServices = services ? Array.isArray(services) ? {
|
|
2496
|
-
postgres: services.includes("postgres"),
|
|
2497
|
-
redis: services.includes("redis"),
|
|
2498
|
-
rabbitmq: services.includes("rabbitmq")
|
|
2499
|
-
} : {
|
|
2500
|
-
postgres: Boolean(services.postgres),
|
|
2501
|
-
redis: Boolean(services.redis),
|
|
2502
|
-
rabbitmq: Boolean(services.rabbitmq)
|
|
2503
|
-
} : void 0;
|
|
2504
2493
|
const constructGlobs$1 = typeof config.constructs === "string" || Array.isArray(config.constructs) ? config.constructs : void 0;
|
|
2505
2494
|
const constructSources = {};
|
|
2506
2495
|
const declared = constructGlobs$1 ? await require_discover.discover({
|
|
@@ -2545,10 +2534,9 @@ async function buildCommand(options) {
|
|
|
2545
2534
|
} : studio,
|
|
2546
2535
|
hooks,
|
|
2547
2536
|
production,
|
|
2548
|
-
dockerServices,
|
|
2549
2537
|
constructGlobs: constructGlobs$1,
|
|
2550
2538
|
cacheBackend,
|
|
2551
|
-
emailBackend:
|
|
2539
|
+
emailBackend: require_discover.DEFAULT_EMAIL,
|
|
2552
2540
|
storageDrivers: driversFor({
|
|
2553
2541
|
appRoot: process.cwd(),
|
|
2554
2542
|
cache: cacheBackendsIn(declared, cacheBackend)
|
|
@@ -2640,7 +2628,7 @@ async function buildForProvider(provider, context, rootOutputDir, endpointGenera
|
|
|
2640
2628
|
let masterKey;
|
|
2641
2629
|
if (context.production?.bundle && !skipBundle) {
|
|
2642
2630
|
logger$7.log(`\n📦 Bundling production server...`);
|
|
2643
|
-
const { bundleServer } = await Promise.resolve().then(() => require("./bundler-
|
|
2631
|
+
const { bundleServer } = await Promise.resolve().then(() => require("./bundler-BMekgx7a.cjs"));
|
|
2644
2632
|
const allConstructs = [
|
|
2645
2633
|
...endpoints.map((e) => e.construct),
|
|
2646
2634
|
...functions.map((f) => f.construct),
|
|
@@ -2648,7 +2636,6 @@ async function buildForProvider(provider, context, rootOutputDir, endpointGenera
|
|
|
2648
2636
|
...subscribers.map((s) => s.construct),
|
|
2649
2637
|
...queues.map((q) => q.construct)
|
|
2650
2638
|
];
|
|
2651
|
-
const dockerServices = context.dockerServices;
|
|
2652
2639
|
const bundleResult = await bundleServer({
|
|
2653
2640
|
entryPoint: (0, node_path.join)(outputDir, "server.ts"),
|
|
2654
2641
|
outputDir: (0, node_path.join)(outputDir, "dist"),
|
|
@@ -2656,8 +2643,7 @@ async function buildForProvider(provider, context, rootOutputDir, endpointGenera
|
|
|
2656
2643
|
sourcemap: false,
|
|
2657
2644
|
external: context.production.external,
|
|
2658
2645
|
stage,
|
|
2659
|
-
constructs: allConstructs
|
|
2660
|
-
dockerServices
|
|
2646
|
+
constructs: allConstructs
|
|
2661
2647
|
});
|
|
2662
2648
|
masterKey = bundleResult.masterKey;
|
|
2663
2649
|
logger$7.log(`✅ Bundle complete: .gkm/server/dist/server.mjs`);
|
|
@@ -2734,71 +2720,6 @@ function turboFilters(workspace) {
|
|
|
2734
2720
|
};
|
|
2735
2721
|
}
|
|
2736
2722
|
/**
|
|
2737
|
-
* The directory turbo treats as the repo root: the nearest ancestor with a
|
|
2738
|
-
* `turbo.json`. Its own path is what `$TURBO_ROOT$` resolves to.
|
|
2739
|
-
*/
|
|
2740
|
-
function turboRoot(from) {
|
|
2741
|
-
let dir = (0, node_path.resolve)(from);
|
|
2742
|
-
for (;;) {
|
|
2743
|
-
if ((0, node_fs.existsSync)((0, node_path.join)(dir, "turbo.json"))) return dir;
|
|
2744
|
-
const parent = (0, node_path.dirname)(dir);
|
|
2745
|
-
if (parent === dir) return void 0;
|
|
2746
|
-
dir = parent;
|
|
2747
|
-
}
|
|
2748
|
-
}
|
|
2749
|
-
/**
|
|
2750
|
-
* Write each app's turbo task config, so a construct edit invalidates the build
|
|
2751
|
-
* that read it.
|
|
2752
|
-
*
|
|
2753
|
-
* Turbo hashes a package's own files. An app's constructs live *above* it, in
|
|
2754
|
-
* the workspace that declares them for everybody — so without this, editing a
|
|
2755
|
-
* database or a surface leaves every cached app build in place and the apps
|
|
2756
|
-
* keep serving a graph that no longer exists. It is a silent wrong answer,
|
|
2757
|
-
* which is the worst kind: the build succeeds and the output is stale.
|
|
2758
|
-
*
|
|
2759
|
-
* Generated rather than documented because the globs are already known here and
|
|
2760
|
-
* a hand-written copy is one more list to keep in step — the same reason the
|
|
2761
|
-
* apps themselves are no longer written down. A file that someone has since
|
|
2762
|
-
* edited by hand is left alone.
|
|
2763
|
-
*/
|
|
2764
|
-
async function writeTurboConfigs(workspace) {
|
|
2765
|
-
const root = turboRoot(workspace.root);
|
|
2766
|
-
if (!root) return;
|
|
2767
|
-
const globs = require_workspace.allConstructGlobs(workspace).map((glob) => `$TURBO_ROOT$/${(0, node_path.relative)(root, glob)}`);
|
|
2768
|
-
if (globs.length === 0) return;
|
|
2769
|
-
const configPath = (0, node_path.join)(workspace.root, "gkm.config.ts");
|
|
2770
|
-
const inputs = [
|
|
2771
|
-
"$TURBO_DEFAULT$",
|
|
2772
|
-
...globs,
|
|
2773
|
-
...(0, node_fs.existsSync)(configPath) ? [`$TURBO_ROOT$/${(0, node_path.relative)(root, configPath)}`] : []
|
|
2774
|
-
];
|
|
2775
|
-
for (const app of Object.values(workspace.apps)) {
|
|
2776
|
-
const dir = (0, node_path.join)(workspace.root, app.path);
|
|
2777
|
-
if (!(0, node_fs.existsSync)((0, node_path.join)(dir, "package.json"))) continue;
|
|
2778
|
-
const file = (0, node_path.join)(dir, "turbo.json");
|
|
2779
|
-
const existing = (0, node_fs.existsSync)(file) ? (0, node_fs.readFileSync)(file, "utf8") : void 0;
|
|
2780
|
-
if (existing && !existing.includes(GENERATED_BY_GKM)) continue;
|
|
2781
|
-
const content = `{
|
|
2782
|
-
"$schema": "https://turborepo.com/schema.json",
|
|
2783
|
-
${GENERATED_BY_GKM}
|
|
2784
|
-
"extends": ["//"],
|
|
2785
|
-
"tasks": {
|
|
2786
|
-
"build": {
|
|
2787
|
-
"inputs": [
|
|
2788
|
-
${inputs.map((glob) => `\t\t\t\t${JSON.stringify(glob)}`).join(",\n")}
|
|
2789
|
-
],
|
|
2790
|
-
"outputs": [${app.type === "backend" ? "\".gkm/**\"" : "\"dist/**\", \".next/**\", \"!.next/cache/**\""}]
|
|
2791
|
-
}
|
|
2792
|
-
}
|
|
2793
|
-
}
|
|
2794
|
-
`;
|
|
2795
|
-
if (existing === content) continue;
|
|
2796
|
-
await (0, node_fs_promises.writeFile)(file, content);
|
|
2797
|
-
}
|
|
2798
|
-
}
|
|
2799
|
-
/** Marks a turbo config as gkm's to rewrite. Edit the file and it is left alone. */
|
|
2800
|
-
const GENERATED_BY_GKM = "// Generated by `gkm build`. Delete this line to take ownership of the file.";
|
|
2801
|
-
/**
|
|
2802
2723
|
* Build all apps in a workspace using Turbo for dependency-ordered parallel builds.
|
|
2803
2724
|
* @internal Exported for testing
|
|
2804
2725
|
*/
|
|
@@ -2816,7 +2737,6 @@ async function workspaceBuildCommand(workspace, options) {
|
|
|
2816
2737
|
const pm = detectPackageManager$2();
|
|
2817
2738
|
logger$7.log(`\n📦 Using ${pm} with Turbo for parallel builds...\n`);
|
|
2818
2739
|
try {
|
|
2819
|
-
await writeTurboConfigs(workspace);
|
|
2820
2740
|
const { filters, unpackaged } = turboFilters(workspace);
|
|
2821
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.`);
|
|
2822
2742
|
const turboCommand = getTurboCommand(pm, filters);
|
|
@@ -3309,16 +3229,16 @@ var UnresolvedParent = class extends Error {
|
|
|
3309
3229
|
/** A cache with nowhere to live. */
|
|
3310
3230
|
var CacheNeedsAHome = class extends Error {
|
|
3311
3231
|
constructor(id, backend) {
|
|
3312
|
-
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}'.` : ""));
|
|
3313
3233
|
this.id = id;
|
|
3314
3234
|
this.backend = backend;
|
|
3315
3235
|
this.name = "CacheNeedsAHome";
|
|
3316
3236
|
}
|
|
3317
3237
|
};
|
|
3318
|
-
/** `
|
|
3238
|
+
/** A `'db'` cache (a server target's default) with more than one database. */
|
|
3319
3239
|
var CacheIsAmbiguous = class extends Error {
|
|
3320
3240
|
constructor(databases) {
|
|
3321
|
-
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().`);
|
|
3322
3242
|
this.databases = databases;
|
|
3323
3243
|
this.name = "CacheIsAmbiguous";
|
|
3324
3244
|
}
|
|
@@ -3334,7 +3254,7 @@ var SurfaceHasNoAddress = class extends Error {
|
|
|
3334
3254
|
/** A queue or topic on a backend this target has no primitive for. */
|
|
3335
3255
|
var UnprovisionableCarrier = class extends Error {
|
|
3336
3256
|
constructor(id, backend) {
|
|
3337
|
-
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.`);
|
|
3338
3258
|
this.id = id;
|
|
3339
3259
|
this.backend = backend;
|
|
3340
3260
|
this.name = "UnprovisionableCarrier";
|
|
@@ -3343,7 +3263,7 @@ var UnprovisionableCarrier = class extends Error {
|
|
|
3343
3263
|
/** A pg-boss carrier in an app that declares no database to host it. */
|
|
3344
3264
|
var BrokerNeedsADatabase = class extends Error {
|
|
3345
3265
|
constructor(id) {
|
|
3346
|
-
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.`);
|
|
3347
3267
|
this.id = id;
|
|
3348
3268
|
this.name = "BrokerNeedsADatabase";
|
|
3349
3269
|
}
|
|
@@ -3351,7 +3271,7 @@ var BrokerNeedsADatabase = class extends Error {
|
|
|
3351
3271
|
/** A bucket on a backend this target has no primitive for. */
|
|
3352
3272
|
var UnprovisionableBucket = class extends Error {
|
|
3353
3273
|
constructor(id, backend) {
|
|
3354
|
-
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.`);
|
|
3355
3275
|
this.id = id;
|
|
3356
3276
|
this.backend = backend;
|
|
3357
3277
|
this.name = "UnprovisionableBucket";
|
|
@@ -3389,9 +3309,9 @@ async function provisionDeclared(options) {
|
|
|
3389
3309
|
environmentId: options.environmentId,
|
|
3390
3310
|
stage: options.stage,
|
|
3391
3311
|
project: workspace.name,
|
|
3392
|
-
cache: require_credentials$1.
|
|
3393
|
-
|
|
3394
|
-
storage: require_credentials$1.
|
|
3312
|
+
cache: require_credentials$1.cacheBackendFor(require_credentials$1.providerOf(workspace)),
|
|
3313
|
+
events: require_credentials$1.eventsBackendFor(require_credentials$1.providerOf(workspace)),
|
|
3314
|
+
storage: require_credentials$1.storageBackendFor(require_credentials$1.providerOf(workspace)),
|
|
3395
3315
|
addresses: surfaceAddresses(workspace, manifest, options.appUrls),
|
|
3396
3316
|
...options.secrets ? { secrets: options.secrets } : {},
|
|
3397
3317
|
deferred: [],
|
|
@@ -4030,563 +3950,6 @@ function getPublicUrlArgNames(app) {
|
|
|
4030
3950
|
return app.dependencies.map((dep) => `${prefix}${dep.toUpperCase()}_URL`);
|
|
4031
3951
|
}
|
|
4032
3952
|
|
|
4033
|
-
//#endregion
|
|
4034
|
-
//#region src/docker/compose.ts
|
|
4035
|
-
/** Default Docker images for services */
|
|
4036
|
-
const DEFAULT_SERVICE_IMAGES = {
|
|
4037
|
-
postgres: "postgres",
|
|
4038
|
-
redis: "redis",
|
|
4039
|
-
rabbitmq: "rabbitmq",
|
|
4040
|
-
minio: "pgsty/minio",
|
|
4041
|
-
mailpit: "axllent/mailpit",
|
|
4042
|
-
localstack: "floci/floci"
|
|
4043
|
-
};
|
|
4044
|
-
/** Default Docker image versions for services */
|
|
4045
|
-
const DEFAULT_SERVICE_VERSIONS = {
|
|
4046
|
-
postgres: `${__geekmidas_manifest.DEFAULT_POSTGRES_VERSION}-alpine`,
|
|
4047
|
-
redis: "7-alpine",
|
|
4048
|
-
rabbitmq: "3-management-alpine",
|
|
4049
|
-
minio: "RELEASE.2026-08-04T00-00-00Z",
|
|
4050
|
-
mailpit: "latest",
|
|
4051
|
-
localstack: "latest"
|
|
4052
|
-
};
|
|
4053
|
-
/** Get the default full image reference for a service */
|
|
4054
|
-
function getDefaultImage(serviceName$1) {
|
|
4055
|
-
return `${DEFAULT_SERVICE_IMAGES[serviceName$1]}:${DEFAULT_SERVICE_VERSIONS[serviceName$1]}`;
|
|
4056
|
-
}
|
|
4057
|
-
/** Normalize services config to a consistent format - returns Map of service name to full image reference */
|
|
4058
|
-
function normalizeServices(services) {
|
|
4059
|
-
const result = /* @__PURE__ */ new Map();
|
|
4060
|
-
if (Array.isArray(services)) for (const name$1 of services) result.set(name$1, getDefaultImage(name$1));
|
|
4061
|
-
else for (const [name$1, config] of Object.entries(services)) {
|
|
4062
|
-
const serviceName$1 = name$1;
|
|
4063
|
-
if (config === true) result.set(serviceName$1, getDefaultImage(serviceName$1));
|
|
4064
|
-
else if (config && typeof config === "object") {
|
|
4065
|
-
const serviceConfig = config;
|
|
4066
|
-
if (serviceConfig.image) result.set(serviceName$1, serviceConfig.image);
|
|
4067
|
-
else {
|
|
4068
|
-
const version$1 = serviceConfig.version ?? DEFAULT_SERVICE_VERSIONS[serviceName$1];
|
|
4069
|
-
result.set(serviceName$1, `${DEFAULT_SERVICE_IMAGES[serviceName$1]}:${version$1}`);
|
|
4070
|
-
}
|
|
4071
|
-
}
|
|
4072
|
-
}
|
|
4073
|
-
return result;
|
|
4074
|
-
}
|
|
4075
|
-
/**
|
|
4076
|
-
* Generate docker-compose.yml for production deployment
|
|
4077
|
-
*/
|
|
4078
|
-
function generateDockerCompose(options) {
|
|
4079
|
-
const { imageName, registry, port, healthCheckPath, services } = options;
|
|
4080
|
-
const serviceMap = normalizeServices(services);
|
|
4081
|
-
const imageRef = registry ? `\${REGISTRY:-${registry}}/` : "";
|
|
4082
|
-
let yaml$1 = `# Use "gkm dev" or "gkm test" to start services.
|
|
4083
|
-
# Running "docker compose up" directly will not inject secrets or resolve ports.
|
|
4084
|
-
version: '3.8'
|
|
4085
|
-
|
|
4086
|
-
services:
|
|
4087
|
-
api:
|
|
4088
|
-
build:
|
|
4089
|
-
context: ../..
|
|
4090
|
-
dockerfile: .gkm/docker/Dockerfile
|
|
4091
|
-
image: ${imageRef}\${IMAGE_NAME:-${imageName}}:\${TAG:-latest}
|
|
4092
|
-
container_name: ${imageName}
|
|
4093
|
-
restart: unless-stopped
|
|
4094
|
-
ports:
|
|
4095
|
-
- "\${PORT:-${port}}:${port}"
|
|
4096
|
-
environment:
|
|
4097
|
-
- NODE_ENV=production
|
|
4098
|
-
`;
|
|
4099
|
-
if (serviceMap.has("postgres")) yaml$1 += ` - DATABASE_URL=\${DATABASE_URL:-postgresql://\${POSTGRES_USER:-postgres}:\${POSTGRES_PASSWORD:-postgres}@postgres:5432/\${POSTGRES_DB:-app}}
|
|
4100
|
-
`;
|
|
4101
|
-
if (serviceMap.has("redis")) yaml$1 += ` - REDIS_URL=\${REDIS_URL:-redis://redis:6379}
|
|
4102
|
-
`;
|
|
4103
|
-
if (serviceMap.has("rabbitmq")) yaml$1 += ` - RABBITMQ_URL=\${RABBITMQ_URL:-amqp://rabbitmq:5672}
|
|
4104
|
-
`;
|
|
4105
|
-
if (serviceMap.has("minio")) yaml$1 += ` - STORAGE_ENDPOINT=\${STORAGE_ENDPOINT:-http://minio:9000}
|
|
4106
|
-
- STORAGE_ACCESS_KEY_ID=\${STORAGE_ACCESS_KEY_ID:-${imageName}}
|
|
4107
|
-
- STORAGE_SECRET_ACCESS_KEY=\${STORAGE_SECRET_ACCESS_KEY:-${imageName}}
|
|
4108
|
-
- STORAGE_BUCKET=\${STORAGE_BUCKET:-${imageName}}
|
|
4109
|
-
- STORAGE_REGION=\${STORAGE_REGION:-eu-west-1}
|
|
4110
|
-
- STORAGE_FORCE_PATH_STYLE=true
|
|
4111
|
-
`;
|
|
4112
|
-
if (serviceMap.has("mailpit")) yaml$1 += ` - SMTP_HOST=\${SMTP_HOST:-mailpit}
|
|
4113
|
-
- SMTP_PORT=\${SMTP_PORT:-1025}
|
|
4114
|
-
- SMTP_USER=\${SMTP_USER:-${imageName}}
|
|
4115
|
-
- SMTP_PASS=\${SMTP_PASS:-${imageName}}
|
|
4116
|
-
- SMTP_SECURE=\${SMTP_SECURE:-false}
|
|
4117
|
-
- MAIL_FROM=\${MAIL_FROM:-noreply@localhost}
|
|
4118
|
-
`;
|
|
4119
|
-
if (serviceMap.has("localstack")) yaml$1 += ` - AWS_ACCESS_KEY_ID=\${AWS_ACCESS_KEY_ID:-localstack}
|
|
4120
|
-
- AWS_SECRET_ACCESS_KEY=\${AWS_SECRET_ACCESS_KEY:-localstack}
|
|
4121
|
-
- AWS_REGION=\${AWS_REGION:-us-east-1}
|
|
4122
|
-
- AWS_ENDPOINT_URL=\${AWS_ENDPOINT_URL:-http://localstack:4566}
|
|
4123
|
-
`;
|
|
4124
|
-
yaml$1 += ` healthcheck:
|
|
4125
|
-
test: ["CMD", "wget", "-q", "--spider", "http://localhost:${port}${healthCheckPath}"]
|
|
4126
|
-
interval: 30s
|
|
4127
|
-
timeout: 3s
|
|
4128
|
-
retries: 3
|
|
4129
|
-
`;
|
|
4130
|
-
if (serviceMap.size > 0) {
|
|
4131
|
-
yaml$1 += ` depends_on:
|
|
4132
|
-
`;
|
|
4133
|
-
for (const serviceName$1 of serviceMap.keys()) yaml$1 += ` ${serviceName$1}:
|
|
4134
|
-
condition: service_healthy
|
|
4135
|
-
`;
|
|
4136
|
-
}
|
|
4137
|
-
yaml$1 += ` networks:
|
|
4138
|
-
- app-network
|
|
4139
|
-
`;
|
|
4140
|
-
const postgresImage = serviceMap.get("postgres");
|
|
4141
|
-
if (postgresImage) yaml$1 += `
|
|
4142
|
-
postgres:
|
|
4143
|
-
image: ${postgresImage}
|
|
4144
|
-
container_name: postgres
|
|
4145
|
-
restart: unless-stopped
|
|
4146
|
-
environment:
|
|
4147
|
-
POSTGRES_USER: \${POSTGRES_USER:-postgres}
|
|
4148
|
-
POSTGRES_PASSWORD: \${POSTGRES_PASSWORD:-postgres}
|
|
4149
|
-
POSTGRES_DB: \${POSTGRES_DB:-app}
|
|
4150
|
-
volumes:
|
|
4151
|
-
- dbdata:/var/lib/postgresql
|
|
4152
|
-
healthcheck:
|
|
4153
|
-
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER"]
|
|
4154
|
-
interval: 5s
|
|
4155
|
-
timeout: 5s
|
|
4156
|
-
retries: 5
|
|
4157
|
-
networks:
|
|
4158
|
-
- app-network
|
|
4159
|
-
`;
|
|
4160
|
-
const redisImage = serviceMap.get("redis");
|
|
4161
|
-
if (redisImage) yaml$1 += `
|
|
4162
|
-
redis:
|
|
4163
|
-
image: ${redisImage}
|
|
4164
|
-
container_name: redis
|
|
4165
|
-
restart: unless-stopped
|
|
4166
|
-
volumes:
|
|
4167
|
-
- redis_data:/data
|
|
4168
|
-
healthcheck:
|
|
4169
|
-
test: ["CMD", "redis-cli", "ping"]
|
|
4170
|
-
interval: 5s
|
|
4171
|
-
timeout: 5s
|
|
4172
|
-
retries: 5
|
|
4173
|
-
networks:
|
|
4174
|
-
- app-network
|
|
4175
|
-
`;
|
|
4176
|
-
const rabbitmqImage = serviceMap.get("rabbitmq");
|
|
4177
|
-
if (rabbitmqImage) yaml$1 += `
|
|
4178
|
-
rabbitmq:
|
|
4179
|
-
image: ${rabbitmqImage}
|
|
4180
|
-
container_name: rabbitmq
|
|
4181
|
-
restart: unless-stopped
|
|
4182
|
-
environment:
|
|
4183
|
-
RABBITMQ_DEFAULT_USER: \${RABBITMQ_USER:-guest}
|
|
4184
|
-
RABBITMQ_DEFAULT_PASS: \${RABBITMQ_PASSWORD:-guest}
|
|
4185
|
-
ports:
|
|
4186
|
-
- "15672:15672" # Management UI
|
|
4187
|
-
volumes:
|
|
4188
|
-
- rabbitmq_data:/var/lib/rabbitmq
|
|
4189
|
-
healthcheck:
|
|
4190
|
-
test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"]
|
|
4191
|
-
interval: 10s
|
|
4192
|
-
timeout: 5s
|
|
4193
|
-
retries: 5
|
|
4194
|
-
networks:
|
|
4195
|
-
- app-network
|
|
4196
|
-
`;
|
|
4197
|
-
const minioImage = serviceMap.get("minio");
|
|
4198
|
-
if (minioImage) yaml$1 += `
|
|
4199
|
-
minio:
|
|
4200
|
-
image: ${minioImage}
|
|
4201
|
-
container_name: minio
|
|
4202
|
-
restart: unless-stopped
|
|
4203
|
-
entrypoint: sh
|
|
4204
|
-
command: -c 'mkdir -p /data/\${STORAGE_BUCKET:-${imageName}} && /usr/bin/docker-entrypoint.sh server --console-address ":9001" /data'
|
|
4205
|
-
environment:
|
|
4206
|
-
MINIO_ROOT_USER: \${STORAGE_ACCESS_KEY_ID:-${imageName}}
|
|
4207
|
-
MINIO_ROOT_PASSWORD: \${STORAGE_SECRET_ACCESS_KEY:-${imageName}}
|
|
4208
|
-
ports:
|
|
4209
|
-
- "\${MINIO_API_PORT:-9000}:9000"
|
|
4210
|
-
- "\${MINIO_CONSOLE_PORT:-9001}:9001"
|
|
4211
|
-
volumes:
|
|
4212
|
-
- minio_data:/data
|
|
4213
|
-
healthcheck:
|
|
4214
|
-
test: ["CMD", "mc", "ready", "local"]
|
|
4215
|
-
interval: 10s
|
|
4216
|
-
timeout: 5s
|
|
4217
|
-
retries: 5
|
|
4218
|
-
networks:
|
|
4219
|
-
- app-network
|
|
4220
|
-
`;
|
|
4221
|
-
const mailpitImage = serviceMap.get("mailpit");
|
|
4222
|
-
if (mailpitImage) yaml$1 += `
|
|
4223
|
-
mailpit:
|
|
4224
|
-
image: ${mailpitImage}
|
|
4225
|
-
container_name: mailpit
|
|
4226
|
-
restart: unless-stopped
|
|
4227
|
-
environment:
|
|
4228
|
-
MP_SMTP_AUTH: \${SMTP_USER:-${imageName}}:\${SMTP_PASS:-${imageName}}
|
|
4229
|
-
MP_SMTP_AUTH_ALLOW_INSECURE: true
|
|
4230
|
-
ports:
|
|
4231
|
-
- "\${MAILPIT_PORT:-8025}:8025" # Web UI / API
|
|
4232
|
-
- "\${SMTP_PORT:-1025}:1025" # SMTP
|
|
4233
|
-
healthcheck:
|
|
4234
|
-
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8025"]
|
|
4235
|
-
interval: 5s
|
|
4236
|
-
timeout: 5s
|
|
4237
|
-
retries: 5
|
|
4238
|
-
networks:
|
|
4239
|
-
- app-network
|
|
4240
|
-
`;
|
|
4241
|
-
const localstackImage = serviceMap.get("localstack");
|
|
4242
|
-
if (localstackImage) yaml$1 += `
|
|
4243
|
-
localstack:
|
|
4244
|
-
image: ${localstackImage}
|
|
4245
|
-
container_name: localstack
|
|
4246
|
-
restart: unless-stopped
|
|
4247
|
-
environment:
|
|
4248
|
-
AWS_DEFAULT_REGION: \${AWS_REGION:-us-east-1}
|
|
4249
|
-
AWS_ACCESS_KEY_ID: \${AWS_ACCESS_KEY_ID:-localstack}
|
|
4250
|
-
AWS_SECRET_ACCESS_KEY: \${AWS_SECRET_ACCESS_KEY:-localstack}
|
|
4251
|
-
ports:
|
|
4252
|
-
- "\${LOCALSTACK_PORT:-4566}:4566"
|
|
4253
|
-
volumes:
|
|
4254
|
-
- localstack_data:/var/lib/localstack
|
|
4255
|
-
healthcheck:
|
|
4256
|
-
test: ["CMD", "curl", "-f", "http://localhost:4566/_localstack/health"]
|
|
4257
|
-
interval: 10s
|
|
4258
|
-
timeout: 5s
|
|
4259
|
-
retries: 5
|
|
4260
|
-
networks:
|
|
4261
|
-
- app-network
|
|
4262
|
-
`;
|
|
4263
|
-
yaml$1 += `
|
|
4264
|
-
volumes:
|
|
4265
|
-
`;
|
|
4266
|
-
if (serviceMap.has("postgres")) yaml$1 += ` dbdata:
|
|
4267
|
-
`;
|
|
4268
|
-
if (serviceMap.has("redis")) yaml$1 += ` redis_data:
|
|
4269
|
-
`;
|
|
4270
|
-
if (serviceMap.has("rabbitmq")) yaml$1 += ` rabbitmq_data:
|
|
4271
|
-
`;
|
|
4272
|
-
if (serviceMap.has("minio")) yaml$1 += ` minio_data:
|
|
4273
|
-
`;
|
|
4274
|
-
if (serviceMap.has("localstack")) yaml$1 += ` localstack_data:
|
|
4275
|
-
`;
|
|
4276
|
-
yaml$1 += `
|
|
4277
|
-
networks:
|
|
4278
|
-
app-network:
|
|
4279
|
-
driver: bridge
|
|
4280
|
-
`;
|
|
4281
|
-
return yaml$1;
|
|
4282
|
-
}
|
|
4283
|
-
/**
|
|
4284
|
-
* Generate a minimal docker-compose.yml for API only
|
|
4285
|
-
*/
|
|
4286
|
-
function generateMinimalDockerCompose(options) {
|
|
4287
|
-
const { imageName, registry, port, healthCheckPath } = options;
|
|
4288
|
-
const imageRef = registry ? `\${REGISTRY:-${registry}}/` : "";
|
|
4289
|
-
return `# Use "gkm dev" or "gkm test" to start services.
|
|
4290
|
-
# Running "docker compose up" directly will not inject secrets or resolve ports.
|
|
4291
|
-
version: '3.8'
|
|
4292
|
-
|
|
4293
|
-
services:
|
|
4294
|
-
api:
|
|
4295
|
-
build:
|
|
4296
|
-
context: ../..
|
|
4297
|
-
dockerfile: .gkm/docker/Dockerfile
|
|
4298
|
-
image: ${imageRef}\${IMAGE_NAME:-${imageName}}:\${TAG:-latest}
|
|
4299
|
-
container_name: ${imageName}
|
|
4300
|
-
restart: unless-stopped
|
|
4301
|
-
ports:
|
|
4302
|
-
- "\${PORT:-${port}}:${port}"
|
|
4303
|
-
environment:
|
|
4304
|
-
- NODE_ENV=production
|
|
4305
|
-
healthcheck:
|
|
4306
|
-
test: ["CMD", "wget", "-q", "--spider", "http://localhost:${port}${healthCheckPath}"]
|
|
4307
|
-
interval: 30s
|
|
4308
|
-
timeout: 3s
|
|
4309
|
-
retries: 3
|
|
4310
|
-
networks:
|
|
4311
|
-
- app-network
|
|
4312
|
-
|
|
4313
|
-
networks:
|
|
4314
|
-
app-network:
|
|
4315
|
-
driver: bridge
|
|
4316
|
-
`;
|
|
4317
|
-
}
|
|
4318
|
-
/**
|
|
4319
|
-
* Generate docker-compose.yml for a workspace with all apps as services.
|
|
4320
|
-
* Apps can communicate with each other via service names.
|
|
4321
|
-
* @internal Exported for testing
|
|
4322
|
-
*/
|
|
4323
|
-
function generateWorkspaceCompose(workspace, options = {}) {
|
|
4324
|
-
const { registry } = options;
|
|
4325
|
-
const apps = Object.entries(workspace.apps);
|
|
4326
|
-
const services = workspace.services;
|
|
4327
|
-
const containers = new Set(options.containers ?? []);
|
|
4328
|
-
const hasPostgres = containers.has("postgres");
|
|
4329
|
-
const hasRedis = containers.has("redis") || containers.has("redis-http");
|
|
4330
|
-
const hasMail = containers.has("mailpit");
|
|
4331
|
-
const hasMinio = containers.has("minio");
|
|
4332
|
-
const hasLocalStack = containers.has("localstack");
|
|
4333
|
-
const hasRabbitMQ = containers.has("rabbitmq");
|
|
4334
|
-
const eventsBackend = services.events;
|
|
4335
|
-
const postgresImage = getInfraServiceImage("postgres", services.images?.postgres);
|
|
4336
|
-
const redisImage = getInfraServiceImage("redis", services.images?.redis);
|
|
4337
|
-
const minioImage = getInfraServiceImage("minio", services.images?.minio);
|
|
4338
|
-
let yaml$1 = `# Docker Compose for ${workspace.name} workspace
|
|
4339
|
-
# Use "gkm dev" or "gkm test" to start services.
|
|
4340
|
-
# Running "docker compose up" directly will not inject secrets or resolve ports.
|
|
4341
|
-
|
|
4342
|
-
services:
|
|
4343
|
-
`;
|
|
4344
|
-
for (const [appName, app] of apps) {
|
|
4345
|
-
if (app.type === "mobile") continue;
|
|
4346
|
-
yaml$1 += generateAppService(appName, app, apps, {
|
|
4347
|
-
registry,
|
|
4348
|
-
projectName: workspace.name,
|
|
4349
|
-
hasPostgres,
|
|
4350
|
-
hasRedis,
|
|
4351
|
-
hasMinio,
|
|
4352
|
-
hasMail,
|
|
4353
|
-
eventsBackend
|
|
4354
|
-
});
|
|
4355
|
-
}
|
|
4356
|
-
if (hasPostgres) yaml$1 += `
|
|
4357
|
-
postgres:
|
|
4358
|
-
image: ${postgresImage}
|
|
4359
|
-
container_name: ${workspace.name}-postgres
|
|
4360
|
-
restart: unless-stopped
|
|
4361
|
-
environment:
|
|
4362
|
-
POSTGRES_USER: \${POSTGRES_USER:-postgres}
|
|
4363
|
-
POSTGRES_PASSWORD: \${POSTGRES_PASSWORD:-postgres}
|
|
4364
|
-
POSTGRES_DB: \${POSTGRES_DB:-app}
|
|
4365
|
-
volumes:
|
|
4366
|
-
- dbdata:/var/lib/postgresql
|
|
4367
|
-
healthcheck:
|
|
4368
|
-
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER"]
|
|
4369
|
-
interval: 5s
|
|
4370
|
-
timeout: 5s
|
|
4371
|
-
retries: 5
|
|
4372
|
-
networks:
|
|
4373
|
-
- workspace-network
|
|
4374
|
-
`;
|
|
4375
|
-
if (hasRedis) yaml$1 += `
|
|
4376
|
-
redis:
|
|
4377
|
-
image: ${redisImage}
|
|
4378
|
-
container_name: ${workspace.name}-redis
|
|
4379
|
-
restart: unless-stopped
|
|
4380
|
-
volumes:
|
|
4381
|
-
- redis_data:/data
|
|
4382
|
-
healthcheck:
|
|
4383
|
-
test: ["CMD", "redis-cli", "ping"]
|
|
4384
|
-
interval: 5s
|
|
4385
|
-
timeout: 5s
|
|
4386
|
-
retries: 5
|
|
4387
|
-
networks:
|
|
4388
|
-
- workspace-network
|
|
4389
|
-
`;
|
|
4390
|
-
if (hasMail) yaml$1 += `
|
|
4391
|
-
mailpit:
|
|
4392
|
-
image: axllent/mailpit:latest
|
|
4393
|
-
container_name: ${workspace.name}-mailpit
|
|
4394
|
-
restart: unless-stopped
|
|
4395
|
-
environment:
|
|
4396
|
-
MP_SMTP_AUTH: \${SMTP_USER:-${workspace.name}}:\${SMTP_PASS:-${workspace.name}}
|
|
4397
|
-
MP_SMTP_AUTH_ALLOW_INSECURE: true
|
|
4398
|
-
ports:
|
|
4399
|
-
- "\${MAILPIT_PORT:-8025}:8025" # Web UI / API
|
|
4400
|
-
- "\${SMTP_PORT:-1025}:1025" # SMTP
|
|
4401
|
-
healthcheck:
|
|
4402
|
-
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8025"]
|
|
4403
|
-
interval: 5s
|
|
4404
|
-
timeout: 5s
|
|
4405
|
-
retries: 5
|
|
4406
|
-
networks:
|
|
4407
|
-
- workspace-network
|
|
4408
|
-
`;
|
|
4409
|
-
if (hasMinio) yaml$1 += `
|
|
4410
|
-
minio:
|
|
4411
|
-
image: ${minioImage}
|
|
4412
|
-
container_name: ${workspace.name}-minio
|
|
4413
|
-
restart: unless-stopped
|
|
4414
|
-
entrypoint: sh
|
|
4415
|
-
command: -c 'mkdir -p /data/\${STORAGE_BUCKET:-${workspace.name}} && /usr/bin/docker-entrypoint.sh server --console-address ":9001" /data'
|
|
4416
|
-
environment:
|
|
4417
|
-
MINIO_ROOT_USER: \${STORAGE_ACCESS_KEY_ID:-${workspace.name}}
|
|
4418
|
-
MINIO_ROOT_PASSWORD: \${STORAGE_SECRET_ACCESS_KEY:-${workspace.name}}
|
|
4419
|
-
ports:
|
|
4420
|
-
- "\${MINIO_API_PORT:-9000}:9000"
|
|
4421
|
-
- "\${MINIO_CONSOLE_PORT:-9001}:9001"
|
|
4422
|
-
volumes:
|
|
4423
|
-
- minio_data:/data
|
|
4424
|
-
healthcheck:
|
|
4425
|
-
test: ["CMD", "mc", "ready", "local"]
|
|
4426
|
-
interval: 10s
|
|
4427
|
-
timeout: 5s
|
|
4428
|
-
retries: 5
|
|
4429
|
-
networks:
|
|
4430
|
-
- workspace-network
|
|
4431
|
-
`;
|
|
4432
|
-
if (hasLocalStack) yaml$1 += `
|
|
4433
|
-
localstack:
|
|
4434
|
-
image: floci/floci:latest
|
|
4435
|
-
container_name: ${workspace.name}-localstack
|
|
4436
|
-
restart: unless-stopped
|
|
4437
|
-
environment:
|
|
4438
|
-
AWS_DEFAULT_REGION: \${AWS_REGION:-us-east-1}
|
|
4439
|
-
AWS_ACCESS_KEY_ID: \${AWS_ACCESS_KEY_ID:-localstack}
|
|
4440
|
-
AWS_SECRET_ACCESS_KEY: \${AWS_SECRET_ACCESS_KEY:-localstack}
|
|
4441
|
-
ports:
|
|
4442
|
-
- "\${LOCALSTACK_PORT:-4566}:4566"
|
|
4443
|
-
volumes:
|
|
4444
|
-
- localstack_data:/var/lib/localstack
|
|
4445
|
-
healthcheck:
|
|
4446
|
-
test: ["CMD", "curl", "-f", "http://localhost:4566/_localstack/health"]
|
|
4447
|
-
interval: 10s
|
|
4448
|
-
timeout: 5s
|
|
4449
|
-
retries: 5
|
|
4450
|
-
networks:
|
|
4451
|
-
- workspace-network
|
|
4452
|
-
`;
|
|
4453
|
-
if (hasRabbitMQ) yaml$1 += `
|
|
4454
|
-
rabbitmq:
|
|
4455
|
-
image: rabbitmq:3-management-alpine
|
|
4456
|
-
container_name: ${workspace.name}-rabbitmq
|
|
4457
|
-
restart: unless-stopped
|
|
4458
|
-
environment:
|
|
4459
|
-
RABBITMQ_DEFAULT_USER: \${RABBITMQ_USER:-guest}
|
|
4460
|
-
RABBITMQ_DEFAULT_PASS: \${RABBITMQ_PASSWORD:-guest}
|
|
4461
|
-
ports:
|
|
4462
|
-
- "\${RABBITMQ_HOST_PORT:-5672}:5672"
|
|
4463
|
-
- "\${RABBITMQ_MGMT_HOST_PORT:-15672}:15672"
|
|
4464
|
-
volumes:
|
|
4465
|
-
- rabbitmq_data:/var/lib/rabbitmq
|
|
4466
|
-
healthcheck:
|
|
4467
|
-
test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"]
|
|
4468
|
-
interval: 10s
|
|
4469
|
-
timeout: 5s
|
|
4470
|
-
retries: 5
|
|
4471
|
-
networks:
|
|
4472
|
-
- workspace-network
|
|
4473
|
-
`;
|
|
4474
|
-
yaml$1 += `
|
|
4475
|
-
volumes:
|
|
4476
|
-
`;
|
|
4477
|
-
if (hasPostgres) yaml$1 += ` dbdata:
|
|
4478
|
-
`;
|
|
4479
|
-
if (hasRedis) yaml$1 += ` redis_data:
|
|
4480
|
-
`;
|
|
4481
|
-
if (hasMinio) yaml$1 += ` minio_data:
|
|
4482
|
-
`;
|
|
4483
|
-
if (hasLocalStack) yaml$1 += ` localstack_data:
|
|
4484
|
-
`;
|
|
4485
|
-
if (hasRabbitMQ) yaml$1 += ` rabbitmq_data:
|
|
4486
|
-
`;
|
|
4487
|
-
yaml$1 += `
|
|
4488
|
-
networks:
|
|
4489
|
-
workspace-network:
|
|
4490
|
-
driver: bridge
|
|
4491
|
-
`;
|
|
4492
|
-
return yaml$1;
|
|
4493
|
-
}
|
|
4494
|
-
/**
|
|
4495
|
-
* Get infrastructure service image with version.
|
|
4496
|
-
*/
|
|
4497
|
-
function getInfraServiceImage(serviceName$1, pin) {
|
|
4498
|
-
const defaults = {
|
|
4499
|
-
postgres: "postgres:18-alpine",
|
|
4500
|
-
redis: "redis:7-alpine",
|
|
4501
|
-
minio: "pgsty/minio:RELEASE.2026-08-04T00-00-00Z"
|
|
4502
|
-
};
|
|
4503
|
-
return pin || defaults[serviceName$1];
|
|
4504
|
-
}
|
|
4505
|
-
/**
|
|
4506
|
-
* Generate a service definition for an app.
|
|
4507
|
-
*/
|
|
4508
|
-
function generateAppService(appName, app, allApps, options) {
|
|
4509
|
-
const { registry, projectName, hasPostgres, hasRedis, hasMinio, hasMail, eventsBackend } = options;
|
|
4510
|
-
const imageRef = registry ? `\${REGISTRY:-${registry}}/` : "";
|
|
4511
|
-
const healthCheckPath = app.type === "web" ? "/" : "/health";
|
|
4512
|
-
const healthCheckCmd = app.type === "web" ? `["CMD", "wget", "-q", "--spider", "http://localhost:${app.port}/"]` : `["CMD", "wget", "-q", "--spider", "http://localhost:${app.port}${healthCheckPath}"]`;
|
|
4513
|
-
let yaml$1 = `
|
|
4514
|
-
${appName}:
|
|
4515
|
-
build:
|
|
4516
|
-
context: .
|
|
4517
|
-
dockerfile: .gkm/docker/Dockerfile.${appName}
|
|
4518
|
-
image: ${imageRef}\${${appName.toUpperCase()}_IMAGE:-${appName}}:\${TAG:-latest}
|
|
4519
|
-
container_name: ${appName}
|
|
4520
|
-
restart: unless-stopped
|
|
4521
|
-
ports:
|
|
4522
|
-
- "\${${appName.toUpperCase()}_PORT:-${app.port}}:${app.port}"
|
|
4523
|
-
environment:
|
|
4524
|
-
- NODE_ENV=production
|
|
4525
|
-
- PORT=${app.port}
|
|
4526
|
-
`;
|
|
4527
|
-
for (const dep of app.dependencies) {
|
|
4528
|
-
const depApp = allApps.find(([name$1]) => name$1 === dep)?.[1];
|
|
4529
|
-
if (depApp) yaml$1 += ` - ${dep.toUpperCase()}_URL=http://${dep}:${depApp.port}
|
|
4530
|
-
`;
|
|
4531
|
-
}
|
|
4532
|
-
if (app.type === "backend") {
|
|
4533
|
-
if (hasPostgres) yaml$1 += ` - DATABASE_URL=\${DATABASE_URL:-postgresql://\${POSTGRES_USER:-postgres}:\${POSTGRES_PASSWORD:-postgres}@postgres:5432/\${POSTGRES_DB:-app}}
|
|
4534
|
-
`;
|
|
4535
|
-
if (hasRedis) yaml$1 += ` - REDIS_URL=\${REDIS_URL:-redis://redis:6379}
|
|
4536
|
-
`;
|
|
4537
|
-
if (hasMinio) yaml$1 += ` - STORAGE_ENDPOINT=\${STORAGE_ENDPOINT:-http://minio:9000}
|
|
4538
|
-
- STORAGE_ACCESS_KEY_ID=\${STORAGE_ACCESS_KEY_ID:-${projectName}}
|
|
4539
|
-
- STORAGE_SECRET_ACCESS_KEY=\${STORAGE_SECRET_ACCESS_KEY:-${projectName}}
|
|
4540
|
-
- STORAGE_BUCKET=\${STORAGE_BUCKET:-${projectName}}
|
|
4541
|
-
- STORAGE_REGION=\${STORAGE_REGION:-eu-west-1}
|
|
4542
|
-
- STORAGE_FORCE_PATH_STYLE=true
|
|
4543
|
-
`;
|
|
4544
|
-
if (hasMail) yaml$1 += ` - SMTP_HOST=\${SMTP_HOST:-mailpit}
|
|
4545
|
-
- SMTP_PORT=\${SMTP_PORT:-1025}
|
|
4546
|
-
- SMTP_USER=\${SMTP_USER:-${projectName}}
|
|
4547
|
-
- SMTP_PASS=\${SMTP_PASS:-${projectName}}
|
|
4548
|
-
- SMTP_SECURE=\${SMTP_SECURE:-false}
|
|
4549
|
-
- MAIL_FROM=\${MAIL_FROM:-noreply@localhost}
|
|
4550
|
-
`;
|
|
4551
|
-
if (eventsBackend) {
|
|
4552
|
-
yaml$1 += ` - EVENT_PUBLISHER_CONNECTION_STRING=\${EVENT_PUBLISHER_CONNECTION_STRING}
|
|
4553
|
-
- EVENT_SUBSCRIBER_CONNECTION_STRING=\${EVENT_SUBSCRIBER_CONNECTION_STRING}
|
|
4554
|
-
`;
|
|
4555
|
-
if (eventsBackend === "sns") yaml$1 += ` - AWS_ACCESS_KEY_ID=\${AWS_ACCESS_KEY_ID:-localstack}
|
|
4556
|
-
- AWS_SECRET_ACCESS_KEY=\${AWS_SECRET_ACCESS_KEY:-localstack}
|
|
4557
|
-
- AWS_REGION=\${AWS_REGION:-us-east-1}
|
|
4558
|
-
- AWS_ENDPOINT_URL=\${AWS_ENDPOINT_URL:-http://localstack:4566}
|
|
4559
|
-
`;
|
|
4560
|
-
}
|
|
4561
|
-
}
|
|
4562
|
-
yaml$1 += ` healthcheck:
|
|
4563
|
-
test: ${healthCheckCmd}
|
|
4564
|
-
interval: 30s
|
|
4565
|
-
timeout: 3s
|
|
4566
|
-
retries: 3
|
|
4567
|
-
`;
|
|
4568
|
-
const dependencies$1 = [...app.dependencies];
|
|
4569
|
-
if (app.type === "backend") {
|
|
4570
|
-
if (hasPostgres) dependencies$1.push("postgres");
|
|
4571
|
-
if (hasRedis) dependencies$1.push("redis");
|
|
4572
|
-
if (hasMinio) dependencies$1.push("minio");
|
|
4573
|
-
if (hasMail) dependencies$1.push("mailpit");
|
|
4574
|
-
if (eventsBackend === "sns") dependencies$1.push("localstack");
|
|
4575
|
-
if (eventsBackend === "rabbitmq") dependencies$1.push("rabbitmq");
|
|
4576
|
-
}
|
|
4577
|
-
if (dependencies$1.length > 0) {
|
|
4578
|
-
yaml$1 += ` depends_on:
|
|
4579
|
-
`;
|
|
4580
|
-
for (const dep of dependencies$1) yaml$1 += ` ${dep}:
|
|
4581
|
-
condition: service_healthy
|
|
4582
|
-
`;
|
|
4583
|
-
}
|
|
4584
|
-
yaml$1 += ` networks:
|
|
4585
|
-
- workspace-network
|
|
4586
|
-
`;
|
|
4587
|
-
return yaml$1;
|
|
4588
|
-
}
|
|
4589
|
-
|
|
4590
3953
|
//#endregion
|
|
4591
3954
|
//#region src/docker/templates.ts
|
|
4592
3955
|
const LOCKFILES = [
|
|
@@ -5017,8 +4380,7 @@ function resolveDockerConfig(config) {
|
|
|
5017
4380
|
registry: docker.registry ?? "",
|
|
5018
4381
|
imageName: docker.imageName ?? defaultImageName,
|
|
5019
4382
|
baseImage: docker.baseImage ?? "node:22-alpine",
|
|
5020
|
-
port: docker.port ?? 3e3
|
|
5021
|
-
compose: docker.compose
|
|
4383
|
+
port: docker.port ?? 3e3
|
|
5022
4384
|
};
|
|
5023
4385
|
}
|
|
5024
4386
|
/**
|
|
@@ -5460,7 +4822,7 @@ async function dockerCommand(options) {
|
|
|
5460
4822
|
const loadedConfig = await require_config.loadWorkspaceConfig();
|
|
5461
4823
|
if (loadedConfig.type === "workspace") {
|
|
5462
4824
|
logger$5.log("📦 Detected workspace configuration");
|
|
5463
|
-
return workspaceDockerCommand(loadedConfig.workspace
|
|
4825
|
+
return workspaceDockerCommand(loadedConfig.workspace);
|
|
5464
4826
|
}
|
|
5465
4827
|
const config = await require_config.loadConfig();
|
|
5466
4828
|
const dockerConfig = resolveDockerConfig(config);
|
|
@@ -5505,18 +4867,7 @@ async function dockerCommand(options) {
|
|
|
5505
4867
|
const dockerfilePath = (0, node_path.join)(dockerDir, "Dockerfile");
|
|
5506
4868
|
await (0, node_fs_promises.writeFile)(dockerfilePath, dockerfile);
|
|
5507
4869
|
logger$5.log(`Generated: .gkm/docker/Dockerfile (${dockerMode}, ${packageManager})`);
|
|
5508
|
-
const
|
|
5509
|
-
imageName: dockerConfig.imageName,
|
|
5510
|
-
registry: options.registry ?? dockerConfig.registry,
|
|
5511
|
-
port: dockerConfig.port,
|
|
5512
|
-
healthCheckPath,
|
|
5513
|
-
services: dockerConfig.compose?.services ?? {}
|
|
5514
|
-
};
|
|
5515
|
-
const hasServices = Array.isArray(composeOptions.services) ? composeOptions.services.length > 0 : Object.keys(composeOptions.services).length > 0;
|
|
5516
|
-
const dockerCompose = hasServices ? generateDockerCompose(composeOptions) : generateMinimalDockerCompose(composeOptions);
|
|
5517
|
-
const composePath = (0, node_path.join)(dockerDir, "docker-compose.yml");
|
|
5518
|
-
await (0, node_fs_promises.writeFile)(composePath, dockerCompose);
|
|
5519
|
-
logger$5.log("Generated: .gkm/docker/docker-compose.yml");
|
|
4870
|
+
const composePath = await writeConstructsCompose(loadedConfig.workspace);
|
|
5520
4871
|
const dockerignore = generateDockerignore();
|
|
5521
4872
|
const dockerignorePath = (0, node_path.join)(process.cwd(), ".dockerignore");
|
|
5522
4873
|
await (0, node_fs_promises.writeFile)(dockerignorePath, dockerignore);
|
|
@@ -5621,7 +4972,7 @@ function getAppPackageName(appPath) {
|
|
|
5621
4972
|
* Generate Dockerfiles for all apps in a workspace.
|
|
5622
4973
|
* @internal Exported for testing
|
|
5623
4974
|
*/
|
|
5624
|
-
async function workspaceDockerCommand(workspace
|
|
4975
|
+
async function workspaceDockerCommand(workspace) {
|
|
5625
4976
|
const results = [];
|
|
5626
4977
|
const apps = Object.entries(workspace.apps);
|
|
5627
4978
|
logger$5.log(`\n🐳 Generating Dockerfiles for workspace: ${workspace.name}`);
|
|
@@ -5696,27 +5047,41 @@ async function workspaceDockerCommand(workspace, options) {
|
|
|
5696
5047
|
const dockerignorePath = (0, node_path.join)(workspace.root, ".dockerignore");
|
|
5697
5048
|
await (0, node_fs_promises.writeFile)(dockerignorePath, dockerignore);
|
|
5698
5049
|
logger$5.log(`\n Generated: .dockerignore (workspace root)`);
|
|
5699
|
-
const
|
|
5700
|
-
|
|
5701
|
-
containers: await require_credentials$1.derivedContainers(workspace, workspace.stages.local)
|
|
5702
|
-
});
|
|
5703
|
-
const composePath = (0, node_path.join)(dockerDir, "docker-compose.yml");
|
|
5704
|
-
await (0, node_fs_promises.writeFile)(composePath, dockerCompose);
|
|
5705
|
-
logger$5.log(` Generated: .gkm/docker/docker-compose.yml`);
|
|
5706
|
-
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) + ${require_credentials$1.COMPOSE_PATH}`);
|
|
5707
5052
|
logger$5.log("\n📋 Build commands:");
|
|
5708
5053
|
for (const result of results) {
|
|
5709
5054
|
const icon = result.type === "backend" ? "⚙️" : result.type === "mobile" ? "📱" : "🌐";
|
|
5710
5055
|
logger$5.log(` ${icon} docker build -f .gkm/docker/Dockerfile.${result.appName} -t ${result.imageName} .`);
|
|
5711
5056
|
}
|
|
5712
|
-
|
|
5713
|
-
logger$5.log(" docker compose -f .gkm/docker/docker-compose.yml up --build");
|
|
5057
|
+
printRunInstructions(workspace);
|
|
5714
5058
|
return {
|
|
5715
5059
|
apps: results,
|
|
5716
5060
|
dockerCompose: composePath,
|
|
5717
5061
|
dockerignore: dockerignorePath
|
|
5718
5062
|
};
|
|
5719
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 require_credentials$1.reconcileWorkspace(workspace, {
|
|
5073
|
+
stage: workspace.stages.local,
|
|
5074
|
+
start: false
|
|
5075
|
+
});
|
|
5076
|
+
logger$5.log(` Generated: ${require_credentials$1.COMPOSE_PATH} (project root)`);
|
|
5077
|
+
return (0, node_path.join)(workspace.root, require_credentials$1.COMPOSE_PATH);
|
|
5078
|
+
}
|
|
5079
|
+
function printRunInstructions(workspace) {
|
|
5080
|
+
const project = (0, node_fs.existsSync)((0, node_path.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 ${require_credentials$1.COMPOSE_PATH}${project} --profile apps up --build`);
|
|
5084
|
+
}
|
|
5720
5085
|
|
|
5721
5086
|
//#endregion
|
|
5722
5087
|
//#region src/deploy/docker.ts
|
|
@@ -7698,7 +7063,8 @@ the stale one is always the copy nobody opened.
|
|
|
7698
7063
|
`;
|
|
7699
7064
|
}
|
|
7700
7065
|
function agentsContent(options, template) {
|
|
7701
|
-
const { name: name$1, monorepo,
|
|
7066
|
+
const { name: name$1, monorepo, telescope, studio, packageManager } = options;
|
|
7067
|
+
const { database } = options.constructs;
|
|
7702
7068
|
const constructsGlob = monorepo ? WORKSPACE_CONSTRUCTS_GLOB : CONSTRUCTS_GLOB;
|
|
7703
7069
|
const isWorker = template.name === "worker";
|
|
7704
7070
|
const isServerless = template.name === "serverless";
|
|
@@ -7764,7 +7130,7 @@ ${run} gkm secrets:show
|
|
|
7764
7130
|
environment and fail in a way that looks like a config bug. Go through
|
|
7765
7131
|
\`gkm exec --\`, which is what injects them.`);
|
|
7766
7132
|
if (!isWorker) sections.push(endpointSection(options));
|
|
7767
|
-
if (isWorker || isServerless
|
|
7133
|
+
if (isWorker || isServerless) sections.push(backgroundSection(template));
|
|
7768
7134
|
if (database) sections.push(databaseSection());
|
|
7769
7135
|
sections.push(`## Environment
|
|
7770
7136
|
|
|
@@ -7812,7 +7178,8 @@ reaches — and neither shows up until something reads the graph.`);
|
|
|
7812
7178
|
return `${sections.join("\n\n")}\n`;
|
|
7813
7179
|
}
|
|
7814
7180
|
function layoutBlock(options, template) {
|
|
7815
|
-
const { monorepo, apiPath
|
|
7181
|
+
const { monorepo, apiPath } = options;
|
|
7182
|
+
const { database } = options.constructs;
|
|
7816
7183
|
const isWorker = template.name === "worker";
|
|
7817
7184
|
if (monorepo) return `constructs/ # every construct: the surface, the database, topics
|
|
7818
7185
|
${apiPath}/ # the API app
|
|
@@ -7828,7 +7195,7 @@ gkm.config.ts # name, constructs glob, secrets`;
|
|
|
7828
7195
|
return lines.join("\n");
|
|
7829
7196
|
}
|
|
7830
7197
|
function endpointSection(options) {
|
|
7831
|
-
const { database } = options;
|
|
7198
|
+
const { database } = options.constructs;
|
|
7832
7199
|
return `## Adding an endpoint
|
|
7833
7200
|
|
|
7834
7201
|
An endpoint is built **from the surface that serves it**. That is where its
|
|
@@ -7984,26 +7351,110 @@ Run tests through \`gkm test\`, not \`vitest\` directly${database ? " — it is
|
|
|
7984
7351
|
//#endregion
|
|
7985
7352
|
//#region src/init/dependencies.ts
|
|
7986
7353
|
/**
|
|
7987
|
-
*
|
|
7354
|
+
* Every third-party range `gkm init` writes, in one place.
|
|
7988
7355
|
*
|
|
7989
|
-
*
|
|
7990
|
-
*
|
|
7991
|
-
*
|
|
7992
|
-
*
|
|
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.
|
|
7993
7362
|
*
|
|
7994
7363
|
* Hand-maintained, unlike `versions.ts`, which `sync-versions` rewrites whole.
|
|
7364
|
+
* Current as of 2026-09-27, inside every `@geekmidas` peer range.
|
|
7995
7365
|
*/
|
|
7996
7366
|
const DEPENDENCY_VERSIONS = {
|
|
7997
|
-
"
|
|
7998
|
-
|
|
7999
|
-
hono: "~
|
|
7367
|
+
"better-auth": "~1.7.6",
|
|
7368
|
+
hono: "~4.13.9",
|
|
7369
|
+
"@hono/node-server": "~1.19.17",
|
|
8000
7370
|
kysely: "~0.29.6",
|
|
8001
7371
|
"kysely-ctl": "~0.21.0",
|
|
8002
7372
|
pg: "~8.23.0",
|
|
8003
7373
|
pino: "~10.3.1",
|
|
7374
|
+
zod: "~4.6.5",
|
|
8004
7375
|
sst: "~4.17.1",
|
|
8005
|
-
|
|
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"
|
|
8006
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"
|
|
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`;
|
|
8007
7458
|
|
|
8008
7459
|
//#endregion
|
|
8009
7460
|
//#region src/init/versions.ts
|
|
@@ -8027,26 +7478,26 @@ const CLI_VERSION = `~${pkg.version}`;
|
|
|
8027
7478
|
* Run: pnpm --filter @geekmidas/cli sync-versions
|
|
8028
7479
|
*/
|
|
8029
7480
|
const GEEKMIDAS_VERSIONS = {
|
|
8030
|
-
"@geekmidas/audit": "~10.0.0-alpha.
|
|
8031
|
-
"@geekmidas/auth": "~10.0.0-alpha.
|
|
8032
|
-
"@geekmidas/cache": "~10.0.0-alpha.
|
|
8033
|
-
"@geekmidas/client": "~10.0.0-alpha.
|
|
8034
|
-
"@geekmidas/cloud": "~10.0.0-alpha.
|
|
8035
|
-
"@geekmidas/constructs": "~10.0.0-alpha.
|
|
8036
|
-
"@geekmidas/db": "~10.0.0-alpha.
|
|
8037
|
-
"@geekmidas/emailkit": "~10.0.0-alpha.
|
|
8038
|
-
"@geekmidas/envkit": "~10.0.0-alpha.
|
|
8039
|
-
"@geekmidas/errors": "~10.0.0-alpha.
|
|
8040
|
-
"@geekmidas/events": "~10.0.0-alpha.
|
|
8041
|
-
"@geekmidas/logger": "~10.0.0-alpha.
|
|
8042
|
-
"@geekmidas/manifest": "~10.0.0-alpha.
|
|
8043
|
-
"@geekmidas/rate-limit": "~10.0.0-alpha.
|
|
8044
|
-
"@geekmidas/schema": "~10.0.0-alpha.
|
|
8045
|
-
"@geekmidas/services": "~10.0.0-alpha.
|
|
8046
|
-
"@geekmidas/storage": "~10.0.0-alpha.
|
|
8047
|
-
"@geekmidas/studio": "~10.0.0-alpha.
|
|
8048
|
-
"@geekmidas/telescope": "~10.0.0-alpha.
|
|
8049
|
-
"@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",
|
|
8050
7501
|
"@geekmidas/cli": CLI_VERSION
|
|
8051
7502
|
};
|
|
8052
7503
|
|
|
@@ -8078,7 +7529,7 @@ function generateAuthAppFiles(options) {
|
|
|
8078
7529
|
"@geekmidas/db": GEEKMIDAS_VERSIONS["@geekmidas/db"],
|
|
8079
7530
|
"@geekmidas/envkit": GEEKMIDAS_VERSIONS["@geekmidas/envkit"],
|
|
8080
7531
|
"@geekmidas/logger": GEEKMIDAS_VERSIONS["@geekmidas/logger"],
|
|
8081
|
-
"@hono/node-server": "
|
|
7532
|
+
"@hono/node-server": DEPENDENCY_VERSIONS["@hono/node-server"],
|
|
8082
7533
|
"better-auth": DEPENDENCY_VERSIONS["better-auth"],
|
|
8083
7534
|
hono: DEPENDENCY_VERSIONS.hono,
|
|
8084
7535
|
kysely: DEPENDENCY_VERSIONS.kysely,
|
|
@@ -8086,10 +7537,10 @@ function generateAuthAppFiles(options) {
|
|
|
8086
7537
|
},
|
|
8087
7538
|
devDependencies: {
|
|
8088
7539
|
"@geekmidas/cli": GEEKMIDAS_VERSIONS["@geekmidas/cli"],
|
|
8089
|
-
"@types/node": "
|
|
8090
|
-
"@types/pg": "
|
|
8091
|
-
tsx: "
|
|
8092
|
-
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"]
|
|
8093
7544
|
}
|
|
8094
7545
|
};
|
|
8095
7546
|
const tsConfig = {
|
|
@@ -8196,21 +7647,6 @@ export default defineConfig({${stagesBlock(options.stages)}${options.monorepo ?
|
|
|
8196
7647
|
logger: './src/config/logger#logger',`;
|
|
8197
7648
|
if (isServerless || hasWorker) gkmConfig += `
|
|
8198
7649
|
functions: './src/functions/**/*.ts',`;
|
|
8199
|
-
if (!options.monorepo) {
|
|
8200
|
-
const { cache, mail, events } = options.services;
|
|
8201
|
-
if (cache || mail || events) {
|
|
8202
|
-
gkmConfig += `
|
|
8203
|
-
services: {`;
|
|
8204
|
-
if (cache) gkmConfig += `
|
|
8205
|
-
cache: true,`;
|
|
8206
|
-
if (mail) gkmConfig += `
|
|
8207
|
-
mail: true,`;
|
|
8208
|
-
if (events) gkmConfig += `
|
|
8209
|
-
events: '${events}',`;
|
|
8210
|
-
gkmConfig += `
|
|
8211
|
-
},`;
|
|
8212
|
-
}
|
|
8213
|
-
}
|
|
8214
7650
|
if (hasWorker) gkmConfig += `
|
|
8215
7651
|
crons: './src/crons/**/*.ts',
|
|
8216
7652
|
subscribers: './src/subscribers/**/*.ts',`;
|
|
@@ -8255,14 +7691,14 @@ export default defineConfig({${stagesBlock(options.stages)}${options.monorepo ?
|
|
|
8255
7691
|
path: "tsconfig.json",
|
|
8256
7692
|
content: `${JSON.stringify(tsConfig, null, 2)}\n`
|
|
8257
7693
|
}];
|
|
8258
|
-
if (options.database) files$2.push({
|
|
7694
|
+
if (options.constructs.database) files$2.push({
|
|
8259
7695
|
path: "vitest.config.ts",
|
|
8260
7696
|
content: vitestConfigContent
|
|
8261
7697
|
});
|
|
8262
7698
|
return files$2;
|
|
8263
7699
|
}
|
|
8264
7700
|
const biomeConfig = {
|
|
8265
|
-
$schema:
|
|
7701
|
+
$schema: BIOME_SCHEMA,
|
|
8266
7702
|
vcs: {
|
|
8267
7703
|
enabled: true,
|
|
8268
7704
|
clientKind: "git",
|
|
@@ -8346,7 +7782,7 @@ export default defineConfig({${stagesBlock(options.stages)}${options.monorepo ?
|
|
|
8346
7782
|
content: `${JSON.stringify(turboConfig, null, 2)}\n`
|
|
8347
7783
|
}
|
|
8348
7784
|
];
|
|
8349
|
-
if (options.database) files$1.push({
|
|
7785
|
+
if (options.constructs.database) files$1.push({
|
|
8350
7786
|
path: "vitest.config.ts",
|
|
8351
7787
|
content: vitestConfigContent
|
|
8352
7788
|
});
|
|
@@ -8383,325 +7819,13 @@ function generateSingleAppConfigFiles(options, _template, _helpers) {
|
|
|
8383
7819
|
path: "tsconfig.json",
|
|
8384
7820
|
content: `${JSON.stringify(tsConfig, null, 2)}\n`
|
|
8385
7821
|
}];
|
|
8386
|
-
if (options.database) files$1.push({
|
|
7822
|
+
if (options.constructs.database) files$1.push({
|
|
8387
7823
|
path: "vitest.config.ts",
|
|
8388
7824
|
content: vitestConfigContent
|
|
8389
7825
|
});
|
|
8390
7826
|
return files$1;
|
|
8391
7827
|
}
|
|
8392
7828
|
|
|
8393
|
-
//#endregion
|
|
8394
|
-
//#region src/init/generators/docker.ts
|
|
8395
|
-
/**
|
|
8396
|
-
* Generate docker-compose.yml based on template and options
|
|
8397
|
-
*/
|
|
8398
|
-
function generateDockerFiles(options, template, dbApps) {
|
|
8399
|
-
const { database } = options;
|
|
8400
|
-
const isServerless = template.name === "serverless";
|
|
8401
|
-
const hasWorker = template.name === "worker";
|
|
8402
|
-
const isFullstack = options.template === "fullstack";
|
|
8403
|
-
const services = [];
|
|
8404
|
-
const volumes = [];
|
|
8405
|
-
const files$1 = [];
|
|
8406
|
-
if (database) {
|
|
8407
|
-
const initVolume = isFullstack && dbApps?.length ? `
|
|
8408
|
-
- ./docker/postgres/init.sh:/docker-entrypoint-initdb.d/init.sh:ro` : "";
|
|
8409
|
-
const envFile = isFullstack && dbApps?.length ? `
|
|
8410
|
-
env_file:
|
|
8411
|
-
- ./docker/.env` : "";
|
|
8412
|
-
services.push(` postgres:
|
|
8413
|
-
image: postgres:18-alpine
|
|
8414
|
-
container_name: ${options.name}-postgres
|
|
8415
|
-
restart: unless-stopped${envFile}
|
|
8416
|
-
environment:
|
|
8417
|
-
POSTGRES_USER: \${POSTGRES_USER:-postgres}
|
|
8418
|
-
POSTGRES_PASSWORD: \${POSTGRES_PASSWORD:-postgres}
|
|
8419
|
-
POSTGRES_DB: \${POSTGRES_DB:-${options.name.replace(/-/g, "_")}_dev}
|
|
8420
|
-
ports:
|
|
8421
|
-
- '\${POSTGRES_HOST_PORT:-5432}:5432'
|
|
8422
|
-
volumes:
|
|
8423
|
-
- dbdata:/var/lib/postgresql${initVolume}
|
|
8424
|
-
healthcheck:
|
|
8425
|
-
test: ['CMD-SHELL', 'pg_isready -U $$POSTGRES_USER']
|
|
8426
|
-
interval: 5s
|
|
8427
|
-
timeout: 5s
|
|
8428
|
-
retries: 5`);
|
|
8429
|
-
volumes.push(" dbdata:");
|
|
8430
|
-
if (isFullstack && dbApps?.length) {
|
|
8431
|
-
files$1.push({
|
|
8432
|
-
path: "docker/postgres/init.sh",
|
|
8433
|
-
content: generatePostgresInitScript(dbApps, options.services?.events)
|
|
8434
|
-
});
|
|
8435
|
-
files$1.push({
|
|
8436
|
-
path: "docker/.env",
|
|
8437
|
-
content: generateDockerEnv(dbApps, options.services?.events)
|
|
8438
|
-
});
|
|
8439
|
-
}
|
|
8440
|
-
}
|
|
8441
|
-
if (isServerless) {
|
|
8442
|
-
services.push(` redis:
|
|
8443
|
-
image: redis:7-alpine
|
|
8444
|
-
container_name: ${options.name}-redis
|
|
8445
|
-
restart: unless-stopped
|
|
8446
|
-
ports:
|
|
8447
|
-
- '\${REDIS_HOST_PORT:-6379}:6379'
|
|
8448
|
-
volumes:
|
|
8449
|
-
- redis_data:/data
|
|
8450
|
-
healthcheck:
|
|
8451
|
-
test: ['CMD', 'redis-cli', 'ping']
|
|
8452
|
-
interval: 5s
|
|
8453
|
-
timeout: 5s
|
|
8454
|
-
retries: 5
|
|
8455
|
-
|
|
8456
|
-
serverless-redis:
|
|
8457
|
-
image: hiett/serverless-redis-http:latest
|
|
8458
|
-
container_name: ${options.name}-serverless-redis
|
|
8459
|
-
restart: unless-stopped
|
|
8460
|
-
ports:
|
|
8461
|
-
- '\${SRH_HOST_PORT:-8079}:80'
|
|
8462
|
-
environment:
|
|
8463
|
-
SRH_MODE: env
|
|
8464
|
-
SRH_TOKEN: local_dev_token
|
|
8465
|
-
SRH_CONNECTION_STRING: redis://redis:6379
|
|
8466
|
-
depends_on:
|
|
8467
|
-
redis:
|
|
8468
|
-
condition: service_healthy`);
|
|
8469
|
-
volumes.push(" redis_data:");
|
|
8470
|
-
} else {
|
|
8471
|
-
services.push(` redis:
|
|
8472
|
-
image: redis:7-alpine
|
|
8473
|
-
container_name: ${options.name}-redis
|
|
8474
|
-
restart: unless-stopped
|
|
8475
|
-
ports:
|
|
8476
|
-
- '\${REDIS_HOST_PORT:-6379}:6379'
|
|
8477
|
-
volumes:
|
|
8478
|
-
- redis_data:/data
|
|
8479
|
-
healthcheck:
|
|
8480
|
-
test: ['CMD', 'redis-cli', 'ping']
|
|
8481
|
-
interval: 5s
|
|
8482
|
-
timeout: 5s
|
|
8483
|
-
retries: 5`);
|
|
8484
|
-
volumes.push(" redis_data:");
|
|
8485
|
-
}
|
|
8486
|
-
if (hasWorker) {
|
|
8487
|
-
services.push(` rabbitmq:
|
|
8488
|
-
image: rabbitmq:3-management-alpine
|
|
8489
|
-
container_name: ${options.name}-rabbitmq
|
|
8490
|
-
restart: unless-stopped
|
|
8491
|
-
ports:
|
|
8492
|
-
- '\${RABBITMQ_HOST_PORT:-5672}:5672'
|
|
8493
|
-
- '\${RABBITMQ_MGMT_HOST_PORT:-15672}:15672'
|
|
8494
|
-
environment:
|
|
8495
|
-
RABBITMQ_DEFAULT_USER: guest
|
|
8496
|
-
RABBITMQ_DEFAULT_PASS: guest
|
|
8497
|
-
volumes:
|
|
8498
|
-
- rabbitmq_data:/var/lib/rabbitmq
|
|
8499
|
-
healthcheck:
|
|
8500
|
-
test: ['CMD', 'rabbitmq-diagnostics', 'check_running']
|
|
8501
|
-
interval: 10s
|
|
8502
|
-
timeout: 5s
|
|
8503
|
-
retries: 5`);
|
|
8504
|
-
volumes.push(" rabbitmq_data:");
|
|
8505
|
-
}
|
|
8506
|
-
if (options.services?.mail) services.push(` mailpit:
|
|
8507
|
-
image: axllent/mailpit:latest
|
|
8508
|
-
container_name: ${options.name}-mailpit
|
|
8509
|
-
restart: unless-stopped
|
|
8510
|
-
ports:
|
|
8511
|
-
- '\${SMTP_PORT:-1025}:1025'
|
|
8512
|
-
- '\${MAILPIT_PORT:-8025}:8025'
|
|
8513
|
-
environment:
|
|
8514
|
-
MP_SMTP_AUTH: \${SMTP_USER:-${options.name}}:\${SMTP_PASS:-${options.name}}`);
|
|
8515
|
-
if (options.services?.storage) {
|
|
8516
|
-
services.push(` minio:
|
|
8517
|
-
image: pgsty/minio:RELEASE.2026-08-04T00-00-00Z
|
|
8518
|
-
container_name: ${options.name}-minio
|
|
8519
|
-
restart: unless-stopped
|
|
8520
|
-
entrypoint: sh
|
|
8521
|
-
command: -c 'mkdir -p /data/\${STORAGE_BUCKET:-${options.name}} && /usr/bin/docker-entrypoint.sh server --console-address ":9001" /data'
|
|
8522
|
-
environment:
|
|
8523
|
-
MINIO_ROOT_USER: \${STORAGE_ACCESS_KEY_ID:-${options.name}}
|
|
8524
|
-
MINIO_ROOT_PASSWORD: \${STORAGE_SECRET_ACCESS_KEY:-${options.name}}
|
|
8525
|
-
ports:
|
|
8526
|
-
- '\${MINIO_API_HOST_PORT:-9000}:9000'
|
|
8527
|
-
- '\${MINIO_CONSOLE_HOST_PORT:-9001}:9001'
|
|
8528
|
-
volumes:
|
|
8529
|
-
- minio_data:/data
|
|
8530
|
-
healthcheck:
|
|
8531
|
-
test: ['CMD', 'mc', 'ready', 'local']
|
|
8532
|
-
interval: 10s
|
|
8533
|
-
timeout: 5s
|
|
8534
|
-
retries: 5`);
|
|
8535
|
-
volumes.push(" minio_data:");
|
|
8536
|
-
}
|
|
8537
|
-
if (options.services?.events === "sns") {
|
|
8538
|
-
services.push(` localstack:
|
|
8539
|
-
image: floci/floci:latest
|
|
8540
|
-
container_name: ${options.name}-localstack
|
|
8541
|
-
restart: unless-stopped
|
|
8542
|
-
environment:
|
|
8543
|
-
AWS_DEFAULT_REGION: \${AWS_REGION:-us-east-1}
|
|
8544
|
-
AWS_ACCESS_KEY_ID: \${AWS_ACCESS_KEY_ID:-localstack}
|
|
8545
|
-
AWS_SECRET_ACCESS_KEY: \${AWS_SECRET_ACCESS_KEY:-localstack}
|
|
8546
|
-
ports:
|
|
8547
|
-
- '\${LOCALSTACK_PORT:-4566}:4566'
|
|
8548
|
-
volumes:
|
|
8549
|
-
- localstack_data:/var/lib/localstack
|
|
8550
|
-
healthcheck:
|
|
8551
|
-
test: ['CMD', 'curl', '-f', 'http://localhost:4566/_localstack/health']
|
|
8552
|
-
interval: 10s
|
|
8553
|
-
timeout: 5s
|
|
8554
|
-
retries: 5`);
|
|
8555
|
-
volumes.push(" localstack_data:");
|
|
8556
|
-
}
|
|
8557
|
-
if (options.services?.events === "rabbitmq" && !hasWorker) {
|
|
8558
|
-
services.push(` rabbitmq:
|
|
8559
|
-
image: rabbitmq:3-management-alpine
|
|
8560
|
-
container_name: ${options.name}-rabbitmq
|
|
8561
|
-
restart: unless-stopped
|
|
8562
|
-
ports:
|
|
8563
|
-
- '\${RABBITMQ_HOST_PORT:-5672}:5672'
|
|
8564
|
-
- '\${RABBITMQ_MGMT_HOST_PORT:-15672}:15672'
|
|
8565
|
-
environment:
|
|
8566
|
-
RABBITMQ_DEFAULT_USER: guest
|
|
8567
|
-
RABBITMQ_DEFAULT_PASS: guest
|
|
8568
|
-
volumes:
|
|
8569
|
-
- rabbitmq_data:/var/lib/rabbitmq
|
|
8570
|
-
healthcheck:
|
|
8571
|
-
test: ['CMD', 'rabbitmq-diagnostics', 'check_running']
|
|
8572
|
-
interval: 10s
|
|
8573
|
-
timeout: 5s
|
|
8574
|
-
retries: 5`);
|
|
8575
|
-
if (!volumes.includes(" rabbitmq_data:")) volumes.push(" rabbitmq_data:");
|
|
8576
|
-
}
|
|
8577
|
-
let dockerCompose = `# Use "gkm dev" or "gkm test" to start services.
|
|
8578
|
-
# Running "docker compose up" directly will not inject secrets or resolve ports.
|
|
8579
|
-
services:
|
|
8580
|
-
${services.join("\n\n")}
|
|
8581
|
-
`;
|
|
8582
|
-
if (volumes.length > 0) dockerCompose += `
|
|
8583
|
-
volumes:
|
|
8584
|
-
${volumes.join("\n")}
|
|
8585
|
-
`;
|
|
8586
|
-
files$1.push({
|
|
8587
|
-
path: "docker-compose.yml",
|
|
8588
|
-
content: dockerCompose
|
|
8589
|
-
});
|
|
8590
|
-
return files$1;
|
|
8591
|
-
}
|
|
8592
|
-
/**
|
|
8593
|
-
* Generate .env file for docker-compose with database passwords
|
|
8594
|
-
*/
|
|
8595
|
-
function generateDockerEnv(apps, _eventsBackend) {
|
|
8596
|
-
const envVars = apps.map((app) => {
|
|
8597
|
-
const envVar = `${app.name.toUpperCase()}_DB_PASSWORD`;
|
|
8598
|
-
return `${envVar}=${app.password}`;
|
|
8599
|
-
});
|
|
8600
|
-
envVars.push(`PGBOSS_DB_PASSWORD=pgboss-dev-password`);
|
|
8601
|
-
return `# Auto-generated docker environment file
|
|
8602
|
-
# Contains database passwords for docker-compose postgres init
|
|
8603
|
-
# This file is gitignored - do not commit to version control
|
|
8604
|
-
${envVars.join("\n")}
|
|
8605
|
-
`;
|
|
8606
|
-
}
|
|
8607
|
-
/**
|
|
8608
|
-
* Generate PostgreSQL init shell script that creates per-app users with separate schemas.
|
|
8609
|
-
* Uses idempotent DO blocks so the script can be re-run safely.
|
|
8610
|
-
* - api user: uses public schema
|
|
8611
|
-
* - auth user: uses auth schema with search_path=auth
|
|
8612
|
-
* - pgboss user: uses pgboss schema (when events === 'pgboss')
|
|
8613
|
-
*/
|
|
8614
|
-
function generatePostgresInitScript(apps, _eventsBackend) {
|
|
8615
|
-
const userCreations = apps.map((app) => {
|
|
8616
|
-
const userName = app.name.replace(/-/g, "_");
|
|
8617
|
-
const envVar = `${app.name.toUpperCase()}_DB_PASSWORD`;
|
|
8618
|
-
const isApi = app.name === "api";
|
|
8619
|
-
const schemaName = isApi ? "public" : userName;
|
|
8620
|
-
if (isApi) return `
|
|
8621
|
-
# Create ${app.name} user (uses public schema) - idempotent
|
|
8622
|
-
echo "Creating user ${userName}..."
|
|
8623
|
-
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
|
8624
|
-
DO \\$\\$
|
|
8625
|
-
BEGIN
|
|
8626
|
-
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '${userName}') THEN
|
|
8627
|
-
CREATE USER ${userName} WITH PASSWORD '$${envVar}';
|
|
8628
|
-
ELSE
|
|
8629
|
-
ALTER USER ${userName} WITH PASSWORD '$${envVar}';
|
|
8630
|
-
END IF;
|
|
8631
|
-
END
|
|
8632
|
-
\\$\\$;
|
|
8633
|
-
GRANT ALL ON SCHEMA public TO ${userName};
|
|
8634
|
-
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO ${userName};
|
|
8635
|
-
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO ${userName};
|
|
8636
|
-
EOSQL
|
|
8637
|
-
`;
|
|
8638
|
-
return `
|
|
8639
|
-
# Create ${app.name} user with dedicated schema - idempotent
|
|
8640
|
-
echo "Creating user ${userName} with schema ${schemaName}..."
|
|
8641
|
-
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
|
8642
|
-
DO \\$\\$
|
|
8643
|
-
BEGIN
|
|
8644
|
-
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '${userName}') THEN
|
|
8645
|
-
CREATE USER ${userName} WITH PASSWORD '$${envVar}';
|
|
8646
|
-
ELSE
|
|
8647
|
-
ALTER USER ${userName} WITH PASSWORD '$${envVar}';
|
|
8648
|
-
END IF;
|
|
8649
|
-
END
|
|
8650
|
-
\\$\\$;
|
|
8651
|
-
CREATE SCHEMA IF NOT EXISTS ${schemaName} AUTHORIZATION ${userName};
|
|
8652
|
-
ALTER USER ${userName} SET search_path TO ${schemaName};
|
|
8653
|
-
GRANT USAGE ON SCHEMA ${schemaName} TO ${userName};
|
|
8654
|
-
GRANT ALL ON ALL TABLES IN SCHEMA ${schemaName} TO ${userName};
|
|
8655
|
-
GRANT ALL ON ALL SEQUENCES IN SCHEMA ${schemaName} TO ${userName};
|
|
8656
|
-
ALTER DEFAULT PRIVILEGES IN SCHEMA ${schemaName} GRANT ALL ON TABLES TO ${userName};
|
|
8657
|
-
ALTER DEFAULT PRIVILEGES IN SCHEMA ${schemaName} GRANT ALL ON SEQUENCES TO ${userName};
|
|
8658
|
-
EOSQL
|
|
8659
|
-
`;
|
|
8660
|
-
});
|
|
8661
|
-
const pgbossBlock = `
|
|
8662
|
-
# Create pgboss user with dedicated schema - idempotent
|
|
8663
|
-
echo "Creating pgboss user and schema..."
|
|
8664
|
-
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
|
8665
|
-
DO \\$\\$
|
|
8666
|
-
BEGIN
|
|
8667
|
-
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'pgboss') THEN
|
|
8668
|
-
CREATE USER pgboss WITH PASSWORD '$PGBOSS_DB_PASSWORD';
|
|
8669
|
-
ELSE
|
|
8670
|
-
ALTER USER pgboss WITH PASSWORD '$PGBOSS_DB_PASSWORD';
|
|
8671
|
-
END IF;
|
|
8672
|
-
END
|
|
8673
|
-
\\$\\$;
|
|
8674
|
-
CREATE SCHEMA IF NOT EXISTS pgboss AUTHORIZATION pgboss;
|
|
8675
|
-
ALTER USER pgboss SET search_path TO pgboss;
|
|
8676
|
-
GRANT USAGE ON SCHEMA pgboss TO pgboss;
|
|
8677
|
-
GRANT ALL ON ALL TABLES IN SCHEMA pgboss TO pgboss;
|
|
8678
|
-
GRANT ALL ON ALL SEQUENCES IN SCHEMA pgboss TO pgboss;
|
|
8679
|
-
ALTER DEFAULT PRIVILEGES IN SCHEMA pgboss GRANT ALL ON TABLES TO pgboss;
|
|
8680
|
-
ALTER DEFAULT PRIVILEGES IN SCHEMA pgboss GRANT ALL ON SEQUENCES TO pgboss;
|
|
8681
|
-
EOSQL
|
|
8682
|
-
`;
|
|
8683
|
-
const extensions = `
|
|
8684
|
-
# Create extensions
|
|
8685
|
-
echo "Creating extensions..."
|
|
8686
|
-
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
|
8687
|
-
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
|
8688
|
-
CREATE EXTENSION IF NOT EXISTS citext;
|
|
8689
|
-
EOSQL
|
|
8690
|
-
`;
|
|
8691
|
-
return `#!/bin/bash
|
|
8692
|
-
set -e
|
|
8693
|
-
|
|
8694
|
-
# Auto-generated PostgreSQL init script (idempotent - safe to re-run)
|
|
8695
|
-
# Creates per-app users with separate schemas in a single database
|
|
8696
|
-
# - api: uses public schema
|
|
8697
|
-
# - auth: uses auth schema (search_path=auth)
|
|
8698
|
-
# - pgboss: uses pgboss schema for event processing
|
|
8699
|
-
${extensions}
|
|
8700
|
-
${userCreations.join("\n")}${pgbossBlock}
|
|
8701
|
-
echo "Database initialization complete!"
|
|
8702
|
-
`;
|
|
8703
|
-
}
|
|
8704
|
-
|
|
8705
7829
|
//#endregion
|
|
8706
7830
|
//#region src/init/generators/env.ts
|
|
8707
7831
|
/**
|
|
@@ -8720,6 +7844,9 @@ node_modules/
|
|
|
8720
7844
|
dist/
|
|
8721
7845
|
.gkm/
|
|
8722
7846
|
|
|
7847
|
+
# Written by gkm from the constructs; your own docker-compose.yml is not
|
|
7848
|
+
docker-compose.constructs.yml
|
|
7849
|
+
|
|
8723
7850
|
# Environment (legacy - use gkm secrets instead)
|
|
8724
7851
|
.env
|
|
8725
7852
|
.env.local
|
|
@@ -9111,32 +8238,32 @@ function generateExpoAppFiles(options) {
|
|
|
9111
8238
|
"@better-auth/expo": DEPENDENCY_VERSIONS["better-auth"],
|
|
9112
8239
|
"@geekmidas/client": GEEKMIDAS_VERSIONS["@geekmidas/client"],
|
|
9113
8240
|
"@geekmidas/envkit": GEEKMIDAS_VERSIONS["@geekmidas/envkit"],
|
|
9114
|
-
"@react-navigation/native": "
|
|
8241
|
+
"@react-navigation/native": EXPO_VERSIONS["@react-navigation/native"],
|
|
9115
8242
|
"@tanstack/react-query": DEPENDENCY_VERSIONS["@tanstack/react-query"],
|
|
9116
8243
|
"better-auth": DEPENDENCY_VERSIONS["better-auth"],
|
|
9117
|
-
expo: "
|
|
9118
|
-
"expo-constants": "
|
|
9119
|
-
"expo-dev-client": "
|
|
9120
|
-
"expo-linking": "
|
|
9121
|
-
"expo-router": "
|
|
9122
|
-
"expo-secure-store": "
|
|
9123
|
-
"expo-splash-screen": "
|
|
9124
|
-
"expo-status-bar": "
|
|
9125
|
-
nativewind: "
|
|
9126
|
-
react: "
|
|
9127
|
-
"react-dom": "
|
|
9128
|
-
"react-native": "
|
|
9129
|
-
"react-native-gesture-handler": "
|
|
9130
|
-
"react-native-reanimated": "
|
|
9131
|
-
"react-native-safe-area-context": "
|
|
9132
|
-
"react-native-screens": "
|
|
9133
|
-
"react-native-web": "
|
|
9134
|
-
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"]
|
|
9135
8262
|
},
|
|
9136
8263
|
devDependencies: {
|
|
9137
|
-
"@babel/core": "
|
|
9138
|
-
"@types/react": "
|
|
9139
|
-
typescript: "
|
|
8264
|
+
"@babel/core": EXPO_VERSIONS["@babel/core"],
|
|
8265
|
+
"@types/react": EXPO_VERSIONS["@types/react"],
|
|
8266
|
+
typescript: TOOLCHAIN_VERSIONS["typescript"]
|
|
9140
8267
|
}
|
|
9141
8268
|
};
|
|
9142
8269
|
const appConfig = `import type { ConfigContext, ExpoConfig } from '@expo/config';
|
|
@@ -9690,7 +8817,7 @@ function generateModelsPackage(options) {
|
|
|
9690
8817
|
exports: { "./*": "./src/*.ts" },
|
|
9691
8818
|
scripts: { typecheck: "tsc --noEmit" },
|
|
9692
8819
|
dependencies: {},
|
|
9693
|
-
devDependencies: { typescript: "
|
|
8820
|
+
devDependencies: { typescript: TOOLCHAIN_VERSIONS["typescript"] }
|
|
9694
8821
|
};
|
|
9695
8822
|
const tsConfig = {
|
|
9696
8823
|
extends: "../../tsconfig.json",
|
|
@@ -9846,11 +8973,11 @@ function generateDeployFiles(options) {
|
|
|
9846
8973
|
if (!options.region) throw new SstNeedsRegion();
|
|
9847
8974
|
const db = databaseFor();
|
|
9848
8975
|
const mail = emailFor();
|
|
9849
|
-
const hasDatabase = options.
|
|
8976
|
+
const hasDatabase = options.constructs.database;
|
|
9850
8977
|
const inputs = [...hasDatabase ? [` // RDS needs a network. Created here rather than assumed, because a
|
|
9851
8978
|
// VPC with NAT is a monthly cost in an account that may already
|
|
9852
8979
|
// have one — replace it with \`sst.aws.Vpc.get(…)\` if so.
|
|
9853
|
-
${db.id}: { vpc },`] : [], ...options.
|
|
8980
|
+
${db.id}: { vpc },`] : [], ...options.constructs.mail ? [` // Every provider rejects an unverified sender, so there is no
|
|
9854
8981
|
// default; the synth stops with \`EmailNeedsSender\` until it is set.
|
|
9855
8982
|
${mail.id}: { from: process.env.MAIL_FROM as string },`] : []];
|
|
9856
8983
|
return [{
|
|
@@ -9940,9 +9067,9 @@ function rootConstructDependencies(options) {
|
|
|
9940
9067
|
"@geekmidas/db": v["@geekmidas/db"],
|
|
9941
9068
|
kysely: DEPENDENCY_VERSIONS.kysely,
|
|
9942
9069
|
pg: DEPENDENCY_VERSIONS.pg,
|
|
9943
|
-
...options.
|
|
9944
|
-
...options.
|
|
9945
|
-
...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"] } : {}
|
|
9946
9073
|
};
|
|
9947
9074
|
}
|
|
9948
9075
|
/**
|
|
@@ -9977,13 +9104,13 @@ function generateMonorepoFiles(options, _template) {
|
|
|
9977
9104
|
...deploy$1.dependencies
|
|
9978
9105
|
},
|
|
9979
9106
|
devDependencies: {
|
|
9980
|
-
"@biomejs/biome": "
|
|
9107
|
+
"@biomejs/biome": DEPENDENCY_VERSIONS["@biomejs/biome"],
|
|
9981
9108
|
"@geekmidas/cli": GEEKMIDAS_VERSIONS["@geekmidas/cli"],
|
|
9982
|
-
esbuild: "
|
|
9983
|
-
tsx: "
|
|
9984
|
-
turbo: "
|
|
9985
|
-
typescript: "
|
|
9986
|
-
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"],
|
|
9987
9114
|
...deploy$1.devDependencies
|
|
9988
9115
|
}
|
|
9989
9116
|
};
|
|
@@ -9994,7 +9121,7 @@ function generateMonorepoFiles(options, _template) {
|
|
|
9994
9121
|
- 'packages/*'
|
|
9995
9122
|
`;
|
|
9996
9123
|
const biomeConfig = {
|
|
9997
|
-
$schema:
|
|
9124
|
+
$schema: BIOME_SCHEMA,
|
|
9998
9125
|
vcs: {
|
|
9999
9126
|
enabled: true,
|
|
10000
9127
|
clientKind: "git",
|
|
@@ -10039,7 +9166,17 @@ function generateMonorepoFiles(options, _template) {
|
|
|
10039
9166
|
tasks: {
|
|
10040
9167
|
build: {
|
|
10041
9168
|
dependsOn: ["^build"],
|
|
10042
|
-
|
|
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
|
+
]
|
|
10043
9180
|
},
|
|
10044
9181
|
dev: {
|
|
10045
9182
|
cache: false,
|
|
@@ -10068,6 +9205,9 @@ node_modules/
|
|
|
10068
9205
|
dist/
|
|
10069
9206
|
.gkm/
|
|
10070
9207
|
|
|
9208
|
+
# Written by gkm from the constructs; your own docker-compose.yml is not
|
|
9209
|
+
docker-compose.constructs.yml
|
|
9210
|
+
|
|
10071
9211
|
# Environment
|
|
10072
9212
|
.env
|
|
10073
9213
|
.env.local
|
|
@@ -10233,7 +9373,7 @@ export default defineConfig({
|
|
|
10233
9373
|
* table in the database that is already there, storage is MinIO, mail is SES.
|
|
10234
9374
|
*/
|
|
10235
9375
|
function generateWorkspaceConfig(options) {
|
|
10236
|
-
|
|
9376
|
+
const config = `import { defineWorkspace } from '@geekmidas/cli/config';
|
|
10237
9377
|
|
|
10238
9378
|
export default defineWorkspace({
|
|
10239
9379
|
// The scope every physical name is built from: \`Database\` becomes
|
|
@@ -10256,11 +9396,6 @@ ${workspaceConstructsGlobs(options.routesStructure, (0, node_path.dirname)(optio
|
|
|
10256
9396
|
},
|
|
10257
9397
|
});
|
|
10258
9398
|
`;
|
|
10259
|
-
if (options.services.events && options.services.events !== "pgboss") config = config.replace(" secrets: {", ` services: {
|
|
10260
|
-
events: '${options.services.events}',
|
|
10261
|
-
},
|
|
10262
|
-
|
|
10263
|
-
secrets: {`);
|
|
10264
9399
|
return config;
|
|
10265
9400
|
}
|
|
10266
9401
|
/**
|
|
@@ -10277,7 +9412,8 @@ ${workspaceConstructsGlobs(options.routesStructure, (0, node_path.dirname)(optio
|
|
|
10277
9412
|
*/
|
|
10278
9413
|
function generateRootConstructs(options) {
|
|
10279
9414
|
if (!options.monorepo || options.template !== "fullstack") return [];
|
|
10280
|
-
const { name: name$1,
|
|
9415
|
+
const { name: name$1, frontendFramework } = options;
|
|
9416
|
+
const { cache, uploads, mail } = options.constructs;
|
|
10281
9417
|
const db = databaseFor();
|
|
10282
9418
|
const files$1 = [];
|
|
10283
9419
|
files$1.push({
|
|
@@ -10392,7 +9528,7 @@ import { auth } from './auth.ts';
|
|
|
10392
9528
|
export const web = new StaticSite('Web', ${variant}).dependsOn([api, auth]);
|
|
10393
9529
|
`
|
|
10394
9530
|
});
|
|
10395
|
-
if (
|
|
9531
|
+
if (uploads) {
|
|
10396
9532
|
const bucket = storageFor();
|
|
10397
9533
|
files$1.push({
|
|
10398
9534
|
path: "constructs/storage.ts",
|
|
@@ -10403,18 +9539,18 @@ export const uploads = new ObjectStorage('${bucket.id}');
|
|
|
10403
9539
|
`
|
|
10404
9540
|
});
|
|
10405
9541
|
}
|
|
10406
|
-
if (
|
|
10407
|
-
const mail = emailFor();
|
|
9542
|
+
if (mail) {
|
|
9543
|
+
const mail$1 = emailFor();
|
|
10408
9544
|
files$1.push({
|
|
10409
9545
|
path: "constructs/email.ts",
|
|
10410
9546
|
content: `import { Email } from '@geekmidas/constructs/email';
|
|
10411
9547
|
|
|
10412
9548
|
/** Outbound mail — Mailpit locally, SES deployed. */
|
|
10413
|
-
export const email = new Email('${mail.id}', { templates: {} });
|
|
9549
|
+
export const email = new Email('${mail$1.id}', { templates: {} });
|
|
10414
9550
|
`
|
|
10415
9551
|
});
|
|
10416
9552
|
}
|
|
10417
|
-
if (
|
|
9553
|
+
if (cache) {
|
|
10418
9554
|
const kv = cacheFor();
|
|
10419
9555
|
files$1.push({
|
|
10420
9556
|
path: "constructs/cache.ts",
|
|
@@ -10447,20 +9583,20 @@ const apiTemplate = {
|
|
|
10447
9583
|
"@geekmidas/services": GEEKMIDAS_VERSIONS["@geekmidas/services"],
|
|
10448
9584
|
"@geekmidas/errors": GEEKMIDAS_VERSIONS["@geekmidas/errors"],
|
|
10449
9585
|
"@geekmidas/auth": GEEKMIDAS_VERSIONS["@geekmidas/auth"],
|
|
10450
|
-
"@hono/node-server": "
|
|
9586
|
+
"@hono/node-server": DEPENDENCY_VERSIONS["@hono/node-server"],
|
|
10451
9587
|
hono: DEPENDENCY_VERSIONS.hono,
|
|
10452
9588
|
pino: DEPENDENCY_VERSIONS.pino,
|
|
10453
9589
|
zod: DEPENDENCY_VERSIONS.zod
|
|
10454
9590
|
},
|
|
10455
9591
|
devDependencies: {
|
|
10456
|
-
"@biomejs/biome": "
|
|
9592
|
+
"@biomejs/biome": DEPENDENCY_VERSIONS["@biomejs/biome"],
|
|
10457
9593
|
"@geekmidas/cli": GEEKMIDAS_VERSIONS["@geekmidas/cli"],
|
|
10458
|
-
"@types/node": "
|
|
10459
|
-
esbuild: "
|
|
10460
|
-
tsx: "
|
|
10461
|
-
turbo: "
|
|
10462
|
-
typescript: "
|
|
10463
|
-
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"]
|
|
10464
9600
|
},
|
|
10465
9601
|
scripts: {
|
|
10466
9602
|
dev: "gkm dev",
|
|
@@ -10473,7 +9609,8 @@ const apiTemplate = {
|
|
|
10473
9609
|
"fmt:check": "biome format ."
|
|
10474
9610
|
},
|
|
10475
9611
|
files: (options) => {
|
|
10476
|
-
const { loggerType, routesStructure, monorepo, name: name$1
|
|
9612
|
+
const { loggerType, routesStructure, monorepo, name: name$1 } = options;
|
|
9613
|
+
const { cache, uploads, mail: sendsMail } = options.constructs;
|
|
10477
9614
|
const bucket = storageFor();
|
|
10478
9615
|
const kv = cacheFor();
|
|
10479
9616
|
const db = databaseFor();
|
|
@@ -10583,7 +9720,7 @@ import { router } from '${src("router.ts")}';
|
|
|
10583
9720
|
export const listUsersEndpoint = router
|
|
10584
9721
|
.get('/users')
|
|
10585
9722
|
.output(ListUsersResponseSchema)
|
|
10586
|
-
${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.
|
|
10587
9724
|
.handle(async ({ db }) => ({
|
|
10588
9725
|
users: await db.selectFrom('users').select(['id', 'name']).execute(),
|
|
10589
9726
|
}));
|
|
@@ -10606,7 +9743,7 @@ export const listUsersEndpoint = router
|
|
|
10606
9743
|
.output(z.object({
|
|
10607
9744
|
users: z.array(UserSchema),
|
|
10608
9745
|
}))
|
|
10609
|
-
${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.
|
|
10610
9747
|
.handle(async ({ db }) => ({
|
|
10611
9748
|
users: await db.selectFrom('users').select(['id', 'name']).execute(),
|
|
10612
9749
|
}));
|
|
@@ -10698,18 +9835,18 @@ export const authService = {
|
|
|
10698
9835
|
files$1.push({
|
|
10699
9836
|
path: "src/router.ts",
|
|
10700
9837
|
content: `import { UnauthorizedError } from '@geekmidas/errors';
|
|
10701
|
-
import { api } from '${constructsImport("api")}';${options.database ? `
|
|
9838
|
+
import { api } from '${constructsImport("api")}';${options.constructs.database ? `
|
|
10702
9839
|
import { database } from '${constructsImport("database")}';` : ""}
|
|
10703
9840
|
import { authService, type Session } from './services/auth.ts';
|
|
10704
9841
|
|
|
10705
9842
|
/**
|
|
10706
9843
|
* The shared endpoint factory — no session required.
|
|
10707
|
-
*${options.database ? `
|
|
9844
|
+
*${options.constructs.database ? `
|
|
10708
9845
|
* Naming the database construct is what puts \`db\` in every handler built from
|
|
10709
9846
|
* this router. Depend on other constructs per endpoint with \`.dependsOn([…])\`.` : `
|
|
10710
9847
|
* Depend on constructs per endpoint with \`.dependsOn([…])\`.`}
|
|
10711
9848
|
*/
|
|
10712
|
-
export const router = api.endpoints${options.database ? ".database(database)" : ""};
|
|
9849
|
+
export const router = api.endpoints${options.constructs.database ? ".database(database)" : ""};
|
|
10713
9850
|
|
|
10714
9851
|
// The auth client available, but the session not enforced.
|
|
10715
9852
|
export const r = router.services([authService]);
|
|
@@ -10745,22 +9882,22 @@ export const profileEndpoint = sessionRouter
|
|
|
10745
9882
|
}
|
|
10746
9883
|
if (!monorepo) files$1.push({
|
|
10747
9884
|
path: "src/router.ts",
|
|
10748
|
-
content: `import { api } from '${constructsImport("api")}';${options.database ? `
|
|
9885
|
+
content: `import { api } from '${constructsImport("api")}';${options.constructs.database ? `
|
|
10749
9886
|
import { database } from '${constructsImport("database")}';` : ""}
|
|
10750
9887
|
|
|
10751
9888
|
/**
|
|
10752
9889
|
* The shared endpoint factory.
|
|
10753
|
-
*${options.database ? `
|
|
9890
|
+
*${options.constructs.database ? `
|
|
10754
9891
|
* Naming the database construct is what puts \`db\` in every handler built from
|
|
10755
9892
|
* this router. Depend on other constructs per endpoint with \`.dependsOn([…])\`.` : `
|
|
10756
9893
|
* Depend on constructs per endpoint with \`.dependsOn([…])\`.`}
|
|
10757
9894
|
*/
|
|
10758
|
-
export const router = api.endpoints${options.database ? ".database(database)" : ""};
|
|
9895
|
+
export const router = api.endpoints${options.constructs.database ? ".database(database)" : ""};
|
|
10759
9896
|
`
|
|
10760
9897
|
});
|
|
10761
|
-
if (options.database && declares) files$1.push(...databaseFiles());
|
|
10762
|
-
else if (options.database) files$1.push(usersMigration());
|
|
10763
|
-
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({
|
|
10764
9901
|
path: "src/constructs/storage.ts",
|
|
10765
9902
|
content: `import { ObjectStorage } from '@geekmidas/constructs/object-storage';
|
|
10766
9903
|
|
|
@@ -10774,7 +9911,7 @@ export const router = api.endpoints${options.database ? ".database(database)" :
|
|
|
10774
9911
|
export const uploads = new ObjectStorage('${bucket.id}');
|
|
10775
9912
|
`
|
|
10776
9913
|
});
|
|
10777
|
-
if (
|
|
9914
|
+
if (sendsMail && declares) files$1.push({
|
|
10778
9915
|
path: "src/constructs/email.ts",
|
|
10779
9916
|
content: `import { Email } from '@geekmidas/constructs/email';
|
|
10780
9917
|
|
|
@@ -10783,13 +9920,13 @@ export const uploads = new ObjectStorage('${bucket.id}');
|
|
|
10783
9920
|
*
|
|
10784
9921
|
* Add React templates to \`templates\` and \`sendTemplate\` becomes typed
|
|
10785
9922
|
* against them. Reach it with \`.dependsOn([email])\` for
|
|
10786
|
-
* \`services.${mail.service}
|
|
10787
|
-
* \`
|
|
9923
|
+
* \`services.${mail.service}\`. Who delivers it is whichever provider the
|
|
9924
|
+
* stage's \`${mail.urlKey}\` points at — every one of them speaks SMTP.
|
|
10788
9925
|
*/
|
|
10789
9926
|
export const email = new Email('${mail.id}', { templates: {} });
|
|
10790
9927
|
`
|
|
10791
9928
|
});
|
|
10792
|
-
if (
|
|
9929
|
+
if (cache && declares) files$1.push({
|
|
10793
9930
|
path: "src/constructs/cache.ts",
|
|
10794
9931
|
content: `import { Cache } from '@geekmidas/constructs/cache';
|
|
10795
9932
|
|
|
@@ -10797,18 +9934,18 @@ export const email = new Email('${mail.id}', { templates: {} });
|
|
|
10797
9934
|
* A cache, declared once.
|
|
10798
9935
|
*
|
|
10799
9936
|
* Reach it with \`.dependsOn([cache])\` for \`services.${kv.service}\`. This
|
|
10800
|
-
* form says the app caches and leaves *where* to the deployment:
|
|
10801
|
-
*
|
|
10802
|
-
*
|
|
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.
|
|
10803
9940
|
*
|
|
10804
9941
|
* To say it caches in a particular database instead, declare it from that
|
|
10805
9942
|
* database — \`database.cache()\` — and entries become a table in it, in its
|
|
10806
|
-
* schema and reached by its role,
|
|
9943
|
+
* schema and reached by its role, whatever the target.
|
|
10807
9944
|
*/
|
|
10808
9945
|
export const cache = new Cache('${kv.id}');
|
|
10809
9946
|
`
|
|
10810
9947
|
});
|
|
10811
|
-
if (options.database && !declares) files$1.push({
|
|
9948
|
+
if (options.constructs.database && !declares) files$1.push({
|
|
10812
9949
|
path: "src/services/database.ts",
|
|
10813
9950
|
content: `import type { Service, ServiceRegisterOptions } from '@geekmidas/services';
|
|
10814
9951
|
import { Kysely, PostgresDialect } from 'kysely';
|
|
@@ -10859,7 +9996,7 @@ export const telescope = new Telescope({
|
|
|
10859
9996
|
});
|
|
10860
9997
|
`
|
|
10861
9998
|
});
|
|
10862
|
-
if (options.studio && options.database) files$1.push({
|
|
9999
|
+
if (options.studio && options.constructs.database) files$1.push({
|
|
10863
10000
|
path: "src/config/studio.ts",
|
|
10864
10001
|
content: `import { Direction, InMemoryMonitoringStorage, Studio } from '@geekmidas/studio';
|
|
10865
10002
|
import { Kysely, PostgresDialect } from 'kysely';
|
|
@@ -10908,20 +10045,20 @@ const minimalTemplate = {
|
|
|
10908
10045
|
"@geekmidas/logger": GEEKMIDAS_VERSIONS["@geekmidas/logger"],
|
|
10909
10046
|
"@geekmidas/rate-limit": GEEKMIDAS_VERSIONS["@geekmidas/rate-limit"],
|
|
10910
10047
|
"@geekmidas/schema": GEEKMIDAS_VERSIONS["@geekmidas/schema"],
|
|
10911
|
-
"@hono/node-server": "
|
|
10048
|
+
"@hono/node-server": DEPENDENCY_VERSIONS["@hono/node-server"],
|
|
10912
10049
|
hono: DEPENDENCY_VERSIONS.hono,
|
|
10913
10050
|
pino: DEPENDENCY_VERSIONS.pino,
|
|
10914
10051
|
zod: DEPENDENCY_VERSIONS.zod
|
|
10915
10052
|
},
|
|
10916
10053
|
devDependencies: {
|
|
10917
|
-
"@biomejs/biome": "
|
|
10054
|
+
"@biomejs/biome": DEPENDENCY_VERSIONS["@biomejs/biome"],
|
|
10918
10055
|
"@geekmidas/cli": GEEKMIDAS_VERSIONS["@geekmidas/cli"],
|
|
10919
|
-
"@types/node": "
|
|
10920
|
-
esbuild: "
|
|
10921
|
-
tsx: "
|
|
10922
|
-
turbo: "
|
|
10923
|
-
typescript: "
|
|
10924
|
-
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"]
|
|
10925
10062
|
},
|
|
10926
10063
|
scripts: {
|
|
10927
10064
|
dev: "gkm dev",
|
|
@@ -11007,7 +10144,7 @@ export const healthEndpoint = api
|
|
|
11007
10144
|
`
|
|
11008
10145
|
}
|
|
11009
10146
|
];
|
|
11010
|
-
if (options.database) files$1.push(...databaseFiles());
|
|
10147
|
+
if (options.constructs.database) files$1.push(...databaseFiles());
|
|
11011
10148
|
if (options.telescope) files$1.push({
|
|
11012
10149
|
path: "src/config/telescope.ts",
|
|
11013
10150
|
content: `import { Telescope } from '@geekmidas/telescope';
|
|
@@ -11019,7 +10156,7 @@ export const telescope = new Telescope({
|
|
|
11019
10156
|
});
|
|
11020
10157
|
`
|
|
11021
10158
|
});
|
|
11022
|
-
if (options.studio && options.database) files$1.push({
|
|
10159
|
+
if (options.studio && options.constructs.database) files$1.push({
|
|
11023
10160
|
path: "src/config/studio.ts",
|
|
11024
10161
|
content: `import { Direction, InMemoryMonitoringStorage, Studio } from '@geekmidas/studio';
|
|
11025
10162
|
import { Kysely, PostgresDialect } from 'kysely';
|
|
@@ -11070,19 +10207,19 @@ const serverlessTemplate = {
|
|
|
11070
10207
|
"@geekmidas/cloud": GEEKMIDAS_VERSIONS["@geekmidas/cloud"],
|
|
11071
10208
|
"@geekmidas/rate-limit": GEEKMIDAS_VERSIONS["@geekmidas/rate-limit"],
|
|
11072
10209
|
"@geekmidas/schema": GEEKMIDAS_VERSIONS["@geekmidas/schema"],
|
|
11073
|
-
"@hono/node-server": "
|
|
10210
|
+
"@hono/node-server": DEPENDENCY_VERSIONS["@hono/node-server"],
|
|
11074
10211
|
hono: DEPENDENCY_VERSIONS.hono,
|
|
11075
10212
|
pino: DEPENDENCY_VERSIONS.pino
|
|
11076
10213
|
},
|
|
11077
10214
|
devDependencies: {
|
|
11078
|
-
"@biomejs/biome": "
|
|
10215
|
+
"@biomejs/biome": DEPENDENCY_VERSIONS["@biomejs/biome"],
|
|
11079
10216
|
"@geekmidas/cli": GEEKMIDAS_VERSIONS["@geekmidas/cli"],
|
|
11080
|
-
"@types/aws-lambda": "
|
|
11081
|
-
"@types/node": "
|
|
11082
|
-
tsx: "
|
|
11083
|
-
turbo: "
|
|
11084
|
-
typescript: "
|
|
11085
|
-
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"]
|
|
11086
10223
|
},
|
|
11087
10224
|
scripts: {
|
|
11088
10225
|
dev: "gkm dev",
|
|
@@ -11190,7 +10327,7 @@ export const worker = new Worker('Jobs', { logger });
|
|
|
11190
10327
|
`
|
|
11191
10328
|
}
|
|
11192
10329
|
];
|
|
11193
|
-
if (options.database) files$1.push(...databaseFiles());
|
|
10330
|
+
if (options.constructs.database) files$1.push(...databaseFiles());
|
|
11194
10331
|
if (options.telescope) files$1.push({
|
|
11195
10332
|
path: "src/config/telescope.ts",
|
|
11196
10333
|
content: `import { Telescope } from '@geekmidas/telescope';
|
|
@@ -11238,13 +10375,13 @@ const workerTemplate = {
|
|
|
11238
10375
|
pino: DEPENDENCY_VERSIONS.pino
|
|
11239
10376
|
},
|
|
11240
10377
|
devDependencies: {
|
|
11241
|
-
"@biomejs/biome": "
|
|
10378
|
+
"@biomejs/biome": DEPENDENCY_VERSIONS["@biomejs/biome"],
|
|
11242
10379
|
"@geekmidas/cli": GEEKMIDAS_VERSIONS["@geekmidas/cli"],
|
|
11243
|
-
"@types/node": "
|
|
11244
|
-
tsx: "
|
|
11245
|
-
turbo: "
|
|
11246
|
-
typescript: "
|
|
11247
|
-
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"]
|
|
11248
10385
|
},
|
|
11249
10386
|
scripts: {
|
|
11250
10387
|
dev: "gkm dev",
|
|
@@ -11257,7 +10394,8 @@ const workerTemplate = {
|
|
|
11257
10394
|
"fmt:check": "biome format ."
|
|
11258
10395
|
},
|
|
11259
10396
|
files: (options) => {
|
|
11260
|
-
const { loggerType, name: name$1
|
|
10397
|
+
const { loggerType, name: name$1 } = options;
|
|
10398
|
+
const { database } = options.constructs;
|
|
11261
10399
|
const loggerContent = `import { createLogger } from '@geekmidas/logger/${loggerType}';
|
|
11262
10400
|
|
|
11263
10401
|
export const logger = createLogger();
|
|
@@ -11373,7 +10511,7 @@ export const cleanup = worker
|
|
|
11373
10511
|
});
|
|
11374
10512
|
`
|
|
11375
10513
|
});
|
|
11376
|
-
if (options.database) files$1.push(...databaseFiles());
|
|
10514
|
+
if (options.constructs.database) files$1.push(...databaseFiles());
|
|
11377
10515
|
if (options.telescope) files$1.push({
|
|
11378
10516
|
path: "src/config/telescope.ts",
|
|
11379
10517
|
content: `import { Telescope } from '@geekmidas/telescope';
|
|
@@ -11518,60 +10656,32 @@ const deployTargetChoices = [
|
|
|
11518
10656
|
}
|
|
11519
10657
|
];
|
|
11520
10658
|
/**
|
|
11521
|
-
*
|
|
11522
|
-
*/
|
|
11523
|
-
/**
|
|
11524
|
-
* What the project declares — and therefore which containers it runs.
|
|
10659
|
+
* Which constructs to declare.
|
|
11525
10660
|
*
|
|
11526
|
-
*
|
|
11527
|
-
*
|
|
11528
|
-
*
|
|
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.
|
|
11529
10664
|
*/
|
|
11530
|
-
const
|
|
10665
|
+
const constructChoices = [
|
|
11531
10666
|
{
|
|
11532
|
-
title: "
|
|
11533
|
-
value: "
|
|
11534
|
-
description: "
|
|
10667
|
+
title: "Database",
|
|
10668
|
+
value: "database",
|
|
10669
|
+
description: "KyselyDatabase — Postgres"
|
|
11535
10670
|
},
|
|
11536
10671
|
{
|
|
11537
|
-
title: "
|
|
10672
|
+
title: "Cache",
|
|
11538
10673
|
value: "cache",
|
|
11539
|
-
description: "
|
|
11540
|
-
},
|
|
11541
|
-
{
|
|
11542
|
-
title: "Mailpit",
|
|
11543
|
-
value: "mail",
|
|
11544
|
-
description: "Declared outbound mail — Email construct"
|
|
11545
|
-
},
|
|
11546
|
-
{
|
|
11547
|
-
title: "MinIO",
|
|
11548
|
-
value: "storage",
|
|
11549
|
-
description: "A declared bucket — ObjectStorage construct"
|
|
11550
|
-
}
|
|
11551
|
-
];
|
|
11552
|
-
/**
|
|
11553
|
-
* Event backend choices for prompts
|
|
11554
|
-
*/
|
|
11555
|
-
const eventsBackendChoices = [
|
|
11556
|
-
{
|
|
11557
|
-
title: "pg-boss",
|
|
11558
|
-
value: "pgboss",
|
|
11559
|
-
description: "PostgreSQL-based job queue (reuses postgres, no extra container)"
|
|
11560
|
-
},
|
|
11561
|
-
{
|
|
11562
|
-
title: "SNS/SQS",
|
|
11563
|
-
value: "sns",
|
|
11564
|
-
description: "AWS SNS+SQS via LocalStack for local dev"
|
|
10674
|
+
description: "Cache — backed by what the deploy target implies"
|
|
11565
10675
|
},
|
|
11566
10676
|
{
|
|
11567
|
-
title: "
|
|
11568
|
-
value: "
|
|
11569
|
-
description: "
|
|
10677
|
+
title: "Uploads",
|
|
10678
|
+
value: "uploads",
|
|
10679
|
+
description: "ObjectStorage — a bucket"
|
|
11570
10680
|
},
|
|
11571
10681
|
{
|
|
11572
|
-
title: "
|
|
11573
|
-
value:
|
|
11574
|
-
description: "
|
|
10682
|
+
title: "Mail",
|
|
10683
|
+
value: "mail",
|
|
10684
|
+
description: "Email — outbound mail over SMTP"
|
|
11575
10685
|
}
|
|
11576
10686
|
];
|
|
11577
10687
|
/**
|
|
@@ -11596,26 +10706,27 @@ function isFullstackTemplate(name$1) {
|
|
|
11596
10706
|
* Generate package.json with dependencies based on template and options
|
|
11597
10707
|
*/
|
|
11598
10708
|
function generatePackageJson(options, template) {
|
|
11599
|
-
const { name: name$1, telescope,
|
|
10709
|
+
const { name: name$1, telescope, studio, monorepo } = options;
|
|
10710
|
+
const { database, cache, uploads, mail } = options.constructs;
|
|
11600
10711
|
const dependencies$1 = { ...template.dependencies };
|
|
11601
10712
|
const devDependencies$1 = { ...template.devDependencies };
|
|
11602
10713
|
const scripts$1 = { ...template.scripts };
|
|
11603
10714
|
if (telescope) dependencies$1["@geekmidas/telescope"] = GEEKMIDAS_VERSIONS["@geekmidas/telescope"];
|
|
11604
10715
|
if (studio) dependencies$1["@geekmidas/studio"] = GEEKMIDAS_VERSIONS["@geekmidas/studio"];
|
|
11605
10716
|
const declares = !monorepo;
|
|
11606
|
-
if (declares && (database ||
|
|
11607
|
-
if (declares &&
|
|
11608
|
-
if (declares &&
|
|
11609
|
-
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"];
|
|
11610
10721
|
if (database) {
|
|
11611
10722
|
dependencies$1["@geekmidas/db"] = GEEKMIDAS_VERSIONS["@geekmidas/db"];
|
|
11612
10723
|
dependencies$1.kysely = DEPENDENCY_VERSIONS.kysely;
|
|
11613
10724
|
dependencies$1.pg = DEPENDENCY_VERSIONS.pg;
|
|
11614
|
-
devDependencies$1["@types/pg"] = "
|
|
10725
|
+
devDependencies$1["@types/pg"] = DEPENDENCY_VERSIONS["@types/pg"];
|
|
11615
10726
|
devDependencies$1["@geekmidas/testkit"] = GEEKMIDAS_VERSIONS["@geekmidas/testkit"];
|
|
11616
|
-
devDependencies$1["@faker-js/faker"] = "
|
|
10727
|
+
devDependencies$1["@faker-js/faker"] = DEPENDENCY_VERSIONS["@faker-js/faker"];
|
|
11617
10728
|
devDependencies$1["kysely-ctl"] = DEPENDENCY_VERSIONS["kysely-ctl"];
|
|
11618
|
-
devDependencies$1["vite-tsconfig-paths"] = "
|
|
10729
|
+
devDependencies$1["vite-tsconfig-paths"] = DEPENDENCY_VERSIONS["vite-tsconfig-paths"];
|
|
11619
10730
|
}
|
|
11620
10731
|
if (monorepo) {
|
|
11621
10732
|
delete devDependencies$1["@biomejs/biome"];
|
|
@@ -11672,7 +10783,7 @@ function generateSourceFiles(options, template) {
|
|
|
11672
10783
|
* factory system with builders/seeds, and an example spec.
|
|
11673
10784
|
*/
|
|
11674
10785
|
function generateTestFiles(options, _template) {
|
|
11675
|
-
if (!options.database) return [];
|
|
10786
|
+
if (!options.constructs.database) return [];
|
|
11676
10787
|
const db = databaseFor();
|
|
11677
10788
|
const declares = !options.monorepo;
|
|
11678
10789
|
const schema = declares ? "../src/constructs/database.ts" : "~/services/database.ts";
|
|
@@ -11840,32 +10951,32 @@ function generateUiPackageFiles(options) {
|
|
|
11840
10951
|
"build:storybook": "storybook build -o dist/storybook"
|
|
11841
10952
|
},
|
|
11842
10953
|
dependencies: {
|
|
11843
|
-
"@radix-ui/react-dialog": "
|
|
11844
|
-
"@radix-ui/react-label": "
|
|
11845
|
-
"@radix-ui/react-separator": "
|
|
11846
|
-
"@radix-ui/react-slot": "
|
|
11847
|
-
"@radix-ui/react-tabs": "
|
|
11848
|
-
"@radix-ui/react-tooltip": "
|
|
11849
|
-
"class-variance-authority": "
|
|
11850
|
-
clsx: "
|
|
11851
|
-
"lucide-react": "
|
|
11852
|
-
"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"]
|
|
11853
10964
|
},
|
|
11854
10965
|
devDependencies: {
|
|
11855
|
-
"@storybook/addon-a11y": "
|
|
11856
|
-
"@storybook/addon-essentials": "
|
|
11857
|
-
"@storybook/addon-interactions": "
|
|
11858
|
-
"@storybook/react": "
|
|
11859
|
-
"@storybook/react-vite": "
|
|
11860
|
-
"@tailwindcss/vite": "
|
|
11861
|
-
"@types/react": "
|
|
11862
|
-
"@types/react-dom": "
|
|
11863
|
-
react: "
|
|
11864
|
-
"react-dom": "
|
|
11865
|
-
storybook: "
|
|
11866
|
-
tailwindcss: "
|
|
11867
|
-
typescript: "
|
|
11868
|
-
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"]
|
|
11869
10980
|
},
|
|
11870
10981
|
peerDependencies: {
|
|
11871
10982
|
react: ">=18.0.0",
|
|
@@ -13258,19 +12369,19 @@ function generateWebAppFiles(options) {
|
|
|
13258
12369
|
"@geekmidas/envkit": GEEKMIDAS_VERSIONS["@geekmidas/envkit"],
|
|
13259
12370
|
"@tanstack/react-query": DEPENDENCY_VERSIONS["@tanstack/react-query"],
|
|
13260
12371
|
"better-auth": DEPENDENCY_VERSIONS["better-auth"],
|
|
13261
|
-
next: "
|
|
13262
|
-
react: "
|
|
13263
|
-
"react-dom": "
|
|
12372
|
+
next: DEPENDENCY_VERSIONS["next"],
|
|
12373
|
+
react: DEPENDENCY_VERSIONS["react"],
|
|
12374
|
+
"react-dom": DEPENDENCY_VERSIONS["react-dom"]
|
|
13264
12375
|
},
|
|
13265
12376
|
devDependencies: {
|
|
13266
12377
|
"@geekmidas/cli": GEEKMIDAS_VERSIONS["@geekmidas/cli"],
|
|
13267
|
-
"@tailwindcss/postcss": "
|
|
13268
|
-
"@types/node": "
|
|
13269
|
-
"@types/react": "
|
|
13270
|
-
"@types/react-dom": "
|
|
13271
|
-
tailwindcss: "
|
|
13272
|
-
tsx: "
|
|
13273
|
-
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"]
|
|
13274
12385
|
}
|
|
13275
12386
|
};
|
|
13276
12387
|
const nextConfig = `import type { NextConfig } from 'next';
|
|
@@ -13590,25 +12701,25 @@ function generateTanStackWebFiles(options) {
|
|
|
13590
12701
|
"@geekmidas/client": GEEKMIDAS_VERSIONS["@geekmidas/client"],
|
|
13591
12702
|
"@geekmidas/envkit": GEEKMIDAS_VERSIONS["@geekmidas/envkit"],
|
|
13592
12703
|
"@tanstack/react-query": DEPENDENCY_VERSIONS["@tanstack/react-query"],
|
|
13593
|
-
"@tanstack/react-router": "
|
|
13594
|
-
"@tanstack/react-start": "
|
|
12704
|
+
"@tanstack/react-router": DEPENDENCY_VERSIONS["@tanstack/react-router"],
|
|
12705
|
+
"@tanstack/react-start": DEPENDENCY_VERSIONS["@tanstack/react-start"],
|
|
13595
12706
|
"better-auth": DEPENDENCY_VERSIONS["better-auth"],
|
|
13596
|
-
react: "
|
|
13597
|
-
"react-dom": "
|
|
12707
|
+
react: DEPENDENCY_VERSIONS["react"],
|
|
12708
|
+
"react-dom": DEPENDENCY_VERSIONS["react-dom"]
|
|
13598
12709
|
},
|
|
13599
12710
|
devDependencies: {
|
|
13600
12711
|
"@geekmidas/cli": GEEKMIDAS_VERSIONS["@geekmidas/cli"],
|
|
13601
|
-
"@tailwindcss/vite": "
|
|
13602
|
-
"@tanstack/router-plugin": "
|
|
13603
|
-
"@types/node": "
|
|
13604
|
-
"@types/react": "
|
|
13605
|
-
"@types/react-dom": "
|
|
13606
|
-
"@vitejs/plugin-react": "
|
|
13607
|
-
tailwindcss: "
|
|
13608
|
-
tsx: "
|
|
13609
|
-
typescript: "
|
|
13610
|
-
vite: "
|
|
13611
|
-
"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"]
|
|
13612
12723
|
}
|
|
13613
12724
|
};
|
|
13614
12725
|
const viteConfig = `import { Credentials } from '@geekmidas/envkit/credentials';
|
|
@@ -14144,21 +13255,14 @@ async function initCommand(projectName, options = {}) {
|
|
|
14144
13255
|
},
|
|
14145
13256
|
{
|
|
14146
13257
|
type: options.yes ? null : "multiselect",
|
|
14147
|
-
name: "
|
|
14148
|
-
message: "
|
|
14149
|
-
choices:
|
|
13258
|
+
name: "constructs",
|
|
13259
|
+
message: "Constructs to declare (space to select, enter to confirm):",
|
|
13260
|
+
choices: constructChoices.map((c) => ({
|
|
14150
13261
|
...c,
|
|
14151
13262
|
selected: true
|
|
14152
13263
|
})),
|
|
14153
13264
|
hint: "- Space to select. Return to submit"
|
|
14154
13265
|
},
|
|
14155
|
-
{
|
|
14156
|
-
type: options.yes ? null : "select",
|
|
14157
|
-
name: "eventsBackend",
|
|
14158
|
-
message: "Event backend:",
|
|
14159
|
-
choices: eventsBackendChoices,
|
|
14160
|
-
initial: 0
|
|
14161
|
-
},
|
|
14162
13266
|
{
|
|
14163
13267
|
type: options.yes ? null : "select",
|
|
14164
13268
|
name: "packageManager",
|
|
@@ -14259,30 +13363,21 @@ async function initCommand(projectName, options = {}) {
|
|
|
14259
13363
|
const template = options.template || answers.template || "api";
|
|
14260
13364
|
const isFullstack = isFullstackTemplate(template);
|
|
14261
13365
|
const monorepo = isFullstack || options.monorepo || false;
|
|
14262
|
-
const
|
|
14263
|
-
|
|
14264
|
-
"
|
|
14265
|
-
"
|
|
14266
|
-
"
|
|
14267
|
-
|
|
14268
|
-
const eventsBackend = options.yes ? "pgboss" : answers.eventsBackend;
|
|
14269
|
-
const services = {
|
|
14270
|
-
db: servicesArray.includes("db"),
|
|
14271
|
-
cache: servicesArray.includes("cache"),
|
|
14272
|
-
mail: servicesArray.includes("mail"),
|
|
14273
|
-
storage: servicesArray.includes("storage"),
|
|
14274
|
-
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")
|
|
14275
13372
|
};
|
|
14276
|
-
if (services.events === "pgboss") services.db = true;
|
|
14277
13373
|
const pkgManager = options.pm ? options.pm : options.yes ? "pnpm" : answers.packageManager ?? detectedPkgManager;
|
|
14278
13374
|
const deployTarget = options.deploy ?? (options.yes ? "none" : answers.deployTarget ?? "none");
|
|
14279
|
-
const database =
|
|
13375
|
+
const database = constructs.database;
|
|
14280
13376
|
const frontendFramework = isFullstack ? options.yes ? "nextjs" : answers.frontendFramework ?? "nextjs" : void 0;
|
|
14281
13377
|
const templateOptions = {
|
|
14282
13378
|
name: name$1,
|
|
14283
13379
|
template,
|
|
14284
13380
|
telescope: options.yes ? true : answers.telescope ?? true,
|
|
14285
|
-
database,
|
|
14286
13381
|
studio: database,
|
|
14287
13382
|
loggerType: options.yes ? "pino" : answers.loggerType ?? "pino",
|
|
14288
13383
|
routesStructure: options.yes ? "centralized-endpoints" : answers.routesStructure ?? "centralized-endpoints",
|
|
@@ -14292,7 +13387,7 @@ async function initCommand(projectName, options = {}) {
|
|
|
14292
13387
|
deployTarget,
|
|
14293
13388
|
stages: resolveStages(options, answers),
|
|
14294
13389
|
...deployTarget === "sst" ? { region: options.region ?? (options.yes ? "eu-west-1" : answers.region?.trim()) } : {},
|
|
14295
|
-
|
|
13390
|
+
constructs,
|
|
14296
13391
|
frontendFramework
|
|
14297
13392
|
};
|
|
14298
13393
|
const targetDir = (0, node_path.join)(cwd, name$1);
|
|
@@ -14303,23 +13398,13 @@ async function initCommand(projectName, options = {}) {
|
|
|
14303
13398
|
await (0, node_fs_promises.mkdir)(targetDir, { recursive: true });
|
|
14304
13399
|
const appDir = isMonorepo$1 ? (0, node_path.join)(targetDir, apiPath) : targetDir;
|
|
14305
13400
|
if (isMonorepo$1) await (0, node_fs_promises.mkdir)(appDir, { recursive: true });
|
|
14306
|
-
const dbApps = [];
|
|
14307
|
-
if (isFullstack && services.db) dbApps.push({
|
|
14308
|
-
name: "api",
|
|
14309
|
-
password: require_fullstack_secrets.generateDbPassword()
|
|
14310
|
-
}, {
|
|
14311
|
-
name: "auth",
|
|
14312
|
-
password: require_fullstack_secrets.generateDbPassword()
|
|
14313
|
-
});
|
|
14314
13401
|
const appFiles = baseTemplate ? [
|
|
14315
13402
|
...generatePackageJson(templateOptions, baseTemplate),
|
|
14316
13403
|
...generateConfigFiles(templateOptions, baseTemplate),
|
|
14317
13404
|
...generateEnvFiles(templateOptions, baseTemplate),
|
|
14318
13405
|
...generateSourceFiles(templateOptions, baseTemplate),
|
|
14319
|
-
...generateTestFiles(templateOptions, baseTemplate)
|
|
14320
|
-
...isMonorepo$1 ? [] : generateDockerFiles(templateOptions, baseTemplate, dbApps)
|
|
13406
|
+
...generateTestFiles(templateOptions, baseTemplate)
|
|
14321
13407
|
] : [];
|
|
14322
|
-
const dockerFiles = isMonorepo$1 && baseTemplate ? generateDockerFiles(templateOptions, baseTemplate, dbApps) : [];
|
|
14323
13408
|
const rootFiles = baseTemplate ? [
|
|
14324
13409
|
...generateAgentFiles(templateOptions, baseTemplate),
|
|
14325
13410
|
...generateGithubFiles(templateOptions),
|
|
@@ -14335,11 +13420,6 @@ async function initCommand(projectName, options = {}) {
|
|
|
14335
13420
|
await (0, node_fs_promises.mkdir)((0, node_path.dirname)(fullPath), { recursive: true });
|
|
14336
13421
|
await (0, node_fs_promises.writeFile)(fullPath, content);
|
|
14337
13422
|
}
|
|
14338
|
-
for (const { path, content } of dockerFiles) {
|
|
14339
|
-
const fullPath = (0, node_path.join)(targetDir, path);
|
|
14340
|
-
await (0, node_fs_promises.mkdir)((0, node_path.dirname)(fullPath), { recursive: true });
|
|
14341
|
-
await (0, node_fs_promises.writeFile)(fullPath, content);
|
|
14342
|
-
}
|
|
14343
13423
|
for (const { path, content } of appFiles) {
|
|
14344
13424
|
const fullPath = (0, node_path.join)(appDir, path);
|
|
14345
13425
|
await (0, node_fs_promises.mkdir)((0, node_path.dirname)(fullPath), { recursive: true });
|
|
@@ -14361,30 +13441,14 @@ async function initCommand(projectName, options = {}) {
|
|
|
14361
13441
|
await (0, node_fs_promises.writeFile)(fullPath, content);
|
|
14362
13442
|
}
|
|
14363
13443
|
console.log("🔐 Initializing encrypted secrets...\n");
|
|
14364
|
-
const secretServices = [];
|
|
14365
|
-
if (services.db) secretServices.push("postgres");
|
|
14366
|
-
if (services.cache) secretServices.push("redis");
|
|
14367
|
-
if (services.storage) secretServices.push("minio");
|
|
14368
|
-
if (services.mail) secretServices.push("mailpit");
|
|
14369
|
-
if (services.events === "sns") secretServices.push("localstack");
|
|
14370
|
-
if (services.events === "rabbitmq") secretServices.push("rabbitmq");
|
|
14371
13444
|
const local = templateOptions.stages.local;
|
|
14372
|
-
const devSecrets = require_generator.createStageSecrets(local,
|
|
14373
|
-
projectName: name$1,
|
|
14374
|
-
eventsBackend: services.events
|
|
14375
|
-
});
|
|
13445
|
+
const devSecrets = require_generator.createStageSecrets(local, [], { projectName: name$1 });
|
|
14376
13446
|
const customSecrets = {
|
|
14377
13447
|
PORT: "3000",
|
|
14378
13448
|
LOG_LEVEL: "debug",
|
|
14379
13449
|
JWT_SECRET: `dev-${Date.now()}-${Math.random().toString(36).slice(2)}`
|
|
14380
13450
|
};
|
|
14381
|
-
if (isFullstack
|
|
14382
|
-
for (const app of dbApps) {
|
|
14383
|
-
const urlKey = `${app.name.toUpperCase()}_DATABASE_URL`;
|
|
14384
|
-
customSecrets[urlKey] = require_fullstack_secrets.generateDbUrl(app.name, app.password, name$1);
|
|
14385
|
-
const passwordKey = `${app.name.toUpperCase()}_DB_PASSWORD`;
|
|
14386
|
-
customSecrets[passwordKey] = app.password;
|
|
14387
|
-
}
|
|
13451
|
+
if (isFullstack) {
|
|
14388
13452
|
customSecrets.AUTH_PORT = "3002";
|
|
14389
13453
|
customSecrets.AUTH_URL = "http://localhost:3002";
|
|
14390
13454
|
customSecrets.BETTER_AUTH_SECRET = `better-auth-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
@@ -14449,12 +13513,8 @@ function printNextSteps(projectName, options, pkgManager) {
|
|
|
14449
13513
|
console.log("\n✅ Project created successfully!\n");
|
|
14450
13514
|
console.log("Next steps:\n");
|
|
14451
13515
|
console.log(` ${cdCommand}`);
|
|
14452
|
-
if (options.services.db && options.monorepo) {
|
|
14453
|
-
console.log(` # Start PostgreSQL (if not running)`);
|
|
14454
|
-
console.log(` docker compose up -d postgres`);
|
|
14455
|
-
}
|
|
14456
13516
|
console.log(` ${devCommand$1}`);
|
|
14457
|
-
if (!options.monorepo && options.
|
|
13517
|
+
if (!options.monorepo && options.constructs.database) {
|
|
14458
13518
|
console.log("");
|
|
14459
13519
|
console.log(" # Then, once the container is up:");
|
|
14460
13520
|
console.log(` gkm exec -- pnpm kysely migrate:latest`);
|
|
@@ -14500,7 +13560,7 @@ function printNextSteps(projectName, options, pkgManager) {
|
|
|
14500
13560
|
console.log("🚀 Deployment (AWS, through SST):");
|
|
14501
13561
|
for (const stage of deployed) console.log(` ${getRunCommand(pkgManager, `deploy:${stage}`)} # gkm build --provider aws, then sst deploy`);
|
|
14502
13562
|
console.log(` Uses your AWS credentials, in ${options.region}.`);
|
|
14503
|
-
if (options.
|
|
13563
|
+
if (options.constructs.mail) console.log(" Set MAIL_FROM to a sender verified in SES first.");
|
|
14504
13564
|
console.log("");
|
|
14505
13565
|
}
|
|
14506
13566
|
console.log("📚 Documentation: https://geekmidas.github.io/toolbox/");
|
|
@@ -14547,14 +13607,6 @@ var NotAnAwsRegion = class extends Error {
|
|
|
14547
13607
|
//#region src/secrets/index.ts
|
|
14548
13608
|
const logger$1 = console;
|
|
14549
13609
|
/**
|
|
14550
|
-
* Extract service names from compose config.
|
|
14551
|
-
*/
|
|
14552
|
-
function getServicesFromConfig(services) {
|
|
14553
|
-
if (!services) return [];
|
|
14554
|
-
if (Array.isArray(services)) return services;
|
|
14555
|
-
return Object.entries(services).filter(([, config]) => config).map(([name$1]) => name$1);
|
|
14556
|
-
}
|
|
14557
|
-
/**
|
|
14558
13610
|
* Initialize secrets for a stage.
|
|
14559
13611
|
* Generates secure random passwords for configured services.
|
|
14560
13612
|
*/
|
|
@@ -14564,9 +13616,6 @@ async function secretsInitCommand(options) {
|
|
|
14564
13616
|
logger$1.error(`Secrets already exist for stage "${stage}". Use --force to overwrite.`);
|
|
14565
13617
|
process.exit(1);
|
|
14566
13618
|
}
|
|
14567
|
-
const config = await require_config.loadConfig();
|
|
14568
|
-
const services = getServicesFromConfig(config.docker?.compose?.services);
|
|
14569
|
-
if (services.length === 0) logger$1.warn("No services configured in docker.compose.services. Creating secrets with empty services.");
|
|
14570
13619
|
let projectName;
|
|
14571
13620
|
let workspaceSecrets;
|
|
14572
13621
|
try {
|
|
@@ -14578,13 +13627,11 @@ async function secretsInitCommand(options) {
|
|
|
14578
13627
|
logger$1.log(" Detected workspace mode — generating per-app secrets");
|
|
14579
13628
|
}
|
|
14580
13629
|
} catch {}
|
|
14581
|
-
const secrets = require_generator.createStageSecrets(stage,
|
|
13630
|
+
const secrets = require_generator.createStageSecrets(stage, [], { projectName });
|
|
14582
13631
|
if (workspaceSecrets) secrets.custom = workspaceSecrets;
|
|
14583
13632
|
await require_storage.writeStageSecrets(secrets);
|
|
14584
13633
|
logger$1.log(`\n✓ Secrets initialized for stage "${stage}"`);
|
|
14585
13634
|
logger$1.log(` Location: .gkm/secrets/${stage}.json`);
|
|
14586
|
-
logger$1.log("\n Generated credentials for:");
|
|
14587
|
-
for (const service of services) logger$1.log(` - ${service}`);
|
|
14588
13635
|
if (secrets.urls.DATABASE_URL) logger$1.log(`\n DATABASE_URL: ${maskUrl(secrets.urls.DATABASE_URL)}`);
|
|
14589
13636
|
if (secrets.urls.REDIS_URL) logger$1.log(` REDIS_URL: ${maskUrl(secrets.urls.REDIS_URL)}`);
|
|
14590
13637
|
if (secrets.urls.RABBITMQ_URL) logger$1.log(` RABBITMQ_URL: ${maskUrl(secrets.urls.RABBITMQ_URL)}`);
|
|
@@ -14758,7 +13805,7 @@ async function testCommand(options = {}) {
|
|
|
14758
13805
|
console.log(`\n🧪 Running tests with ${stage} environment...\n`);
|
|
14759
13806
|
const autoSetup = options.autoSetup || Boolean(process.env.GKM_AUTO_SETUP);
|
|
14760
13807
|
if (autoSetup) {
|
|
14761
|
-
const { ensureStageSecrets } = await Promise.resolve().then(() => require("./setup-
|
|
13808
|
+
const { ensureStageSecrets } = await Promise.resolve().then(() => require("./setup-BrR1_xGy.cjs"));
|
|
14762
13809
|
const generated = await ensureStageSecrets(stage, cwd);
|
|
14763
13810
|
if (generated) console.log(` 🔐 Generated fresh ${stage} secrets (auto-setup)`);
|
|
14764
13811
|
}
|
|
@@ -15395,13 +14442,13 @@ program.command("secrets:push").description("Push secrets to remote provider (SS
|
|
|
15395
14442
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
15396
14443
|
const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await Promise.resolve().then(() => require("./config.cjs"));
|
|
15397
14444
|
const { pushSecrets } = await Promise.resolve().then(() => require("./sync-DYENfOt4.cjs"));
|
|
15398
|
-
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-
|
|
14445
|
+
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-Bhw5fmKu.cjs"));
|
|
15399
14446
|
const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-DlVol_oj.cjs"));
|
|
15400
14447
|
const { workspace } = await loadWorkspaceConfig$1();
|
|
15401
14448
|
const secrets = await readStageSecrets$1(options.stage, workspace.root);
|
|
15402
14449
|
if (secrets) {
|
|
15403
|
-
const { derivedContainers
|
|
15404
|
-
const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers
|
|
14450
|
+
const { derivedContainers } = await Promise.resolve().then(() => require("./workspace-Cji6lE8E.cjs"));
|
|
14451
|
+
const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, options.stage));
|
|
15405
14452
|
if (result) {
|
|
15406
14453
|
await writeStageSecrets$1(result.secrets, workspace.root);
|
|
15407
14454
|
console.log(` Reconciled ${result.addedKeys.length} missing secret(s):`);
|
|
@@ -15422,15 +14469,15 @@ program.command("secrets:pull").description("Pull secrets from remote provider (
|
|
|
15422
14469
|
const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await Promise.resolve().then(() => require("./config.cjs"));
|
|
15423
14470
|
const { pullSecrets } = await Promise.resolve().then(() => require("./sync-DYENfOt4.cjs"));
|
|
15424
14471
|
const { writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-DlVol_oj.cjs"));
|
|
15425
|
-
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-
|
|
14472
|
+
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-Bhw5fmKu.cjs"));
|
|
15426
14473
|
const { workspace } = await loadWorkspaceConfig$1();
|
|
15427
14474
|
let secrets = await pullSecrets(options.stage, workspace);
|
|
15428
14475
|
if (!secrets) {
|
|
15429
14476
|
console.error(`No remote secrets found for stage "${options.stage}".`);
|
|
15430
14477
|
process.exit(1);
|
|
15431
14478
|
}
|
|
15432
|
-
const { derivedContainers
|
|
15433
|
-
const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers
|
|
14479
|
+
const { derivedContainers } = await Promise.resolve().then(() => require("./workspace-Cji6lE8E.cjs"));
|
|
14480
|
+
const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, options.stage));
|
|
15434
14481
|
if (result) {
|
|
15435
14482
|
secrets = result.secrets;
|
|
15436
14483
|
console.log(` Reconciled ${result.addedKeys.length} missing secret(s):`);
|
|
@@ -15448,7 +14495,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
|
|
|
15448
14495
|
const globalOptions = program.opts();
|
|
15449
14496
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
15450
14497
|
const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await Promise.resolve().then(() => require("./config.cjs"));
|
|
15451
|
-
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-
|
|
14498
|
+
const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-Bhw5fmKu.cjs"));
|
|
15452
14499
|
const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-DlVol_oj.cjs"));
|
|
15453
14500
|
const { workspace } = await loadWorkspaceConfig$1();
|
|
15454
14501
|
const stage = options.stage ?? workspace.stages.local;
|
|
@@ -15457,8 +14504,8 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
|
|
|
15457
14504
|
console.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
|
|
15458
14505
|
process.exit(1);
|
|
15459
14506
|
}
|
|
15460
|
-
const { derivedContainers
|
|
15461
|
-
const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers
|
|
14507
|
+
const { derivedContainers } = await Promise.resolve().then(() => require("./workspace-Cji6lE8E.cjs"));
|
|
14508
|
+
const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
|
|
15462
14509
|
if (!result) {
|
|
15463
14510
|
console.log(`\n✓ Secrets for stage "${stage}" are up-to-date`);
|
|
15464
14511
|
return;
|
|
@@ -15516,7 +14563,7 @@ program.command("deploy:github").description("Let GitHub Actions deploy a stage:
|
|
|
15516
14563
|
try {
|
|
15517
14564
|
const globalOptions = program.opts();
|
|
15518
14565
|
if (globalOptions.cwd) process.chdir(globalOptions.cwd);
|
|
15519
|
-
const { deployGithubCommand } = await Promise.resolve().then(() => require("./github-
|
|
14566
|
+
const { deployGithubCommand } = await Promise.resolve().then(() => require("./github-BThvbf2W.cjs"));
|
|
15520
14567
|
await deployGithubCommand(options);
|
|
15521
14568
|
} catch (error) {
|
|
15522
14569
|
console.error(formatError(error));
|