@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.mjs CHANGED
@@ -1,15 +1,14 @@
1
1
  #!/usr/bin/env -S npx tsx
2
2
  import { n as __require, t as __exportAll } from "./rolldown-runtime-BhDjJH2R.mjs";
3
3
  import { i as DEFAULT_CACHE, r as ConstructGenerator, s as isPartitionedRoutes, t as discover } from "./discover-0fhCbnk0.mjs";
4
- import { i as loadConfig, l as derivedApps, n as getAppNameFromCwd, o as loadWorkspaceConfig, r as loadAppConfig, s as parseModuleConfig } from "./config-DBGpRTUy.mjs";
5
- import { A as stripPublicPrefix, C as InvalidStages, D as stageProblems, E as deployedProblems, T as assertDeployedStage, a as getAppBuildOrder, k as getPublicEnvPrefix, o as getAppGkmConfig, s as getDependencyEnvVars, t as allConstructGlobs, v as getDeployTargetError, w as TEST_STAGE, y as isDeployTargetSupported } from "./workspace-Bu3xNCej.mjs";
6
- import { C as providerOf, S as eventsBackendFor, _ as COMPOSE_PATH, a as loadEnvFiles, b as resourceName, c as resolveServicePorts, d as backendsOf, f as constructGlobs, h as usesConstructs, i as isPortAvailable, l as rewriteUrlsWithPorts, m as reconcileWorkspace, n as createEntryWrapper, o as loadSecretsForApp, p as derivedContainers, r as findAvailablePort, s as prepareEntryCredentials, t as createCredentialsPreload, u as startWorkspaceServices, v as LOCAL_CA_PATH, w as storageBackendFor, x as cacheBackendFor, y as applyPostgres } from "./credentials-BpoOUa74.mjs";
7
- import { a as generateServiceCredentials, i as generateSecurePassword, n as generateConnectionUrls, o as rotateServicePassword, t as createStageSecrets } from "./generator-LfZU7NlU.mjs";
4
+ import { a as loadConfig, c as parseModuleConfig, i as loadAppConfig, r as getAppNameFromCwd, s as loadWorkspaceConfig, u as derivedApps } from "./config-DXrrYvob.mjs";
5
+ import { D as assertDeployedStage, E as TEST_STAGE, O as deployedProblems, T as InvalidStages, a as getAppBuildOrder, b as getDeployTargetError, k as stageProblems, m as stripPublicPrefix, o as getAppGkmConfig, p as getPublicEnvPrefix, t as allConstructGlobs, x as isDeployTargetSupported } from "./workspace-T4IVGSoB.mjs";
8
6
  import { c as getKeyPath, i as setCustomSecret, n as readStageSecrets, o as toEmbeddableSecrets, r as secretsExist, s as writeStageSecrets, t as maskPassword } from "./storage-TP0x118M.mjs";
9
- import { a as openapiCommand, c as EndpointGenerator, l as runtimeFor, n as generateOpenApi, o as resolveOpenApiConfig, s as OpenApiTsGenerator, t as OPENAPI_OUTPUT_PATH$1 } from "./openapi-DHQlstv6.mjs";
7
+ import { _ as isPortAvailable, b as prepareEntryCredentials, c as applyPostgres, d as eventsBackendFor, f as providerOf, g as findAvailablePort, h as createEntryWrapper, i as reconcileWorkspace, l as resourceName, m as createCredentialsPreload, n as constructGlobs, o as COMPOSE_PATH, p as storageBackendFor, r as derivedContainers, s as LOCAL_CA_PATH, t as backendsOf, u as cacheBackendFor, v as loadEnvFiles, y as loadSecretsForApp } from "./workspace-CUmR_k4J.mjs";
8
+ import { a as openapiCommand, c as EndpointGenerator, l as runtimeFor, n as generateOpenApi, o as resolveOpenApiConfig, s as OpenApiTsGenerator, t as OPENAPI_OUTPUT_PATH$1 } from "./openapi-3NLVvtz-.mjs";
10
9
  import { i as withRoutes, r as withCompute } from "./emit-Bzq7JsMP.mjs";
11
- import { i as writeDockerEnvFromSecrets, r as generateFullstackCustomSecrets } from "./reconcile-gb1lz1Dk.mjs";
12
- import { a as isRemoteStore, n as pullStageSecrets, r as pushStageSecrets, t as NoStoredSecrets } from "./transfer-B88fTvnh.mjs";
10
+ import { a as createStageSecrets, c as generateServiceCredentials, i as writeDockerEnvFromSecrets, l as rotateServicePassword, o as generateConnectionUrls, r as generateFullstackCustomSecrets, s as generateSecurePassword } from "./reconcile-DhbCvh4K.mjs";
11
+ import { a as isRemoteStore, n as pullStageSecrets, r as pushStageSecrets, t as NoStoredSecrets } from "./transfer-BV00TxZv.mjs";
13
12
  import { createRequire } from "node:module";
14
13
  import { Command } from "commander";
15
14
  import { stdin, stdout } from "node:process";
@@ -22,13 +21,13 @@ import { execFile, execSync, spawn } from "node:child_process";
22
21
  import { cacheTable, canonicalId, cloudName, cookieDomain, dependentsOf, kebabCase, provideKey, provisionOrder, scopedName, serviceKey } from "@geekmidas/manifest";
23
22
  import fg from "fast-glob";
24
23
  import chokidar from "chokidar";
25
- import { parse as parse$1, parseDocument } from "yaml";
26
24
  import { createCipheriv, createHash, randomBytes } from "node:crypto";
25
+ import { fileURLToPath, pathToFileURL } from "node:url";
27
26
  import { ownerRole, readerRole, roleStatements } from "@geekmidas/db/pg/roles";
28
27
  import { Client } from "pg";
28
+ import { parse as parse$1, parseDocument } from "yaml";
29
29
  import { promisify } from "node:util";
30
30
  import { cacheTableStatements } from "@geekmidas/cache/postgres";
31
- import { fileURLToPath, pathToFileURL } from "node:url";
32
31
  import { Cron } from "@geekmidas/constructs/crons";
33
32
  import { Function } from "@geekmidas/constructs/functions";
34
33
  import { Queue } from "@geekmidas/constructs/queue";
@@ -39,7 +38,7 @@ import prompts from "prompts";
39
38
  import { EnvironmentParser } from "@geekmidas/envkit";
40
39
  import { serviceContext } from "@geekmidas/services";
41
40
  //#region package.json
42
- var version = "10.0.0-alpha.25";
41
+ var version = "10.0.0-alpha.27";
43
42
  //#endregion
44
43
  //#region src/auth/credentials.ts
45
44
  /**
@@ -431,10 +430,7 @@ async function execCommand(commandArgs, options = {}) {
431
430
  if (!commandArgs[0]) throw new NoCommandSpecified();
432
431
  const defaultEnv = loadEnvFiles(".env");
433
432
  if (defaultEnv.loaded.length > 0) logger$12.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
434
- const { credentials, secretsJsonPath, appName } = await prepareEntryCredentials({
435
- cwd,
436
- resolveDockerPorts: "readonly"
437
- });
433
+ const { credentials, secretsJsonPath, appName } = await prepareEntryCredentials({ cwd });
438
434
  if (appName) logger$12.log(`📦 App: ${appName}`);
439
435
  const secretCount = Object.keys(credentials).filter((k) => k !== "PORT").length;
440
436
  if (secretCount > 0) logger$12.log(`🔐 Loaded ${secretCount} secret(s)`);
@@ -592,6 +588,7 @@ async function devCommand(options) {
592
588
  return workspaceDevCommand(loadedConfig.workspace, options);
593
589
  }
594
590
  config = loadedConfig.raw;
591
+ workspace = loadedConfig.workspace;
595
592
  }
596
593
  else {
597
594
  const loadedConfig = await loadWorkspaceConfig();
@@ -636,24 +633,13 @@ async function devCommand(options) {
636
633
  const runtime = config.runtime ?? "node";
637
634
  let secretsJsonPath;
638
635
  const appSecrets = await loadSecretsForApp(secretsRoot, config.stages.local, workspaceAppName);
639
- if (workspace && usesConstructs(workspace)) {
636
+ if (workspace) {
640
637
  const reconciled = await reconcileWorkspace(workspace, { stage: workspace.stages.local });
641
638
  if (reconciled.changed && reconciled.plan.containers.length > 0) {
642
639
  logger$11.log(`🐳 Services: ${reconciled.plan.containers.join(", ")}`);
643
640
  for (const [container, address] of Object.entries(reconciled.addresses)) logger$11.log(` ${container}: ${address}`);
644
641
  }
645
642
  Object.assign(appSecrets, reconciled.env);
646
- } else {
647
- const resolvedPorts = await resolveServicePorts(secretsRoot);
648
- if (Object.keys(resolvedPorts.ports).length > 0) {
649
- const rewritten = rewriteUrlsWithPorts(appSecrets, resolvedPorts);
650
- Object.assign(appSecrets, rewritten);
651
- logger$11.log(`🔌 Applied ${Object.keys(resolvedPorts.ports).length} port mapping(s)`);
652
- }
653
- }
654
- if (workspace && workspaceAppName) {
655
- const depEnv = getDependencyEnvVars(workspace, workspaceAppName);
656
- Object.assign(appSecrets, depEnv);
657
643
  }
658
644
  if (Object.keys(appSecrets).length > 0) {
659
645
  const secretsDir = join(secretsRoot, ".gkm");
@@ -663,7 +649,7 @@ async function devCommand(options) {
663
649
  await writeFile(secretsJsonPath, JSON.stringify(appSecrets, null, 2));
664
650
  logger$11.log(`🔐 Loaded ${Object.keys(appSecrets).length} secret(s)`);
665
651
  }
666
- const devServer = new DevServer(resolved.providers[0], options.port ?? workspaceAppPort ?? 3e3, options.portExplicit ?? false, enableOpenApi, telescope, studio, runtime, appRoot, secretsJsonPath, initial.selfServing !== void 0);
652
+ 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);
667
653
  await devServer.start();
668
654
  const hooksFile = (config.hooks?.server?.split("#"))?.[0];
669
655
  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);
@@ -722,19 +708,6 @@ async function devCommand(options) {
722
708
  process.on("SIGTERM", shutdown);
723
709
  }
724
710
  /**
725
- * Generate all dependency environment variables for all apps.
726
- * Returns a flat object with all {APP_NAME}_URL variables.
727
- * @internal Exported for testing
728
- */
729
- function generateAllDependencyEnvVars(workspace, urlPrefix = "http://localhost") {
730
- const env = {};
731
- for (const appName of Object.keys(workspace.apps)) {
732
- const appEnv = getDependencyEnvVars(workspace, appName, urlPrefix);
733
- Object.assign(env, appEnv);
734
- }
735
- return env;
736
- }
737
- /**
738
711
  * Check for port conflicts across all apps.
739
712
  * Returns list of conflicts if any ports are duplicated.
740
713
  * @internal Exported for testing
@@ -959,27 +932,25 @@ async function workspaceDevCommand(workspace, options) {
959
932
  logger$11.log("✅ Frontend apps validated");
960
933
  }
961
934
  const rawSecrets = await loadDevSecrets(workspace);
962
- let secretsEnv;
963
- if (usesConstructs(workspace)) {
964
- const reconciled = await reconcileWorkspace(workspace, { stage: workspace.stages.local });
965
- if (reconciled.changed && reconciled.plan.containers.length > 0) {
966
- logger$11.log(`🐳 Services: ${reconciled.plan.containers.join(", ")}`);
967
- for (const [container, address] of Object.entries(reconciled.addresses)) logger$11.log(` ${container}: ${address}`);
968
- }
969
- secretsEnv = {
970
- ...rawSecrets,
971
- ...reconciled.env
972
- };
973
- } else {
974
- const resolvedPorts = await resolveServicePorts(workspace.root);
975
- await startWorkspaceServices(workspace, resolvedPorts.dockerEnv, rawSecrets);
976
- secretsEnv = rewriteUrlsWithPorts(rawSecrets, resolvedPorts);
935
+ const reconciled = await reconcileWorkspace(workspace, { stage: workspace.stages.local });
936
+ if (reconciled.changed && reconciled.plan.containers.length > 0) {
937
+ logger$11.log(`🐳 Services: ${reconciled.plan.containers.join(", ")}`);
938
+ for (const [container, address] of Object.entries(reconciled.addresses)) logger$11.log(` ${container}: ${address}`);
939
+ }
940
+ const secretsEnv = {
941
+ ...rawSecrets,
942
+ ...reconciled.env
943
+ };
944
+ const appUrls = {};
945
+ for (const resource of reconciled.plan.resources) {
946
+ if (resource.kind !== "rest-api" && resource.kind !== "site") continue;
947
+ const address = reconciled.env[resource.envKey];
948
+ if (address) appUrls[resource.id] = address;
977
949
  }
978
950
  if (Object.keys(secretsEnv).length > 0) logger$11.log(` Loaded ${Object.keys(secretsEnv).length} secret(s)`);
979
- const dependencyEnv = generateAllDependencyEnvVars(workspace);
980
- if (Object.keys(dependencyEnv).length > 0) {
981
- logger$11.log("📡 Dependency URLs:");
982
- for (const [key, value] of Object.entries(dependencyEnv)) logger$11.log(` ${key}=${value}`);
951
+ if (Object.keys(appUrls).length > 0) {
952
+ logger$11.log("🔒 App URLs:");
953
+ for (const [id, address] of Object.entries(appUrls)) logger$11.log(` ${id}: ${address}`);
983
954
  }
984
955
  let turboFilter = [];
985
956
  if (options.app) {
@@ -1023,10 +994,20 @@ async function workspaceDevCommand(workspace, options) {
1023
994
  const turboEnv = {
1024
995
  ...process.env,
1025
996
  ...secretsEnv,
1026
- ...dependencyEnv,
1027
997
  NODE_ENV: "development",
1028
998
  ...configPath ? { GKM_CONFIG_PATH: configPath } : {}
1029
999
  };
1000
+ const scoped = options.app ? [options.app] : options.filter ? [] : Object.keys(workspace.apps);
1001
+ const held = [];
1002
+ for (const appName of scoped) {
1003
+ const port = workspace.apps[appName]?.port;
1004
+ if (port && !await isPortAvailable(port)) held.push({
1005
+ app: appName,
1006
+ port,
1007
+ holder: listenerOn(port)
1008
+ });
1009
+ }
1010
+ if (held.length > 0) throw new WorkspacePortsInUse(held);
1030
1011
  logger$11.log("\n🏃 Starting turbo run dev...\n");
1031
1012
  const turboProcess = spawn("pnpm", [
1032
1013
  "turbo",
@@ -1204,6 +1185,19 @@ var EntryRunner = class {
1204
1185
  */
1205
1186
  function generateServerEntryContent(options) {
1206
1187
  const { secretsJsonPath, runtime = "node", enableOpenApi = false, appImportPath = "./app.js", selfServing = false } = options;
1188
+ const parentWatch = `// Exit when the gkm dev that started this server is gone.
1189
+ const __gkmDevPid = Number(process.env.GKM_DEV_PID);
1190
+ if (__gkmDevPid) {
1191
+ setInterval(() => {
1192
+ try {
1193
+ process.kill(__gkmDevPid, 0);
1194
+ } catch {
1195
+ process.exit(0);
1196
+ }
1197
+ }, 1000).unref();
1198
+ }
1199
+
1200
+ `;
1207
1201
  const credentialsInjection = secretsJsonPath ? `import { existsSync, readFileSync } from 'node:fs';
1208
1202
 
1209
1203
  // Inject dev secrets via globalThis (must happen before app import)
@@ -1234,8 +1228,7 @@ if (existsSync(secretsPath)) {
1234
1228
  * Development server entry point for a surface that serves itself
1235
1229
  * This file is auto-generated by 'gkm dev'
1236
1230
  */
1237
- ${credentialsInjection}
1238
- const port = process.argv.includes('--port')
1231
+ ${credentialsInjection}${parentWatch}const port = process.argv.includes('--port')
1239
1232
  ? Number.parseInt(process.argv[process.argv.indexOf('--port') + 1])
1240
1233
  : 3000;
1241
1234
 
@@ -1250,8 +1243,7 @@ console.log(\`Server started on port \${port}\`);
1250
1243
  * Development server entry point
1251
1244
  * This file is auto-generated by 'gkm dev'
1252
1245
  */
1253
- ${credentialsInjection}
1254
- const port = process.argv.includes('--port')
1246
+ ${credentialsInjection}${parentWatch}const port = process.argv.includes('--port')
1255
1247
  ? Number.parseInt(process.argv[process.argv.indexOf('--port') + 1])
1256
1248
  : 3000;
1257
1249
 
@@ -1273,6 +1265,44 @@ start({
1273
1265
  });
1274
1266
  `;
1275
1267
  }
1268
+ /** A port this app must serve on is held by something else. */
1269
+ var DevPortInUse = class extends Error {
1270
+ port;
1271
+ holder;
1272
+ constructor(port, holder) {
1273
+ 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.`);
1274
+ this.port = port;
1275
+ this.holder = holder;
1276
+ this.name = "DevPortInUse";
1277
+ }
1278
+ };
1279
+ /** Apps whose provisioned ports are held before the workspace starts. */
1280
+ var WorkspacePortsInUse = class extends Error {
1281
+ held;
1282
+ constructor(held) {
1283
+ 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.`);
1284
+ this.held = held;
1285
+ this.name = "WorkspacePortsInUse";
1286
+ }
1287
+ };
1288
+ /** What is listening on a port, as `command (pid N)`, if `lsof` can tell. */
1289
+ function listenerOn(port) {
1290
+ try {
1291
+ const out = execSync(`lsof -nP -iTCP:${port} -sTCP:LISTEN -Fpc`, {
1292
+ encoding: "utf8",
1293
+ stdio: [
1294
+ "ignore",
1295
+ "pipe",
1296
+ "ignore"
1297
+ ]
1298
+ });
1299
+ const pid = out.match(/^p(\d+)/m)?.[1];
1300
+ const command = out.match(/^c(.+)$/m)?.[1];
1301
+ return pid ? `${command ?? "a process"} (pid ${pid})` : void 0;
1302
+ } catch {
1303
+ return;
1304
+ }
1305
+ }
1276
1306
  var DevServer = class {
1277
1307
  provider;
1278
1308
  requestedPort;
@@ -1305,7 +1335,7 @@ var DevServer = class {
1305
1335
  this.startTime = Date.now();
1306
1336
  if (this.isRunning) await this.stop();
1307
1337
  if (this.portExplicit) {
1308
- if (!await 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.`);
1338
+ if (!await isPortAvailable(this.requestedPort)) throw new DevPortInUse(this.requestedPort, listenerOn(this.requestedPort));
1309
1339
  this.actualPort = this.requestedPort;
1310
1340
  } else {
1311
1341
  this.actualPort = await findAvailablePort(this.requestedPort);
@@ -1323,7 +1353,8 @@ var DevServer = class {
1323
1353
  stdio: "inherit",
1324
1354
  env: {
1325
1355
  ...process.env,
1326
- NODE_ENV: "development"
1356
+ NODE_ENV: "development",
1357
+ GKM_DEV_PID: String(process.pid)
1327
1358
  },
1328
1359
  detached: true
1329
1360
  });
@@ -3496,10 +3527,6 @@ var UnprovisionableBucket = class extends Error {
3496
3527
  */
3497
3528
  /**
3498
3529
  * Provision everything the workspace declares, and return what it resolved.
3499
- *
3500
- * Nothing happens for a project that has not adopted the model: `usesConstructs`
3501
- * is the same hard switch reconcile reads, so an existing deploy is untouched
3502
- * until it declares something.
3503
3530
  */
3504
3531
  async function provisionDeclared(options) {
3505
3532
  const { workspace } = options;
@@ -3509,7 +3536,6 @@ async function provisionDeclared(options) {
3509
3536
  provisioned: {},
3510
3537
  clusters: {}
3511
3538
  };
3512
- if (!usesConstructs(workspace)) return empty;
3513
3539
  const manifest = options.manifest ?? await discover({
3514
3540
  patterns: constructGlobs(workspace),
3515
3541
  cwd: workspace.root
@@ -8515,26 +8541,26 @@ function loadPackageJson() {
8515
8541
  * Run: pnpm --filter @geekmidas/cli sync-versions
8516
8542
  */
8517
8543
  const GEEKMIDAS_VERSIONS = {
8518
- "@geekmidas/audit": "~10.0.0-alpha.25",
8519
- "@geekmidas/auth": "~10.0.0-alpha.25",
8520
- "@geekmidas/cache": "~10.0.0-alpha.25",
8521
- "@geekmidas/client": "~10.0.0-alpha.25",
8522
- "@geekmidas/cloud": "~10.0.0-alpha.25",
8523
- "@geekmidas/constructs": "~10.0.0-alpha.25",
8524
- "@geekmidas/db": "~10.0.0-alpha.25",
8525
- "@geekmidas/emailkit": "~10.0.0-alpha.25",
8526
- "@geekmidas/envkit": "~10.0.0-alpha.25",
8527
- "@geekmidas/errors": "~10.0.0-alpha.25",
8528
- "@geekmidas/events": "~10.0.0-alpha.25",
8529
- "@geekmidas/logger": "~10.0.0-alpha.25",
8530
- "@geekmidas/manifest": "~10.0.0-alpha.25",
8531
- "@geekmidas/rate-limit": "~10.0.0-alpha.25",
8532
- "@geekmidas/schema": "~10.0.0-alpha.25",
8533
- "@geekmidas/services": "~10.0.0-alpha.25",
8534
- "@geekmidas/storage": "~10.0.0-alpha.25",
8535
- "@geekmidas/studio": "~10.0.0-alpha.25",
8536
- "@geekmidas/telescope": "~10.0.0-alpha.25",
8537
- "@geekmidas/testkit": "~10.0.0-alpha.25",
8544
+ "@geekmidas/audit": "~10.0.0-alpha.27",
8545
+ "@geekmidas/auth": "~10.0.0-alpha.27",
8546
+ "@geekmidas/cache": "~10.0.0-alpha.27",
8547
+ "@geekmidas/client": "~10.0.0-alpha.27",
8548
+ "@geekmidas/cloud": "~10.0.0-alpha.27",
8549
+ "@geekmidas/constructs": "~10.0.0-alpha.27",
8550
+ "@geekmidas/db": "~10.0.0-alpha.27",
8551
+ "@geekmidas/emailkit": "~10.0.0-alpha.27",
8552
+ "@geekmidas/envkit": "~10.0.0-alpha.27",
8553
+ "@geekmidas/errors": "~10.0.0-alpha.27",
8554
+ "@geekmidas/events": "~10.0.0-alpha.27",
8555
+ "@geekmidas/logger": "~10.0.0-alpha.27",
8556
+ "@geekmidas/manifest": "~10.0.0-alpha.27",
8557
+ "@geekmidas/rate-limit": "~10.0.0-alpha.27",
8558
+ "@geekmidas/schema": "~10.0.0-alpha.27",
8559
+ "@geekmidas/services": "~10.0.0-alpha.27",
8560
+ "@geekmidas/storage": "~10.0.0-alpha.27",
8561
+ "@geekmidas/studio": "~10.0.0-alpha.27",
8562
+ "@geekmidas/telescope": "~10.0.0-alpha.27",
8563
+ "@geekmidas/testkit": "~10.0.0-alpha.27",
8538
8564
  "@geekmidas/cli": `~${loadPackageJson().version}`
8539
8565
  };
8540
8566
  //#endregion
@@ -15480,7 +15506,6 @@ async function testCommand(options = {}) {
15480
15506
  stage,
15481
15507
  startDocker: !options.prepare,
15482
15508
  secretsFileName: "test-secrets.json",
15483
- resolveDockerPorts: "full",
15484
15509
  reconcileStage: "test"
15485
15510
  });
15486
15511
  let finalCredentials = { ...result.credentials };
@@ -15494,14 +15519,13 @@ async function testCommand(options = {}) {
15494
15519
  console.log(` 🔍 Sniffed ${sniffed.requiredEnvVars.length} required env var(s)` + (result.declaredKeys.length > 0 ? `, kept ${result.declaredKeys.length} declared` : ""));
15495
15520
  }
15496
15521
  }
15497
- finalCredentials = rewriteDatabaseUrlForTests(finalCredentials);
15498
15522
  console.log("");
15499
15523
  await writeFile(result.secretsJsonPath, JSON.stringify(finalCredentials, null, 2));
15500
15524
  const gkmDir = join(cwd, ".gkm");
15501
15525
  const preloadPath = join(gkmDir, "test-credentials-preload.ts");
15502
15526
  await createCredentialsPreload(preloadPath, result.secretsJsonPath);
15503
15527
  const workspace = await loadWorkspaceConfig(cwd).then((loaded) => loaded.workspace).catch(() => void 0);
15504
- const manifestPath = workspace && usesConstructs(workspace) ? await writeTestHarness({
15528
+ const manifestPath = workspace ? await writeTestHarness({
15505
15529
  root: workspace.root,
15506
15530
  targets: [workspace.root, ...Object.values(workspace.apps).map((app) => isAbsolute(app.path) ? app.path : join(workspace.root, app.path))],
15507
15531
  patterns: constructGlobs(workspace),
@@ -15545,29 +15569,6 @@ async function testCommand(options = {}) {
15545
15569
  });
15546
15570
  });
15547
15571
  }
15548
- const TEST_DB_SUFFIX = "_test";
15549
- /**
15550
- * Rewrite DATABASE_URL to point to a separate test database.
15551
- * Appends `_test` to the database name (e.g., `app` -> `app_test`).
15552
- * @internal Exported for testing
15553
- */
15554
- function rewriteDatabaseUrlForTests(env) {
15555
- const result = { ...env };
15556
- for (const key of Object.keys(result)) {
15557
- if (!key.includes("DATABASE_URL")) continue;
15558
- const value = result[key];
15559
- try {
15560
- const url = new URL(value);
15561
- const dbName = url.pathname.slice(1);
15562
- if (dbName && !dbName.endsWith(TEST_DB_SUFFIX)) {
15563
- url.pathname = `/${dbName}${TEST_DB_SUFFIX}`;
15564
- result[key] = url.toString();
15565
- console.log(` 🧪 ${key}: using test database "${dbName}${TEST_DB_SUFFIX}"`);
15566
- }
15567
- } catch {}
15568
- }
15569
- return result;
15570
- }
15571
15572
  /** `gkm test` outside a project that declares stages, with none named. */
15572
15573
  var NoStageToTest = class extends Error {
15573
15574
  constructor() {
@@ -16108,7 +16109,7 @@ program.command("secrets:push").description("Send a deployed stage's secrets to
16108
16109
  const globalOptions = program.opts();
16109
16110
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16110
16111
  const { loadWorkspaceConfig } = await import("./config.mjs");
16111
- const { pushStageSecrets } = await import("./transfer-B88fTvnh.mjs").then((n) => n.i);
16112
+ const { pushStageSecrets } = await import("./transfer-BV00TxZv.mjs").then((n) => n.i);
16112
16113
  const { workspace } = await loadWorkspaceConfig();
16113
16114
  const { addedKeys } = await pushStageSecrets(workspace, options.stage, { profile: options.profile });
16114
16115
  for (const key of addedKeys) console.log(` + ${key} (reconciled)`);
@@ -16123,7 +16124,7 @@ program.command("secrets:pull").description("Bring a deployed stage's secrets fr
16123
16124
  const globalOptions = program.opts();
16124
16125
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16125
16126
  const { loadWorkspaceConfig } = await import("./config.mjs");
16126
- const { pullStageSecrets } = await import("./transfer-B88fTvnh.mjs").then((n) => n.i);
16127
+ const { pullStageSecrets } = await import("./transfer-BV00TxZv.mjs").then((n) => n.i);
16127
16128
  const { workspace } = await loadWorkspaceConfig();
16128
16129
  const { addedKeys } = await pullStageSecrets(workspace, options.stage, { profile: options.profile });
16129
16130
  for (const key of addedKeys) console.log(` + ${key} (reconciled)`);
@@ -16138,7 +16139,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
16138
16139
  const globalOptions = program.opts();
16139
16140
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16140
16141
  const { loadWorkspaceConfig } = await import("./config.mjs");
16141
- const { reconcileMissingSecrets } = await import("./reconcile-gb1lz1Dk.mjs").then((n) => n.n);
16142
+ const { reconcileMissingSecrets } = await import("./reconcile-DhbCvh4K.mjs").then((n) => n.n);
16142
16143
  const { readStageSecrets, writeStageSecrets } = await import("./storage-TP0x118M.mjs").then((n) => n.a);
16143
16144
  const { workspace } = await loadWorkspaceConfig();
16144
16145
  const stage = options.stage ?? workspace.stages.local;
@@ -16147,7 +16148,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
16147
16148
  console.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
16148
16149
  process.exit(1);
16149
16150
  }
16150
- const { derivedContainers } = await import("./credentials-BpoOUa74.mjs").then((n) => n.g);
16151
+ const { derivedContainers } = await import("./workspace-CUmR_k4J.mjs").then((n) => n.a);
16151
16152
  const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
16152
16153
  if (!result) {
16153
16154
  console.log(`\n✓ Secrets for stage "${stage}" are up-to-date`);
@@ -16206,7 +16207,7 @@ program.command("deploy:github").description("Let GitHub Actions deploy a stage:
16206
16207
  try {
16207
16208
  const globalOptions = program.opts();
16208
16209
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16209
- const { deployGithubCommand } = await import("./github-BOaa3kQR.mjs");
16210
+ const { deployGithubCommand } = await import("./github-Ba5kXEJo.mjs");
16210
16211
  await deployGithubCommand(options);
16211
16212
  } catch (error) {
16212
16213
  console.error(formatError(error));