@geekmidas/cli 10.0.0-alpha.25 → 10.0.0-alpha.27

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.
Files changed (59) hide show
  1. package/dist/{config-Dn9DW2Fz.cjs → config-7R_-WyHq.cjs} +24 -3
  2. package/dist/{config-Dn9DW2Fz.cjs.map → config-7R_-WyHq.cjs.map} +1 -1
  3. package/dist/{config-DBGpRTUy.mjs → config-DXrrYvob.mjs} +19 -4
  4. package/dist/{config-DBGpRTUy.mjs.map → config-DXrrYvob.mjs.map} +1 -1
  5. package/dist/config.cjs +3 -2
  6. package/dist/config.d.cts +12 -1
  7. package/dist/config.d.cts.map +1 -1
  8. package/dist/config.d.mts +12 -1
  9. package/dist/config.d.mts.map +1 -1
  10. package/dist/config.mjs +3 -3
  11. package/dist/{github-BOaa3kQR.mjs → github-Ba5kXEJo.mjs} +4 -4
  12. package/dist/{github-BOaa3kQR.mjs.map → github-Ba5kXEJo.mjs.map} +1 -1
  13. package/dist/{github-CUjW3G91.cjs → github-CQflOKKV.cjs} +4 -4
  14. package/dist/{github-CUjW3G91.cjs.map → github-CQflOKKV.cjs.map} +1 -1
  15. package/dist/{index-RCUizGxL.d.cts → index-BPN7kghQ.d.cts} +2 -12
  16. package/dist/{index-B4H4XF7A.d.mts.map → index-BPN7kghQ.d.cts.map} +1 -1
  17. package/dist/{index-B4H4XF7A.d.mts → index-BqWKWRm0.d.mts} +2 -12
  18. package/dist/{index-RCUizGxL.d.cts.map → index-BqWKWRm0.d.mts.map} +1 -1
  19. package/dist/index.cjs +170 -169
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.mjs +123 -122
  22. package/dist/index.mjs.map +1 -1
  23. package/dist/{openapi-DHQlstv6.mjs → openapi-3NLVvtz-.mjs} +2 -2
  24. package/dist/{openapi-DHQlstv6.mjs.map → openapi-3NLVvtz-.mjs.map} +1 -1
  25. package/dist/{openapi-BAKQLho-.cjs → openapi-rhJdy2GC.cjs} +2 -2
  26. package/dist/{openapi-BAKQLho-.cjs.map → openapi-rhJdy2GC.cjs.map} +1 -1
  27. package/dist/openapi.cjs +1 -1
  28. package/dist/openapi.mjs +1 -1
  29. package/dist/{generator-LfZU7NlU.mjs → reconcile-DhbCvh4K.mjs} +111 -2
  30. package/dist/reconcile-DhbCvh4K.mjs.map +1 -0
  31. package/dist/{generator-Wdfp0jCS.cjs → reconcile-szmJw_Sb.cjs} +130 -3
  32. package/dist/reconcile-szmJw_Sb.cjs.map +1 -0
  33. package/dist/{transfer-B88fTvnh.mjs → transfer-BV00TxZv.mjs} +4 -4
  34. package/dist/{transfer-B88fTvnh.mjs.map → transfer-BV00TxZv.mjs.map} +1 -1
  35. package/dist/{transfer-D59M4aaR.cjs → transfer-BYIn7z8m.cjs} +4 -4
  36. package/dist/{transfer-D59M4aaR.cjs.map → transfer-BYIn7z8m.cjs.map} +1 -1
  37. package/dist/workspace/index.cjs +1 -2
  38. package/dist/workspace/index.d.cts +2 -2
  39. package/dist/workspace/index.d.mts +2 -2
  40. package/dist/workspace/index.mjs +2 -2
  41. package/dist/{workspace-CRD9YinV.cjs → workspace-Bav3TVrP.cjs} +47 -78
  42. package/dist/workspace-Bav3TVrP.cjs.map +1 -0
  43. package/dist/{credentials-BpoOUa74.mjs → workspace-CUmR_k4J.mjs} +228 -492
  44. package/dist/workspace-CUmR_k4J.mjs.map +1 -0
  45. package/dist/{credentials-Dbyccl3A.cjs → workspace-DgeVgxmm.cjs} +229 -517
  46. package/dist/workspace-DgeVgxmm.cjs.map +1 -0
  47. package/dist/{workspace-Bu3xNCej.mjs → workspace-T4IVGSoB.mjs} +48 -73
  48. package/dist/workspace-T4IVGSoB.mjs.map +1 -0
  49. package/package.json +14 -14
  50. package/dist/credentials-BpoOUa74.mjs.map +0 -1
  51. package/dist/credentials-Dbyccl3A.cjs.map +0 -1
  52. package/dist/generator-LfZU7NlU.mjs.map +0 -1
  53. package/dist/generator-Wdfp0jCS.cjs.map +0 -1
  54. package/dist/reconcile-gb1lz1Dk.mjs +0 -113
  55. package/dist/reconcile-gb1lz1Dk.mjs.map +0 -1
  56. package/dist/reconcile-hx_vBvux.cjs +0 -136
  57. package/dist/reconcile-hx_vBvux.cjs.map +0 -1
  58. package/dist/workspace-Bu3xNCej.mjs.map +0 -1
  59. package/dist/workspace-CRD9YinV.cjs.map +0 -1
package/dist/index.cjs CHANGED
@@ -2,15 +2,14 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
4
4
  const require_discover = require("./discover-B_HtYJCr.cjs");
5
- const require_config = require("./config-Dn9DW2Fz.cjs");
6
- const require_workspace = require("./workspace-CRD9YinV.cjs");
7
- const require_credentials = require("./credentials-Dbyccl3A.cjs");
8
- const require_generator = require("./generator-Wdfp0jCS.cjs");
5
+ const require_config = require("./config-7R_-WyHq.cjs");
6
+ const require_workspace = require("./workspace-Bav3TVrP.cjs");
9
7
  const require_storage = require("./storage-DgypfSD6.cjs");
10
- const require_openapi = require("./openapi-BAKQLho-.cjs");
8
+ const require_workspace$1 = require("./workspace-DgeVgxmm.cjs");
9
+ const require_openapi = require("./openapi-rhJdy2GC.cjs");
11
10
  const require_emit = require("./emit-BdoPFjZf.cjs");
12
- const require_reconcile = require("./reconcile-hx_vBvux.cjs");
13
- const require_transfer = require("./transfer-D59M4aaR.cjs");
11
+ const require_reconcile = require("./reconcile-szmJw_Sb.cjs");
12
+ const require_transfer = require("./transfer-BYIn7z8m.cjs");
14
13
  let commander = require("commander");
15
14
  let node_process = require("node:process");
16
15
  let node_readline_promises = require("node:readline/promises");
@@ -25,13 +24,13 @@ let fast_glob = require("fast-glob");
25
24
  fast_glob = require_rolldown_runtime.__toESM(fast_glob, 1);
26
25
  let chokidar = require("chokidar");
27
26
  chokidar = require_rolldown_runtime.__toESM(chokidar, 1);
28
- let yaml = require("yaml");
29
27
  let node_crypto = require("node:crypto");
28
+ let node_url = require("node:url");
30
29
  let _geekmidas_db_pg_roles = require("@geekmidas/db/pg/roles");
31
30
  let pg = require("pg");
31
+ let yaml = require("yaml");
32
32
  let node_util = require("node:util");
33
33
  let _geekmidas_cache_postgres = require("@geekmidas/cache/postgres");
34
- let node_url = require("node:url");
35
34
  let _geekmidas_constructs_crons = require("@geekmidas/constructs/crons");
36
35
  let _geekmidas_constructs_functions = require("@geekmidas/constructs/functions");
37
36
  let _geekmidas_constructs_queue = require("@geekmidas/constructs/queue");
@@ -44,7 +43,7 @@ prompts = require_rolldown_runtime.__toESM(prompts, 1);
44
43
  let _geekmidas_envkit = require("@geekmidas/envkit");
45
44
  let _geekmidas_services = require("@geekmidas/services");
46
45
  //#region package.json
47
- var version = "10.0.0-alpha.25";
46
+ var version = "10.0.0-alpha.27";
48
47
  //#endregion
49
48
  //#region src/auth/credentials.ts
50
49
  /**
@@ -434,19 +433,16 @@ var NoCommandSpecified = class extends Error {
434
433
  async function execCommand(commandArgs, options = {}) {
435
434
  const cwd = options.cwd ?? process.cwd();
436
435
  if (!commandArgs[0]) throw new NoCommandSpecified();
437
- const defaultEnv = require_credentials.loadEnvFiles(".env");
436
+ const defaultEnv = require_workspace$1.loadEnvFiles(".env");
438
437
  if (defaultEnv.loaded.length > 0) logger$12.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
439
- const { credentials, secretsJsonPath, appName } = await require_credentials.prepareEntryCredentials({
440
- cwd,
441
- resolveDockerPorts: "readonly"
442
- });
438
+ const { credentials, secretsJsonPath, appName } = await require_workspace$1.prepareEntryCredentials({ cwd });
443
439
  if (appName) logger$12.log(`📦 App: ${appName}`);
444
440
  const secretCount = Object.keys(credentials).filter((k) => k !== "PORT").length;
445
441
  if (secretCount > 0) logger$12.log(`🔐 Loaded ${secretCount} secret(s)`);
446
442
  const preloadDir = (0, node_path.join)(cwd, ".gkm");
447
443
  await (0, node_fs_promises.mkdir)(preloadDir, { recursive: true });
448
444
  const preloadPath = (0, node_path.join)(preloadDir, "credentials-preload.mjs");
449
- await require_credentials.createCredentialsPreload(preloadPath, secretsJsonPath);
445
+ await require_workspace$1.createCredentialsPreload(preloadPath, secretsJsonPath);
450
446
  const [cmd, ...args] = commandArgs;
451
447
  logger$12.log(`🚀 Running: ${[cmd, ...args].join(" ")}`);
452
448
  const nodeOptions = [(process.env.NODE_OPTIONS ?? "").replace(/--import[= ]\S+/g, "").trim(), `--import=${preloadPath}`].filter(Boolean).join(" ");
@@ -563,7 +559,7 @@ function getProductionConfigFromGkm(config) {
563
559
  }
564
560
  async function devCommand(options) {
565
561
  if (options.entry) return entryDevCommand(options);
566
- const defaultEnv = require_credentials.loadEnvFiles(".env");
562
+ const defaultEnv = require_workspace$1.loadEnvFiles(".env");
567
563
  if (defaultEnv.loaded.length > 0) logger$11.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
568
564
  const appName = require_config.getAppNameFromCwd();
569
565
  let config;
@@ -597,6 +593,7 @@ async function devCommand(options) {
597
593
  return workspaceDevCommand(loadedConfig.workspace, options);
598
594
  }
599
595
  config = loadedConfig.raw;
596
+ workspace = loadedConfig.workspace;
600
597
  }
601
598
  else {
602
599
  const loadedConfig = await require_config.loadWorkspaceConfig();
@@ -608,7 +605,7 @@ async function devCommand(options) {
608
605
  workspace = loadedConfig.workspace;
609
606
  }
610
607
  if (config.env) {
611
- const { loaded, missing } = require_credentials.loadEnvFiles(config.env, appRoot);
608
+ const { loaded, missing } = require_workspace$1.loadEnvFiles(config.env, appRoot);
612
609
  if (loaded.length > 0) logger$11.log(`📦 Loaded env: ${loaded.join(", ")}`);
613
610
  if (missing.length > 0) logger$11.warn(`⚠️ Missing env files: ${missing.join(", ")}`);
614
611
  }
@@ -629,7 +626,7 @@ async function devCommand(options) {
629
626
  appRoot,
630
627
  providers: ["server"],
631
628
  enableOpenApi,
632
- cacheBackend: require_credentials.cacheBackendFor(require_credentials.providerOf(workspace ?? config)),
629
+ cacheBackend: require_workspace$1.cacheBackendFor(require_workspace$1.providerOf(workspace ?? config)),
633
630
  telescope,
634
631
  studio,
635
632
  hooks,
@@ -640,25 +637,14 @@ async function devCommand(options) {
640
637
  const initial = await build();
641
638
  const runtime = config.runtime ?? "node";
642
639
  let secretsJsonPath;
643
- const appSecrets = await require_credentials.loadSecretsForApp(secretsRoot, config.stages.local, workspaceAppName);
644
- if (workspace && require_credentials.usesConstructs(workspace)) {
645
- const reconciled = await require_credentials.reconcileWorkspace(workspace, { stage: workspace.stages.local });
640
+ const appSecrets = await require_workspace$1.loadSecretsForApp(secretsRoot, config.stages.local, workspaceAppName);
641
+ if (workspace) {
642
+ const reconciled = await require_workspace$1.reconcileWorkspace(workspace, { stage: workspace.stages.local });
646
643
  if (reconciled.changed && reconciled.plan.containers.length > 0) {
647
644
  logger$11.log(`🐳 Services: ${reconciled.plan.containers.join(", ")}`);
648
645
  for (const [container, address] of Object.entries(reconciled.addresses)) logger$11.log(` ${container}: ${address}`);
649
646
  }
650
647
  Object.assign(appSecrets, reconciled.env);
651
- } else {
652
- const resolvedPorts = await require_credentials.resolveServicePorts(secretsRoot);
653
- if (Object.keys(resolvedPorts.ports).length > 0) {
654
- const rewritten = require_credentials.rewriteUrlsWithPorts(appSecrets, resolvedPorts);
655
- Object.assign(appSecrets, rewritten);
656
- logger$11.log(`🔌 Applied ${Object.keys(resolvedPorts.ports).length} port mapping(s)`);
657
- }
658
- }
659
- if (workspace && workspaceAppName) {
660
- const depEnv = require_workspace.getDependencyEnvVars(workspace, workspaceAppName);
661
- Object.assign(appSecrets, depEnv);
662
648
  }
663
649
  if (Object.keys(appSecrets).length > 0) {
664
650
  const secretsDir = (0, node_path.join)(secretsRoot, ".gkm");
@@ -668,7 +654,7 @@ async function devCommand(options) {
668
654
  await (0, node_fs_promises.writeFile)(secretsJsonPath, JSON.stringify(appSecrets, null, 2));
669
655
  logger$11.log(`🔐 Loaded ${Object.keys(appSecrets).length} secret(s)`);
670
656
  }
671
- const devServer = new DevServer(resolved.providers[0], options.port ?? workspaceAppPort ?? 3e3, options.portExplicit ?? false, enableOpenApi, telescope, studio, runtime, appRoot, secretsJsonPath, initial.selfServing !== void 0);
657
+ const devServer = new DevServer(resolved.providers[0], options.port ?? workspaceAppPort ?? 3e3, options.portExplicit ?? workspaceAppPort !== void 0, enableOpenApi, telescope, studio, runtime, appRoot, secretsJsonPath, initial.selfServing !== void 0);
672
658
  await devServer.start();
673
659
  const hooksFile = (config.hooks?.server?.split("#"))?.[0];
674
660
  const normalizedPatterns = [config.constructs, ...hooksFile ? [hooksFile.endsWith(".ts") ? hooksFile : `${hooksFile}.ts`] : []].flat().filter((p) => typeof p === "string").map((p) => p.startsWith("./") ? p.slice(2) : p);
@@ -727,19 +713,6 @@ async function devCommand(options) {
727
713
  process.on("SIGTERM", shutdown);
728
714
  }
729
715
  /**
730
- * Generate all dependency environment variables for all apps.
731
- * Returns a flat object with all {APP_NAME}_URL variables.
732
- * @internal Exported for testing
733
- */
734
- function generateAllDependencyEnvVars(workspace, urlPrefix = "http://localhost") {
735
- const env = {};
736
- for (const appName of Object.keys(workspace.apps)) {
737
- const appEnv = require_workspace.getDependencyEnvVars(workspace, appName, urlPrefix);
738
- Object.assign(env, appEnv);
739
- }
740
- return env;
741
- }
742
- /**
743
716
  * Check for port conflicts across all apps.
744
717
  * Returns list of conflicts if any ports are duplicated.
745
718
  * @internal Exported for testing
@@ -964,27 +937,25 @@ async function workspaceDevCommand(workspace, options) {
964
937
  logger$11.log("✅ Frontend apps validated");
965
938
  }
966
939
  const rawSecrets = await loadDevSecrets(workspace);
967
- let secretsEnv;
968
- if (require_credentials.usesConstructs(workspace)) {
969
- const reconciled = await require_credentials.reconcileWorkspace(workspace, { stage: workspace.stages.local });
970
- if (reconciled.changed && reconciled.plan.containers.length > 0) {
971
- logger$11.log(`🐳 Services: ${reconciled.plan.containers.join(", ")}`);
972
- for (const [container, address] of Object.entries(reconciled.addresses)) logger$11.log(` ${container}: ${address}`);
973
- }
974
- secretsEnv = {
975
- ...rawSecrets,
976
- ...reconciled.env
977
- };
978
- } else {
979
- const resolvedPorts = await require_credentials.resolveServicePorts(workspace.root);
980
- await require_credentials.startWorkspaceServices(workspace, resolvedPorts.dockerEnv, rawSecrets);
981
- secretsEnv = require_credentials.rewriteUrlsWithPorts(rawSecrets, resolvedPorts);
940
+ const reconciled = await require_workspace$1.reconcileWorkspace(workspace, { stage: workspace.stages.local });
941
+ if (reconciled.changed && reconciled.plan.containers.length > 0) {
942
+ logger$11.log(`🐳 Services: ${reconciled.plan.containers.join(", ")}`);
943
+ for (const [container, address] of Object.entries(reconciled.addresses)) logger$11.log(` ${container}: ${address}`);
944
+ }
945
+ const secretsEnv = {
946
+ ...rawSecrets,
947
+ ...reconciled.env
948
+ };
949
+ const appUrls = {};
950
+ for (const resource of reconciled.plan.resources) {
951
+ if (resource.kind !== "rest-api" && resource.kind !== "site") continue;
952
+ const address = reconciled.env[resource.envKey];
953
+ if (address) appUrls[resource.id] = address;
982
954
  }
983
955
  if (Object.keys(secretsEnv).length > 0) logger$11.log(` Loaded ${Object.keys(secretsEnv).length} secret(s)`);
984
- const dependencyEnv = generateAllDependencyEnvVars(workspace);
985
- if (Object.keys(dependencyEnv).length > 0) {
986
- logger$11.log("📡 Dependency URLs:");
987
- for (const [key, value] of Object.entries(dependencyEnv)) logger$11.log(` ${key}=${value}`);
956
+ if (Object.keys(appUrls).length > 0) {
957
+ logger$11.log("🔒 App URLs:");
958
+ for (const [id, address] of Object.entries(appUrls)) logger$11.log(` ${id}: ${address}`);
988
959
  }
989
960
  let turboFilter = [];
990
961
  if (options.app) {
@@ -1028,10 +999,20 @@ async function workspaceDevCommand(workspace, options) {
1028
999
  const turboEnv = {
1029
1000
  ...process.env,
1030
1001
  ...secretsEnv,
1031
- ...dependencyEnv,
1032
1002
  NODE_ENV: "development",
1033
1003
  ...configPath ? { GKM_CONFIG_PATH: configPath } : {}
1034
1004
  };
1005
+ const scoped = options.app ? [options.app] : options.filter ? [] : Object.keys(workspace.apps);
1006
+ const held = [];
1007
+ for (const appName of scoped) {
1008
+ const port = workspace.apps[appName]?.port;
1009
+ if (port && !await require_workspace$1.isPortAvailable(port)) held.push({
1010
+ app: appName,
1011
+ port,
1012
+ holder: listenerOn(port)
1013
+ });
1014
+ }
1015
+ if (held.length > 0) throw new WorkspacePortsInUse(held);
1035
1016
  logger$11.log("\n🏃 Starting turbo run dev...\n");
1036
1017
  const turboProcess = (0, node_child_process.spawn)("pnpm", [
1037
1018
  "turbo",
@@ -1090,16 +1071,16 @@ async function entryDevCommand(options) {
1090
1071
  if (!entry) throw new Error("--entry requires a file path");
1091
1072
  const entryPath = (0, node_path.resolve)(process.cwd(), entry);
1092
1073
  if (!(0, node_fs.existsSync)(entryPath)) throw new Error(`Entry file not found: ${entryPath}`);
1093
- const defaultEnv = require_credentials.loadEnvFiles(".env");
1074
+ const defaultEnv = require_workspace$1.loadEnvFiles(".env");
1094
1075
  if (defaultEnv.loaded.length > 0) logger$11.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
1095
- const { credentials, resolvedPort, secretsJsonPath, appName } = await require_credentials.prepareEntryCredentials({ explicitPort: options.portExplicit ? options.port : void 0 });
1076
+ const { credentials, resolvedPort, secretsJsonPath, appName } = await require_workspace$1.prepareEntryCredentials({ explicitPort: options.portExplicit ? options.port : void 0 });
1096
1077
  if (appName) logger$11.log(`📦 App: ${appName} (port ${resolvedPort})`);
1097
1078
  logger$11.log(`🚀 Starting entry file: ${entry} on port ${resolvedPort}`);
1098
1079
  if (Object.keys(credentials).length > 1) logger$11.log(`🔐 Loaded ${Object.keys(credentials).length - 1} secret(s) + PORT`);
1099
1080
  const wrapperDir = (0, node_path.join)(process.cwd(), ".gkm");
1100
1081
  await (0, node_fs_promises.mkdir)(wrapperDir, { recursive: true });
1101
1082
  const wrapperPath = (0, node_path.join)(wrapperDir, "entry-wrapper.ts");
1102
- await require_credentials.createEntryWrapper(wrapperPath, entryPath, secretsJsonPath);
1083
+ await require_workspace$1.createEntryWrapper(wrapperPath, entryPath, secretsJsonPath);
1103
1084
  const runner = new EntryRunner(wrapperPath, entryPath, watch, resolvedPort);
1104
1085
  await runner.start();
1105
1086
  let isShuttingDown = false;
@@ -1209,6 +1190,19 @@ var EntryRunner = class {
1209
1190
  */
1210
1191
  function generateServerEntryContent(options) {
1211
1192
  const { secretsJsonPath, runtime = "node", enableOpenApi = false, appImportPath = "./app.js", selfServing = false } = options;
1193
+ const parentWatch = `// Exit when the gkm dev that started this server is gone.
1194
+ const __gkmDevPid = Number(process.env.GKM_DEV_PID);
1195
+ if (__gkmDevPid) {
1196
+ setInterval(() => {
1197
+ try {
1198
+ process.kill(__gkmDevPid, 0);
1199
+ } catch {
1200
+ process.exit(0);
1201
+ }
1202
+ }, 1000).unref();
1203
+ }
1204
+
1205
+ `;
1212
1206
  const credentialsInjection = secretsJsonPath ? `import { existsSync, readFileSync } from 'node:fs';
1213
1207
 
1214
1208
  // Inject dev secrets via globalThis (must happen before app import)
@@ -1239,8 +1233,7 @@ if (existsSync(secretsPath)) {
1239
1233
  * Development server entry point for a surface that serves itself
1240
1234
  * This file is auto-generated by 'gkm dev'
1241
1235
  */
1242
- ${credentialsInjection}
1243
- const port = process.argv.includes('--port')
1236
+ ${credentialsInjection}${parentWatch}const port = process.argv.includes('--port')
1244
1237
  ? Number.parseInt(process.argv[process.argv.indexOf('--port') + 1])
1245
1238
  : 3000;
1246
1239
 
@@ -1255,8 +1248,7 @@ console.log(\`Server started on port \${port}\`);
1255
1248
  * Development server entry point
1256
1249
  * This file is auto-generated by 'gkm dev'
1257
1250
  */
1258
- ${credentialsInjection}
1259
- const port = process.argv.includes('--port')
1251
+ ${credentialsInjection}${parentWatch}const port = process.argv.includes('--port')
1260
1252
  ? Number.parseInt(process.argv[process.argv.indexOf('--port') + 1])
1261
1253
  : 3000;
1262
1254
 
@@ -1278,6 +1270,44 @@ start({
1278
1270
  });
1279
1271
  `;
1280
1272
  }
1273
+ /** A port this app must serve on is held by something else. */
1274
+ var DevPortInUse = class extends Error {
1275
+ port;
1276
+ holder;
1277
+ constructor(port, holder) {
1278
+ super(`Port ${port} is already in use${holder ? ` by ${holder}` : ""}. It is the port this app is provisioned on — the one every other app and its CORS origins point at — so dev will not move off it. Stop what is holding it (often a dev server a previous run left behind), or pass -p/--port to run this app somewhere else.`);
1279
+ this.port = port;
1280
+ this.holder = holder;
1281
+ this.name = "DevPortInUse";
1282
+ }
1283
+ };
1284
+ /** Apps whose provisioned ports are held before the workspace starts. */
1285
+ var WorkspacePortsInUse = class extends Error {
1286
+ held;
1287
+ constructor(held) {
1288
+ super(`Ports this workspace's apps are provisioned on are already in use:\n${held.map(({ app, port, holder }) => ` ${app}: ${port}${holder ? ` — held by ${holder}` : ""}`).join("\n")}\nStop what is holding them (often dev servers a previous run left behind) and start again. Nothing was started.`);
1289
+ this.held = held;
1290
+ this.name = "WorkspacePortsInUse";
1291
+ }
1292
+ };
1293
+ /** What is listening on a port, as `command (pid N)`, if `lsof` can tell. */
1294
+ function listenerOn(port) {
1295
+ try {
1296
+ const out = (0, node_child_process.execSync)(`lsof -nP -iTCP:${port} -sTCP:LISTEN -Fpc`, {
1297
+ encoding: "utf8",
1298
+ stdio: [
1299
+ "ignore",
1300
+ "pipe",
1301
+ "ignore"
1302
+ ]
1303
+ });
1304
+ const pid = out.match(/^p(\d+)/m)?.[1];
1305
+ const command = out.match(/^c(.+)$/m)?.[1];
1306
+ return pid ? `${command ?? "a process"} (pid ${pid})` : void 0;
1307
+ } catch {
1308
+ return;
1309
+ }
1310
+ }
1281
1311
  var DevServer = class {
1282
1312
  provider;
1283
1313
  requestedPort;
@@ -1310,10 +1340,10 @@ var DevServer = class {
1310
1340
  this.startTime = Date.now();
1311
1341
  if (this.isRunning) await this.stop();
1312
1342
  if (this.portExplicit) {
1313
- if (!await require_credentials.isPortAvailable(this.requestedPort)) throw new Error(`Port ${this.requestedPort} is already in use. Either stop the process using that port or omit -p/--port to auto-select an available port.`);
1343
+ if (!await require_workspace$1.isPortAvailable(this.requestedPort)) throw new DevPortInUse(this.requestedPort, listenerOn(this.requestedPort));
1314
1344
  this.actualPort = this.requestedPort;
1315
1345
  } else {
1316
- this.actualPort = await require_credentials.findAvailablePort(this.requestedPort);
1346
+ this.actualPort = await require_workspace$1.findAvailablePort(this.requestedPort);
1317
1347
  if (this.actualPort !== this.requestedPort) logger$11.log(`ℹ️ Port ${this.requestedPort} was in use, using port ${this.actualPort} instead`);
1318
1348
  }
1319
1349
  const serverEntryPath = (0, node_path.join)(this.appRoot, ".gkm", this.provider, "server.ts");
@@ -1328,7 +1358,8 @@ var DevServer = class {
1328
1358
  stdio: "inherit",
1329
1359
  env: {
1330
1360
  ...process.env,
1331
- NODE_ENV: "development"
1361
+ NODE_ENV: "development",
1362
+ GKM_DEV_PID: String(process.pid)
1332
1363
  },
1333
1364
  detached: true
1334
1365
  });
@@ -1385,7 +1416,7 @@ var DevServer = class {
1385
1416
  await this.stop();
1386
1417
  let attempts = 0;
1387
1418
  while (attempts < 30) {
1388
- if (await require_credentials.isPortAvailable(portToReuse)) break;
1419
+ if (await require_workspace$1.isPortAvailable(portToReuse)) break;
1389
1420
  await new Promise((resolve) => setTimeout(resolve, 100));
1390
1421
  attempts++;
1391
1422
  }
@@ -2561,7 +2592,7 @@ async function buildCommand(options) {
2561
2592
  }
2562
2593
  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;
2563
2594
  const resolved = resolveProviders(config, options);
2564
- const cacheBackend = require_credentials.cacheBackendFor(require_credentials.providerOf(loadedConfig.workspace));
2595
+ const cacheBackend = require_workspace$1.cacheBackendFor(require_workspace$1.providerOf(loadedConfig.workspace));
2565
2596
  const productionConfigFromGkm = getProductionConfigFromGkm(config);
2566
2597
  const production = normalizeProductionConfig(options.production ?? false, productionConfigFromGkm);
2567
2598
  if (production) logger$9.log(`🏭 Building for PRODUCTION`);
@@ -2995,7 +3026,7 @@ const PROVISIONERS = {
2995
3026
  */
2996
3027
  database: async (declaration, context) => {
2997
3028
  if (declaration.kind !== "database") throw new WrongKind(declaration.kind);
2998
- const name = require_credentials.resourceName(declaration.id, declaration.kind, context.stage);
3029
+ const name = require_workspace$1.resourceName(declaration.id, declaration.kind, context.stage);
2999
3030
  const { postgres } = await context.api.findOrCreatePostgres(serviceName({
3000
3031
  stage: context.stage,
3001
3032
  app: context.project
@@ -3031,7 +3062,7 @@ const PROVISIONERS = {
3031
3062
  if (declaration.kind !== "database-schema") throw new WrongKind(declaration.kind);
3032
3063
  const parent = context.provisioned[declaration.of];
3033
3064
  if (!parent) throw new UnresolvedParent(declaration.id, declaration.of);
3034
- const runtime = require_credentials.resourceName(declaration.id, declaration.kind, context.stage);
3065
+ const runtime = require_workspace$1.resourceName(declaration.id, declaration.kind, context.stage);
3035
3066
  const owner = (0, _geekmidas_db_pg_roles.ownerRole)(runtime);
3036
3067
  const reads = readsFrom(context.manifest, declaration.id);
3037
3068
  const parentUrl = new URL(parent.provides[(0, _geekmidas_manifest.provideKey)(declaration.of, "url")] ?? "");
@@ -3133,7 +3164,7 @@ const PROVISIONERS = {
3133
3164
  stage: context.stage,
3134
3165
  app: context.project
3135
3166
  }, declaration.id);
3136
- const bucket = require_credentials.resourceName(declaration.id, declaration.kind, context.stage);
3167
+ const bucket = require_workspace$1.resourceName(declaration.id, declaration.kind, context.stage);
3137
3168
  const user = `${bucket}-root`;
3138
3169
  const password = derivedPassword(context, `objects:${declaration.id}`);
3139
3170
  const { compose } = await context.api.findOrCreateCompose(service, context.projectId, context.environmentId, minioCompose({
@@ -3501,10 +3532,6 @@ var UnprovisionableBucket = class extends Error {
3501
3532
  */
3502
3533
  /**
3503
3534
  * Provision everything the workspace declares, and return what it resolved.
3504
- *
3505
- * Nothing happens for a project that has not adopted the model: `usesConstructs`
3506
- * is the same hard switch reconcile reads, so an existing deploy is untouched
3507
- * until it declares something.
3508
3535
  */
3509
3536
  async function provisionDeclared(options) {
3510
3537
  const { workspace } = options;
@@ -3514,9 +3541,8 @@ async function provisionDeclared(options) {
3514
3541
  provisioned: {},
3515
3542
  clusters: {}
3516
3543
  };
3517
- if (!require_credentials.usesConstructs(workspace)) return empty;
3518
3544
  const manifest = options.manifest ?? await require_discover.discover({
3519
- patterns: require_credentials.constructGlobs(workspace),
3545
+ patterns: require_workspace$1.constructGlobs(workspace),
3520
3546
  cwd: workspace.root
3521
3547
  });
3522
3548
  if (Object.keys(manifest).length === 0) return empty;
@@ -3528,9 +3554,9 @@ async function provisionDeclared(options) {
3528
3554
  environmentId: options.environmentId,
3529
3555
  stage: options.stage,
3530
3556
  project: workspace.name,
3531
- cache: require_credentials.cacheBackendFor(require_credentials.providerOf(workspace)),
3532
- events: require_credentials.eventsBackendFor(require_credentials.providerOf(workspace)),
3533
- storage: require_credentials.storageBackendFor(require_credentials.providerOf(workspace)),
3557
+ cache: require_workspace$1.cacheBackendFor(require_workspace$1.providerOf(workspace)),
3558
+ events: require_workspace$1.eventsBackendFor(require_workspace$1.providerOf(workspace)),
3559
+ storage: require_workspace$1.storageBackendFor(require_workspace$1.providerOf(workspace)),
3534
3560
  addresses: surfaceAddresses(workspace, manifest, options.appUrls),
3535
3561
  ...options.secrets ? { secrets: options.secrets } : {},
3536
3562
  deferred: [],
@@ -3567,7 +3593,7 @@ async function provisionDeclared(options) {
3567
3593
  * run again.
3568
3594
  */
3569
3595
  async function applyDeclared(client, statements) {
3570
- return (await require_credentials.applyPostgres(client, statements)).filter((entry) => entry.created).length;
3596
+ return (await require_workspace$1.applyPostgres(client, statements)).filter((entry) => entry.created).length;
3571
3597
  }
3572
3598
  /**
3573
3599
  * The provisioning order, with queues and topics moved to the end.
@@ -5257,7 +5283,7 @@ async function workspaceDockerCommand(workspace) {
5257
5283
  await (0, node_fs_promises.writeFile)(dockerignorePath, dockerignore);
5258
5284
  logger$7.log(`\n Generated: .dockerignore (workspace root)`);
5259
5285
  const composePath = await writeConstructsCompose(workspace);
5260
- logger$7.log(`\n✅ Generated ${results.length} Dockerfile(s) + ${require_credentials.COMPOSE_PATH}`);
5286
+ logger$7.log(`\n✅ Generated ${results.length} Dockerfile(s) + ${require_workspace$1.COMPOSE_PATH}`);
5261
5287
  logger$7.log("\n📋 Build commands:");
5262
5288
  for (const result of results) {
5263
5289
  const icon = result.type === "backend" ? "⚙️" : result.type === "mobile" ? "📱" : "🌐";
@@ -5278,18 +5304,18 @@ async function workspaceDockerCommand(workspace) {
5278
5304
  * describes and what `gkm dev` runs cannot disagree. Nothing is started.
5279
5305
  */
5280
5306
  async function writeConstructsCompose(workspace) {
5281
- await require_credentials.reconcileWorkspace(workspace, {
5307
+ await require_workspace$1.reconcileWorkspace(workspace, {
5282
5308
  stage: workspace.stages.local,
5283
5309
  start: false
5284
5310
  });
5285
- logger$7.log(` Generated: ${require_credentials.COMPOSE_PATH} (project root)`);
5286
- return (0, node_path.join)(workspace.root, require_credentials.COMPOSE_PATH);
5311
+ logger$7.log(` Generated: ${require_workspace$1.COMPOSE_PATH} (project root)`);
5312
+ return (0, node_path.join)(workspace.root, require_workspace$1.COMPOSE_PATH);
5287
5313
  }
5288
5314
  function printRunInstructions(workspace) {
5289
5315
  const project = (0, node_fs.existsSync)((0, node_path.join)(workspace.root, "docker-compose.yml")) ? " -f docker-compose.yml" : "";
5290
5316
  logger$7.log("\n📋 Run everything (the apps sit behind the \"apps\" profile):");
5291
5317
  logger$7.log(" gkm setup # starts the containers and creates what they hold");
5292
- logger$7.log(` docker compose -f ${require_credentials.COMPOSE_PATH}${project} --profile apps up --build`);
5318
+ logger$7.log(` docker compose -f ${require_workspace$1.COMPOSE_PATH}${project} --profile apps up --build`);
5293
5319
  }
5294
5320
  //#endregion
5295
5321
  //#region src/deploy/docker.ts
@@ -6972,7 +6998,7 @@ function requiredOf(requirements) {
6972
6998
  async function workspaceDeployCommand(configured, options) {
6973
6999
  const { provider, stage, tag, apps: selectedApps } = options;
6974
7000
  const manifest = await require_discover.discover({
6975
- patterns: require_credentials.constructGlobs(configured),
7001
+ patterns: require_workspace$1.constructGlobs(configured),
6976
7002
  cwd: configured.root
6977
7003
  });
6978
7004
  const units = deployUnits(manifest, configured);
@@ -8526,26 +8552,26 @@ function loadPackageJson() {
8526
8552
  * Run: pnpm --filter @geekmidas/cli sync-versions
8527
8553
  */
8528
8554
  const GEEKMIDAS_VERSIONS = {
8529
- "@geekmidas/audit": "~10.0.0-alpha.25",
8530
- "@geekmidas/auth": "~10.0.0-alpha.25",
8531
- "@geekmidas/cache": "~10.0.0-alpha.25",
8532
- "@geekmidas/client": "~10.0.0-alpha.25",
8533
- "@geekmidas/cloud": "~10.0.0-alpha.25",
8534
- "@geekmidas/constructs": "~10.0.0-alpha.25",
8535
- "@geekmidas/db": "~10.0.0-alpha.25",
8536
- "@geekmidas/emailkit": "~10.0.0-alpha.25",
8537
- "@geekmidas/envkit": "~10.0.0-alpha.25",
8538
- "@geekmidas/errors": "~10.0.0-alpha.25",
8539
- "@geekmidas/events": "~10.0.0-alpha.25",
8540
- "@geekmidas/logger": "~10.0.0-alpha.25",
8541
- "@geekmidas/manifest": "~10.0.0-alpha.25",
8542
- "@geekmidas/rate-limit": "~10.0.0-alpha.25",
8543
- "@geekmidas/schema": "~10.0.0-alpha.25",
8544
- "@geekmidas/services": "~10.0.0-alpha.25",
8545
- "@geekmidas/storage": "~10.0.0-alpha.25",
8546
- "@geekmidas/studio": "~10.0.0-alpha.25",
8547
- "@geekmidas/telescope": "~10.0.0-alpha.25",
8548
- "@geekmidas/testkit": "~10.0.0-alpha.25",
8555
+ "@geekmidas/audit": "~10.0.0-alpha.27",
8556
+ "@geekmidas/auth": "~10.0.0-alpha.27",
8557
+ "@geekmidas/cache": "~10.0.0-alpha.27",
8558
+ "@geekmidas/client": "~10.0.0-alpha.27",
8559
+ "@geekmidas/cloud": "~10.0.0-alpha.27",
8560
+ "@geekmidas/constructs": "~10.0.0-alpha.27",
8561
+ "@geekmidas/db": "~10.0.0-alpha.27",
8562
+ "@geekmidas/emailkit": "~10.0.0-alpha.27",
8563
+ "@geekmidas/envkit": "~10.0.0-alpha.27",
8564
+ "@geekmidas/errors": "~10.0.0-alpha.27",
8565
+ "@geekmidas/events": "~10.0.0-alpha.27",
8566
+ "@geekmidas/logger": "~10.0.0-alpha.27",
8567
+ "@geekmidas/manifest": "~10.0.0-alpha.27",
8568
+ "@geekmidas/rate-limit": "~10.0.0-alpha.27",
8569
+ "@geekmidas/schema": "~10.0.0-alpha.27",
8570
+ "@geekmidas/services": "~10.0.0-alpha.27",
8571
+ "@geekmidas/storage": "~10.0.0-alpha.27",
8572
+ "@geekmidas/studio": "~10.0.0-alpha.27",
8573
+ "@geekmidas/telescope": "~10.0.0-alpha.27",
8574
+ "@geekmidas/testkit": "~10.0.0-alpha.27",
8549
8575
  "@geekmidas/cli": `~${loadPackageJson().version}`
8550
8576
  };
8551
8577
  //#endregion
@@ -14460,7 +14486,7 @@ async function initCommand(projectName, options = {}) {
14460
14486
  }
14461
14487
  console.log("🔐 Initializing encrypted secrets...\n");
14462
14488
  const local = templateOptions.stages.local;
14463
- const devSecrets = require_generator.createStageSecrets(local, [], { projectName: name });
14489
+ const devSecrets = require_reconcile.createStageSecrets(local, [], { projectName: name });
14464
14490
  const customSecrets = {
14465
14491
  PORT: "3000",
14466
14492
  LOG_LEVEL: "debug",
@@ -14645,7 +14671,7 @@ async function secretsInitCommand(options) {
14645
14671
  logger$3.log(" Detected workspace mode — generating per-app secrets");
14646
14672
  }
14647
14673
  } catch {}
14648
- const secrets = require_generator.createStageSecrets(stage, [], { projectName });
14674
+ const secrets = require_reconcile.createStageSecrets(stage, [], { projectName });
14649
14675
  if (workspaceSecrets) secrets.custom = workspaceSecrets;
14650
14676
  await require_storage.writeStageSecrets(secrets);
14651
14677
  logger$3.log(`\n✓ Secrets initialized for stage "${stage}"`);
@@ -14743,13 +14769,13 @@ async function secretsRotateCommand(options) {
14743
14769
  logger$3.error(`Service "${service}" not configured in stage "${stage}"`);
14744
14770
  process.exit(1);
14745
14771
  }
14746
- const updated = require_generator.rotateServicePassword(secrets, service);
14772
+ const updated = require_reconcile.rotateServicePassword(secrets, service);
14747
14773
  await require_storage.writeStageSecrets(updated);
14748
14774
  logger$3.log(`\n✓ Password rotated for ${service} in stage "${stage}"`);
14749
14775
  } else {
14750
14776
  let updated = secrets;
14751
14777
  const services = Object.keys(secrets.services);
14752
- for (const svc of services) updated = require_generator.rotateServicePassword(updated, svc);
14778
+ for (const svc of services) updated = require_reconcile.rotateServicePassword(updated, svc);
14753
14779
  await require_storage.writeStageSecrets(updated);
14754
14780
  logger$3.log(`\n✓ Passwords rotated for all services in stage "${stage}": ${services.join(", ")}`);
14755
14781
  }
@@ -14873,7 +14899,7 @@ function installation(platform, certificate) {
14873
14899
  */
14874
14900
  async function trustCommand(options = {}) {
14875
14901
  const { workspace } = await require_config.loadWorkspaceConfig();
14876
- const certificate = (0, node_path.join)(workspace.root, require_credentials.LOCAL_CA_PATH);
14902
+ const certificate = (0, node_path.join)(workspace.root, require_workspace$1.LOCAL_CA_PATH);
14877
14903
  if (!(0, node_fs.existsSync)(certificate)) throw new NoLocalAuthority(certificate);
14878
14904
  const { describe, steps } = installation(process.platform, certificate);
14879
14905
  logger$2.log(`\n🔐 Trusting the local authority in ${describe}`);
@@ -15016,7 +15042,7 @@ async function setupCommand(options = {}) {
15016
15042
  logger$1.error("❌ Failed to resolve secrets. Exiting.");
15017
15043
  process.exit(1);
15018
15044
  }
15019
- const containers = await require_credentials.derivedContainers(workspace, stage);
15045
+ const containers = await require_workspace$1.derivedContainers(workspace, stage);
15020
15046
  if (isMultiApp && containers.includes("postgres")) {
15021
15047
  await require_reconcile.writeDockerEnvFromSecrets(secrets, workspace.root);
15022
15048
  logger$1.log("📄 Generated docker/.env with database passwords");
@@ -15035,7 +15061,7 @@ async function setupCommand(options = {}) {
15035
15061
  * free and quiet.
15036
15062
  */
15037
15063
  async function reconcileLocal(workspace, stage, options) {
15038
- const result = await require_credentials.reconcileWorkspace(workspace, { stage });
15064
+ const result = await require_workspace$1.reconcileWorkspace(workspace, { stage });
15039
15065
  if (result.plan.containers.length === 0) {
15040
15066
  logger$1.log("🐳 No containers declared");
15041
15067
  return;
@@ -15067,7 +15093,7 @@ async function resolveSecrets(stage, workspace, options) {
15067
15093
  logger$1.log("🔐 Using existing local secrets");
15068
15094
  const secrets = await require_storage.readStageSecrets(stage, workspace.root);
15069
15095
  if (secrets) {
15070
- const reconciled = reconcileSecrets(secrets, workspace, await require_credentials.derivedContainers(workspace, stage));
15096
+ const reconciled = reconcileSecrets(secrets, workspace, await require_workspace$1.derivedContainers(workspace, stage));
15071
15097
  if (reconciled) await require_storage.writeStageSecrets(reconciled, workspace.root);
15072
15098
  return reconciled ?? secrets;
15073
15099
  }
@@ -15115,7 +15141,7 @@ function reconcileSecrets(secrets, workspace, containers = []) {
15115
15141
  let changed = false;
15116
15142
  let result = { ...secrets };
15117
15143
  for (const name of credentialedServices(containers)) if (!result.services[name]) {
15118
- const creds = require_generator.generateServiceCredentials(name);
15144
+ const creds = require_reconcile.generateServiceCredentials(name);
15119
15145
  if (name === "minio") {
15120
15146
  creds.bucket = workspace.name;
15121
15147
  creds.username = workspace.name;
@@ -15127,7 +15153,7 @@ function reconcileSecrets(secrets, workspace, containers = []) {
15127
15153
  [name]: creds
15128
15154
  }
15129
15155
  };
15130
- result.urls = require_generator.generateConnectionUrls(result.services, result.eventsBackend);
15156
+ result.urls = require_reconcile.generateConnectionUrls(result.services, result.eventsBackend);
15131
15157
  logger$1.log(` 🔄 Adding missing service credentials: ${name}`);
15132
15158
  changed = true;
15133
15159
  }
@@ -15140,12 +15166,12 @@ function reconcileSecrets(secrets, workspace, containers = []) {
15140
15166
  host: result.services.postgres.host,
15141
15167
  port: result.services.postgres.port,
15142
15168
  username: "pgboss",
15143
- password: require_generator.generateSecurePassword(),
15169
+ password: require_reconcile.generateSecurePassword(),
15144
15170
  database: result.services.postgres.database ?? "app"
15145
15171
  }
15146
15172
  }
15147
15173
  };
15148
- result.urls = require_generator.generateConnectionUrls(result.services, result.eventsBackend);
15174
+ result.urls = require_reconcile.generateConnectionUrls(result.services, result.eventsBackend);
15149
15175
  logger$1.log(" 🔄 Adding missing service credentials: pgboss");
15150
15176
  changed = true;
15151
15177
  }
@@ -15180,7 +15206,7 @@ function reconcileSecrets(secrets, workspace, containers = []) {
15180
15206
  */
15181
15207
  function createFreshWorkspaceSecrets(stage, workspace, containers = []) {
15182
15208
  const serviceNames = credentialedServices(containers);
15183
- const secrets = require_generator.createStageSecrets(stage, serviceNames, { projectName: workspace.name });
15209
+ const secrets = require_reconcile.createStageSecrets(stage, serviceNames, { projectName: workspace.name });
15184
15210
  if (Object.keys(workspace.apps).length > 1) secrets.custom = require_reconcile.generateFullstackCustomSecrets(workspace, containers);
15185
15211
  else secrets.custom = {
15186
15212
  PORT: "3000",
@@ -15202,7 +15228,7 @@ function createFreshWorkspaceSecrets(stage, workspace, containers = []) {
15202
15228
  async function ensureStageSecrets(stage, cwd = process.cwd()) {
15203
15229
  const { workspace } = await require_config.loadWorkspaceConfig(cwd);
15204
15230
  if (require_storage.secretsExist(stage, workspace.root)) return false;
15205
- const secrets = createFreshWorkspaceSecrets(stage, workspace, await require_credentials.derivedContainers(workspace, stage));
15231
+ const secrets = createFreshWorkspaceSecrets(stage, workspace, await require_workspace$1.derivedContainers(workspace, stage));
15206
15232
  await require_storage.writeStageSecrets(secrets, workspace.root);
15207
15233
  return true;
15208
15234
  }
@@ -15210,7 +15236,7 @@ async function ensureStageSecrets(stage, cwd = process.cwd()) {
15210
15236
  * Generate fresh secrets for the workspace.
15211
15237
  */
15212
15238
  async function generateFreshSecrets(stage, workspace, options) {
15213
- const secrets = createFreshWorkspaceSecrets(stage, workspace, await require_credentials.derivedContainers(workspace, stage));
15239
+ const secrets = createFreshWorkspaceSecrets(stage, workspace, await require_workspace$1.derivedContainers(workspace, stage));
15214
15240
  await require_storage.writeStageSecrets(secrets, workspace.root);
15215
15241
  logger$1.log(` Secrets written to .gkm/secrets/${stage}.json`);
15216
15242
  if (require_transfer.isRemoteStore(workspace, stage) && !options.yes) {
@@ -15485,13 +15511,12 @@ async function testCommand(options = {}) {
15485
15511
  const { ensureStageSecrets } = await Promise.resolve().then(() => setup_exports);
15486
15512
  if (await ensureStageSecrets(stage, cwd)) console.log(` 🔐 Generated fresh ${stage} secrets (auto-setup)`);
15487
15513
  }
15488
- const defaultEnv = require_credentials.loadEnvFiles(".env");
15514
+ const defaultEnv = require_workspace$1.loadEnvFiles(".env");
15489
15515
  if (defaultEnv.loaded.length > 0) console.log(` 📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
15490
- const result = await require_credentials.prepareEntryCredentials({
15516
+ const result = await require_workspace$1.prepareEntryCredentials({
15491
15517
  stage,
15492
15518
  startDocker: !options.prepare,
15493
15519
  secretsFileName: "test-secrets.json",
15494
- resolveDockerPorts: "full",
15495
15520
  reconcileStage: "test"
15496
15521
  });
15497
15522
  let finalCredentials = { ...result.credentials };
@@ -15505,18 +15530,17 @@ async function testCommand(options = {}) {
15505
15530
  console.log(` 🔍 Sniffed ${sniffed.requiredEnvVars.length} required env var(s)` + (result.declaredKeys.length > 0 ? `, kept ${result.declaredKeys.length} declared` : ""));
15506
15531
  }
15507
15532
  }
15508
- finalCredentials = rewriteDatabaseUrlForTests(finalCredentials);
15509
15533
  console.log("");
15510
15534
  await (0, node_fs_promises.writeFile)(result.secretsJsonPath, JSON.stringify(finalCredentials, null, 2));
15511
15535
  const gkmDir = (0, node_path.join)(cwd, ".gkm");
15512
15536
  const preloadPath = (0, node_path.join)(gkmDir, "test-credentials-preload.ts");
15513
- await require_credentials.createCredentialsPreload(preloadPath, result.secretsJsonPath);
15537
+ await require_workspace$1.createCredentialsPreload(preloadPath, result.secretsJsonPath);
15514
15538
  const workspace = await require_config.loadWorkspaceConfig(cwd).then((loaded) => loaded.workspace).catch(() => void 0);
15515
- const manifestPath = workspace && require_credentials.usesConstructs(workspace) ? await writeTestHarness({
15539
+ const manifestPath = workspace ? await writeTestHarness({
15516
15540
  root: workspace.root,
15517
15541
  targets: [workspace.root, ...Object.values(workspace.apps).map((app) => (0, node_path.isAbsolute)(app.path) ? app.path : (0, node_path.join)(workspace.root, app.path))],
15518
- patterns: require_credentials.constructGlobs(workspace),
15519
- cacheBackend: require_credentials.backendsOf(workspace).cache,
15542
+ patterns: require_workspace$1.constructGlobs(workspace),
15543
+ cacheBackend: require_workspace$1.backendsOf(workspace).cache,
15520
15544
  stage: require_workspace.TEST_STAGE,
15521
15545
  env: finalCredentials
15522
15546
  }) : void 0;
@@ -15556,29 +15580,6 @@ async function testCommand(options = {}) {
15556
15580
  });
15557
15581
  });
15558
15582
  }
15559
- const TEST_DB_SUFFIX = "_test";
15560
- /**
15561
- * Rewrite DATABASE_URL to point to a separate test database.
15562
- * Appends `_test` to the database name (e.g., `app` -> `app_test`).
15563
- * @internal Exported for testing
15564
- */
15565
- function rewriteDatabaseUrlForTests(env) {
15566
- const result = { ...env };
15567
- for (const key of Object.keys(result)) {
15568
- if (!key.includes("DATABASE_URL")) continue;
15569
- const value = result[key];
15570
- try {
15571
- const url = new URL(value);
15572
- const dbName = url.pathname.slice(1);
15573
- if (dbName && !dbName.endsWith(TEST_DB_SUFFIX)) {
15574
- url.pathname = `/${dbName}${TEST_DB_SUFFIX}`;
15575
- result[key] = url.toString();
15576
- console.log(` 🧪 ${key}: using test database "${dbName}${TEST_DB_SUFFIX}"`);
15577
- }
15578
- } catch {}
15579
- }
15580
- return result;
15581
- }
15582
15583
  /** `gkm test` outside a project that declares stages, with none named. */
15583
15584
  var NoStageToTest = class extends Error {
15584
15585
  constructor() {
@@ -16119,7 +16120,7 @@ program.command("secrets:push").description("Send a deployed stage's secrets to
16119
16120
  const globalOptions = program.opts();
16120
16121
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16121
16122
  const { loadWorkspaceConfig } = await Promise.resolve().then(() => require("./config.cjs"));
16122
- const { pushStageSecrets } = await Promise.resolve().then(() => require("./transfer-D59M4aaR.cjs")).then((n) => n.transfer_exports);
16123
+ const { pushStageSecrets } = await Promise.resolve().then(() => require("./transfer-BYIn7z8m.cjs")).then((n) => n.transfer_exports);
16123
16124
  const { workspace } = await loadWorkspaceConfig();
16124
16125
  const { addedKeys } = await pushStageSecrets(workspace, options.stage, { profile: options.profile });
16125
16126
  for (const key of addedKeys) console.log(` + ${key} (reconciled)`);
@@ -16134,7 +16135,7 @@ program.command("secrets:pull").description("Bring a deployed stage's secrets fr
16134
16135
  const globalOptions = program.opts();
16135
16136
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16136
16137
  const { loadWorkspaceConfig } = await Promise.resolve().then(() => require("./config.cjs"));
16137
- const { pullStageSecrets } = await Promise.resolve().then(() => require("./transfer-D59M4aaR.cjs")).then((n) => n.transfer_exports);
16138
+ const { pullStageSecrets } = await Promise.resolve().then(() => require("./transfer-BYIn7z8m.cjs")).then((n) => n.transfer_exports);
16138
16139
  const { workspace } = await loadWorkspaceConfig();
16139
16140
  const { addedKeys } = await pullStageSecrets(workspace, options.stage, { profile: options.profile });
16140
16141
  for (const key of addedKeys) console.log(` + ${key} (reconciled)`);
@@ -16149,7 +16150,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
16149
16150
  const globalOptions = program.opts();
16150
16151
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16151
16152
  const { loadWorkspaceConfig } = await Promise.resolve().then(() => require("./config.cjs"));
16152
- const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-hx_vBvux.cjs")).then((n) => n.reconcile_exports);
16153
+ const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-szmJw_Sb.cjs")).then((n) => n.reconcile_exports);
16153
16154
  const { readStageSecrets, writeStageSecrets } = await Promise.resolve().then(() => require("./storage-DgypfSD6.cjs")).then((n) => n.storage_exports);
16154
16155
  const { workspace } = await loadWorkspaceConfig();
16155
16156
  const stage = options.stage ?? workspace.stages.local;
@@ -16158,7 +16159,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
16158
16159
  console.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
16159
16160
  process.exit(1);
16160
16161
  }
16161
- const { derivedContainers } = await Promise.resolve().then(() => require("./credentials-Dbyccl3A.cjs")).then((n) => n.workspace_exports);
16162
+ const { derivedContainers } = await Promise.resolve().then(() => require("./workspace-DgeVgxmm.cjs")).then((n) => n.workspace_exports);
16162
16163
  const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
16163
16164
  if (!result) {
16164
16165
  console.log(`\n✓ Secrets for stage "${stage}" are up-to-date`);
@@ -16217,7 +16218,7 @@ program.command("deploy:github").description("Let GitHub Actions deploy a stage:
16217
16218
  try {
16218
16219
  const globalOptions = program.opts();
16219
16220
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16220
- const { deployGithubCommand } = await Promise.resolve().then(() => require("./github-CUjW3G91.cjs"));
16221
+ const { deployGithubCommand } = await Promise.resolve().then(() => require("./github-CQflOKKV.cjs"));
16221
16222
  await deployGithubCommand(options);
16222
16223
  } catch (error) {
16223
16224
  console.error(formatError(error));