@geekmidas/cli 10.0.0-alpha.67 → 10.0.0-alpha.69

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 (175) hide show
  1. package/dist/{LegacyStateStore-C39UKwmf.mjs → LegacyStateStore-BE1Et8RS.mjs} +2 -2
  2. package/dist/{LegacyStateStore-C39UKwmf.mjs.map → LegacyStateStore-BE1Et8RS.mjs.map} +1 -1
  3. package/dist/{LegacyStateStore-BZiO9-Lw.cjs → LegacyStateStore-LS_uTO8D.cjs} +2 -2
  4. package/dist/{LegacyStateStore-BZiO9-Lw.cjs.map → LegacyStateStore-LS_uTO8D.cjs.map} +1 -1
  5. package/dist/{LocalStateStore-DHVsZLyF.cjs → LocalStateStore-CV6phNMZ.cjs} +2 -2
  6. package/dist/{LocalStateStore-DHVsZLyF.cjs.map → LocalStateStore-CV6phNMZ.cjs.map} +1 -1
  7. package/dist/{LocalStateStore-Dk3Wbvzk.mjs → LocalStateStore-DAnFc708.mjs} +2 -2
  8. package/dist/{LocalStateStore-Dk3Wbvzk.mjs.map → LocalStateStore-DAnFc708.mjs.map} +1 -1
  9. package/dist/{OpenApiTsGenerator-BmUAcR7d.mjs → OpenApiTsGenerator-CSaXs-dB.mjs} +2 -2
  10. package/dist/{OpenApiTsGenerator-BmUAcR7d.mjs.map → OpenApiTsGenerator-CSaXs-dB.mjs.map} +1 -1
  11. package/dist/{OpenApiTsGenerator-B2zrdyP4.cjs → OpenApiTsGenerator-CxB8mqXz.cjs} +2 -2
  12. package/dist/{OpenApiTsGenerator-B2zrdyP4.cjs.map → OpenApiTsGenerator-CxB8mqXz.cjs.map} +1 -1
  13. package/dist/{S3StateStore-B_ZF9eHt.mjs → S3StateStore-8kJEGnTh.mjs} +2 -2
  14. package/dist/{S3StateStore-B_ZF9eHt.mjs.map → S3StateStore-8kJEGnTh.mjs.map} +1 -1
  15. package/dist/{S3StateStore--4tYVMAL.cjs → S3StateStore-dA34LAYm.cjs} +2 -2
  16. package/dist/{S3StateStore--4tYVMAL.cjs.map → S3StateStore-dA34LAYm.cjs.map} +1 -1
  17. package/dist/{SSMStateStore-ACZ8hRvZ.cjs → SSMStateStore-DBzlfc-S.cjs} +2 -2
  18. package/dist/{SSMStateStore-ACZ8hRvZ.cjs.map → SSMStateStore-DBzlfc-S.cjs.map} +1 -1
  19. package/dist/{SSMStateStore-DjPuKqrm.mjs → SSMStateStore-Dkdw-fPL.mjs} +2 -2
  20. package/dist/{SSMStateStore-DjPuKqrm.mjs.map → SSMStateStore-Dkdw-fPL.mjs.map} +1 -1
  21. package/dist/add-CoIr2wgv.cjs +568 -0
  22. package/dist/add-CoIr2wgv.cjs.map +1 -0
  23. package/dist/add-Dzk23476.mjs +566 -0
  24. package/dist/add-Dzk23476.mjs.map +1 -0
  25. package/dist/{bundler-pyLj-gS0.cjs → bundler-CSR2nM4m.cjs} +5 -5
  26. package/dist/{bundler-pyLj-gS0.cjs.map → bundler-CSR2nM4m.cjs.map} +1 -1
  27. package/dist/{bundler-Bq3HaLOZ.mjs → bundler-Cn0JF5G0.mjs} +5 -5
  28. package/dist/{bundler-Bq3HaLOZ.mjs.map → bundler-Cn0JF5G0.mjs.map} +1 -1
  29. package/dist/{cli-DR7B2_fX.cjs → cli-IoM2-x48.cjs} +13 -4
  30. package/dist/cli-IoM2-x48.cjs.map +1 -0
  31. package/dist/{cli-C2Tjbn27.mjs → cli-ij8Lwlww.mjs} +13 -4
  32. package/dist/cli-ij8Lwlww.mjs.map +1 -0
  33. package/dist/{compose-BjToENzG.mjs → compose-CZtinmtw.mjs} +173 -58
  34. package/dist/compose-CZtinmtw.mjs.map +1 -0
  35. package/dist/{compose-5oNHAKNT.cjs → compose-DfIioFX0.cjs} +173 -58
  36. package/dist/compose-DfIioFX0.cjs.map +1 -0
  37. package/dist/{config-4XFxX1al.d.cts → config-BnA0kPTY.d.cts} +3 -3
  38. package/dist/{config-4XFxX1al.d.cts.map → config-BnA0kPTY.d.cts.map} +1 -1
  39. package/dist/{config-UtKzhpFb.d.mts → config-CxEp_g7j.d.mts} +3 -3
  40. package/dist/{config-UtKzhpFb.d.mts.map → config-CxEp_g7j.d.mts.map} +1 -1
  41. package/dist/config.cjs +3 -3
  42. package/dist/config.d.cts +3 -3
  43. package/dist/config.d.mts +3 -3
  44. package/dist/config.mjs +3 -3
  45. package/dist/credentialSchemas-BcNL1N5R.cjs +271 -0
  46. package/dist/credentialSchemas-BcNL1N5R.cjs.map +1 -0
  47. package/dist/credentialSchemas-BmYxnEz_.mjs +236 -0
  48. package/dist/credentialSchemas-BmYxnEz_.mjs.map +1 -0
  49. package/dist/deploy/public.cjs +11 -7
  50. package/dist/deploy/public.d.cts +15 -4
  51. package/dist/deploy/public.d.cts.map +1 -1
  52. package/dist/deploy/public.d.mts +15 -4
  53. package/dist/deploy/public.d.mts.map +1 -1
  54. package/dist/deploy/public.mjs +8 -8
  55. package/dist/{deploy-DRAwTG72.mjs → deploy-BzqbkJ3o.mjs} +11 -7
  56. package/dist/deploy-BzqbkJ3o.mjs.map +1 -0
  57. package/dist/{deploy-BucRfbvX.cjs → deploy-Dpz9raPM.cjs} +11 -7
  58. package/dist/deploy-Dpz9raPM.cjs.map +1 -0
  59. package/dist/{discover-OEfZ9OkA.cjs → discover-B4VjaFJk.cjs} +2 -2
  60. package/dist/{discover-OEfZ9OkA.cjs.map → discover-B4VjaFJk.cjs.map} +1 -1
  61. package/dist/{discover-BFoJWKGD.mjs → discover-Buwf1z4P.mjs} +2 -2
  62. package/dist/{discover-BFoJWKGD.mjs.map → discover-Buwf1z4P.mjs.map} +1 -1
  63. package/dist/{dokploy-m345Zx02.cjs → dokploy-B6O-J1fb.cjs} +2 -2
  64. package/dist/{dokploy-m345Zx02.cjs.map → dokploy-B6O-J1fb.cjs.map} +1 -1
  65. package/dist/{dokploy-DIt1IZl1.mjs → dokploy-DkwcORbv.mjs} +2 -2
  66. package/dist/{dokploy-DIt1IZl1.mjs.map → dokploy-DkwcORbv.mjs.map} +1 -1
  67. package/dist/{encryption-Ccbahik7.mjs → encryption-7m1mKcRy.mjs} +3 -3
  68. package/dist/{encryption-Ccbahik7.mjs.map → encryption-7m1mKcRy.mjs.map} +1 -1
  69. package/dist/{encryption-rZ3BghXy.cjs → encryption-C8Ic5t0C.cjs} +3 -3
  70. package/dist/{encryption-rZ3BghXy.cjs.map → encryption-C8Ic5t0C.cjs.map} +1 -1
  71. package/dist/{engine-Gs0abjuj.mjs → engine-62Kl70Pj.mjs} +146 -28
  72. package/dist/engine-62Kl70Pj.mjs.map +1 -0
  73. package/dist/{engine-Cx_dvx9q.cjs → engine-BR9qlS7A.cjs} +146 -28
  74. package/dist/engine-BR9qlS7A.cjs.map +1 -0
  75. package/dist/{github-CrcGFkfD.cjs → github-CmGIZjhu.cjs} +3 -3
  76. package/dist/{github-CrcGFkfD.cjs.map → github-CmGIZjhu.cjs.map} +1 -1
  77. package/dist/{github-CJANTYqi.mjs → github-DtymyOcH.mjs} +3 -3
  78. package/dist/{github-CJANTYqi.mjs.map → github-DtymyOcH.mjs.map} +1 -1
  79. package/dist/{index-DD1FfUHh.d.mts → index-CkX89m6S.d.mts} +2 -2
  80. package/dist/{index-CyApaHVe.d.cts.map → index-CkX89m6S.d.mts.map} +1 -1
  81. package/dist/{index-CyApaHVe.d.cts → index-EvZBkycu.d.cts} +2 -2
  82. package/dist/{index-DD1FfUHh.d.mts.map → index-EvZBkycu.d.cts.map} +1 -1
  83. package/dist/index.cjs +94 -52
  84. package/dist/index.cjs.map +1 -1
  85. package/dist/index.mjs +94 -52
  86. package/dist/index.mjs.map +1 -1
  87. package/dist/{migrate-DCVo5gLS.mjs → migrate-CEzq6DjL.mjs} +4 -4
  88. package/dist/{migrate-DCVo5gLS.mjs.map → migrate-CEzq6DjL.mjs.map} +1 -1
  89. package/dist/{migrate-CQdSpttI.cjs → migrate-CYC-hBOU.cjs} +4 -4
  90. package/dist/{migrate-CQdSpttI.cjs.map → migrate-CYC-hBOU.cjs.map} +1 -1
  91. package/dist/openapi.cjs +1 -1
  92. package/dist/openapi.d.cts +1 -1
  93. package/dist/openapi.d.mts +1 -1
  94. package/dist/openapi.mjs +1 -1
  95. package/dist/{ready-2s3-CndS.mjs → ready-DeYaSoNN.mjs} +3 -3
  96. package/dist/{ready-2s3-CndS.mjs.map → ready-DeYaSoNN.mjs.map} +1 -1
  97. package/dist/{ready-B6uHr4sm.cjs → ready-QGgjmcuN.cjs} +3 -3
  98. package/dist/{ready-B6uHr4sm.cjs.map → ready-QGgjmcuN.cjs.map} +1 -1
  99. package/dist/reconcile/public.cjs +1 -1
  100. package/dist/reconcile/public.d.cts +1 -1
  101. package/dist/reconcile/public.d.mts +1 -1
  102. package/dist/reconcile/public.mjs +1 -1
  103. package/dist/{resolve-BexmDcsN.cjs → resolve-BDsMffvz.cjs} +3 -3
  104. package/dist/{resolve-BexmDcsN.cjs.map → resolve-BDsMffvz.cjs.map} +1 -1
  105. package/dist/{resolve-DhZgtvZe.mjs → resolve-Dn5wGf1r.mjs} +3 -3
  106. package/dist/{resolve-DhZgtvZe.mjs.map → resolve-Dn5wGf1r.mjs.map} +1 -1
  107. package/dist/{rollback-BnY7KuG_.cjs → rollback-5XHaBDxW.cjs} +5 -5
  108. package/dist/{rollback-BnY7KuG_.cjs.map → rollback-5XHaBDxW.cjs.map} +1 -1
  109. package/dist/{rollback-m4Yeym5x.mjs → rollback-DDfaqv7P.mjs} +5 -5
  110. package/dist/{rollback-m4Yeym5x.mjs.map → rollback-DDfaqv7P.mjs.map} +1 -1
  111. package/dist/{runtime-CEk5n4VH.d.mts → runtime-B3jNgeP-.d.mts} +2 -2
  112. package/dist/{runtime-BRqFOvPM.d.cts.map → runtime-B3jNgeP-.d.mts.map} +1 -1
  113. package/dist/{runtime-BRqFOvPM.d.cts → runtime-DWThEP-r.d.cts} +2 -2
  114. package/dist/{runtime-CEk5n4VH.d.mts.map → runtime-DWThEP-r.d.cts.map} +1 -1
  115. package/dist/sandbox/credentials-worker.cjs +54 -0
  116. package/dist/sandbox/credentials-worker.cjs.map +1 -0
  117. package/dist/sandbox/credentials-worker.d.cts +1 -0
  118. package/dist/sandbox/credentials-worker.d.mts +1 -0
  119. package/dist/sandbox/credentials-worker.mjs +55 -0
  120. package/dist/sandbox/credentials-worker.mjs.map +1 -0
  121. package/dist/sandbox/discover-worker.cjs +1 -1
  122. package/dist/sandbox/discover-worker.mjs +1 -1
  123. package/dist/sandbox/migrate-worker.cjs +1 -1
  124. package/dist/sandbox/migrate-worker.mjs +1 -1
  125. package/dist/{schemas-RUlKHu27.cjs → schemas-BObwxB_t.cjs} +164 -3
  126. package/dist/schemas-BObwxB_t.cjs.map +1 -0
  127. package/dist/{schemas-Cfhtpdo0.mjs → schemas-D-xeKJDD.mjs} +153 -4
  128. package/dist/schemas-D-xeKJDD.mjs.map +1 -0
  129. package/dist/{sniffer-D66HIvi9.cjs → sniffer-Bc0nNBDF.cjs} +7 -7
  130. package/dist/sniffer-Bc0nNBDF.cjs.map +1 -0
  131. package/dist/{sniffer-BdFeLSmA.mjs → sniffer-DMqi2b1q.mjs} +7 -7
  132. package/dist/sniffer-DMqi2b1q.mjs.map +1 -0
  133. package/dist/{state-DgmM4EKq.cjs → state-8RgXeX5Y.cjs} +398 -8
  134. package/dist/state-8RgXeX5Y.cjs.map +1 -0
  135. package/dist/{state-DkUStoqJ.mjs → state-DClnPZt5.mjs} +291 -9
  136. package/dist/state-DClnPZt5.mjs.map +1 -0
  137. package/dist/target/public.cjs +1 -1
  138. package/dist/target/public.d.cts +2 -2
  139. package/dist/target/public.d.mts +2 -2
  140. package/dist/target/public.mjs +1 -1
  141. package/dist/{terminal-C3cv-OQu.mjs → terminal-C6TBo_SA.mjs} +2 -2
  142. package/dist/{terminal-C3cv-OQu.mjs.map → terminal-C6TBo_SA.mjs.map} +1 -1
  143. package/dist/{terminal-DjzgLu6m.cjs → terminal-CFO3xWoF.cjs} +2 -2
  144. package/dist/{terminal-DjzgLu6m.cjs.map → terminal-CFO3xWoF.cjs.map} +1 -1
  145. package/dist/{types-CSw2w69a.d.mts → types-CZdCxUCK.d.cts} +64 -2
  146. package/dist/types-CZdCxUCK.d.cts.map +1 -0
  147. package/dist/{types-D5WIxRDP.d.cts → types-Da0E3Qv7.d.mts} +63 -3
  148. package/dist/types-Da0E3Qv7.d.mts.map +1 -0
  149. package/dist/vitest.cjs +1 -1
  150. package/dist/vitest.mjs +1 -1
  151. package/dist/workspace/index.cjs +1 -1
  152. package/dist/workspace/index.d.cts +2 -2
  153. package/dist/workspace/index.d.mts +2 -2
  154. package/dist/workspace/index.mjs +1 -1
  155. package/dist/{workspace-ON-bqiJz.cjs → workspace-8NuAjaXJ.cjs} +2 -2
  156. package/dist/{workspace-ON-bqiJz.cjs.map → workspace-8NuAjaXJ.cjs.map} +1 -1
  157. package/dist/{workspace-Be6uBhlF.mjs → workspace-B2UK_rVP.mjs} +2 -2
  158. package/dist/{workspace-Be6uBhlF.mjs.map → workspace-B2UK_rVP.mjs.map} +1 -1
  159. package/package.json +16 -13
  160. package/dist/cli-C2Tjbn27.mjs.map +0 -1
  161. package/dist/cli-DR7B2_fX.cjs.map +0 -1
  162. package/dist/compose-5oNHAKNT.cjs.map +0 -1
  163. package/dist/compose-BjToENzG.mjs.map +0 -1
  164. package/dist/deploy-BucRfbvX.cjs.map +0 -1
  165. package/dist/deploy-DRAwTG72.mjs.map +0 -1
  166. package/dist/engine-Cx_dvx9q.cjs.map +0 -1
  167. package/dist/engine-Gs0abjuj.mjs.map +0 -1
  168. package/dist/schemas-Cfhtpdo0.mjs.map +0 -1
  169. package/dist/schemas-RUlKHu27.cjs.map +0 -1
  170. package/dist/sniffer-BdFeLSmA.mjs.map +0 -1
  171. package/dist/sniffer-D66HIvi9.cjs.map +0 -1
  172. package/dist/state-DgmM4EKq.cjs.map +0 -1
  173. package/dist/state-DkUStoqJ.mjs.map +0 -1
  174. package/dist/types-CSw2w69a.d.mts.map +0 -1
  175. package/dist/types-D5WIxRDP.d.cts.map +0 -1
@@ -1,11 +1,12 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-BhDjJH2R.mjs";
2
2
  import { a as runOutput, i as run } from "./run-BJLIerMM.mjs";
3
- import { r as discover } from "./discover-BFoJWKGD.mjs";
3
+ import { r as discover } from "./discover-Buwf1z4P.mjs";
4
4
  import { t as appKey } from "./derive-B4vDpzgP.mjs";
5
5
  import { t as defineTarget } from "./define-DR8xGYfL.mjs";
6
- import { B as pgClient, C as resolveHost, Et as imageRef, F as applyPostgres, G as planFor, H as networkEnv, J as composeBuildPaths, L as postgresStatements, Q as findBuildRoot, S as isMainFrontendApp, U as envFor, V as appEnvKeys, W as localRolePassword, X as TURBO_VERSION, Y as imageLayout, a as recordRelease, b as ensureDockerignore, g as credentialsFileContent, h as credentialsBuildArg, jt as initStageSecrets, k as constructGlobs, m as withGeneratedSecrets, ot as portKeys, p as DeployJournal, q as hostFor, rt as DEFAULT_IMAGES, st as primaryPortKey, t as createEmptyState, v as appDockerfile, w as encryptSecrets, x as validateImageRef, z as composeFor } from "./state-DkUStoqJ.mjs";
6
+ import { A as assertExternalServices, C as isMainFrontendApp, F as reportDevServices, L as suppliedOnly, Q as bucketPolicies, R as encryptSecrets, S as NoDomainForStage, Tt as primaryPortKey, U as constructGlobs, Wt as imageRef, X as applyPolicies, Y as applyBuckets, Yt as initStageSecrets, Z as applyPostgres, a as recordRelease, at as appEnvKeys, b as ensureDockerignore, ct as localRolePassword, dt as hostFor, et as postgresStatements, ft as composeBuildPaths, g as credentialsFileContent, gt as findBuildRoot, h as credentialsBuildArg, it as pgClient, j as devServicesUsed, lt as planFor, m as withGeneratedSecrets, mt as TURBO_VERSION, nt as composeFor, ot as networkEnv, p as DeployJournal, pt as imageLayout, rt as bucketClient, st as envFor, t as createEmptyState, v as appDockerfile, w as resolveHost, wt as portKeys, x as validateImageRef, xt as DEFAULT_IMAGES } from "./state-DClnPZt5.mjs";
7
7
  import { n as DEFAULT_CACHE, r as DEFAULT_EVENTS } from "./fakes-C9RiT6XG.mjs";
8
8
  import { u as migrateDatabases } from "./databases-hUHTuwhx.mjs";
9
+ import { n as assertStageCredentials } from "./credentialSchemas-BmYxnEz_.mjs";
9
10
  import { dirname, join } from "node:path";
10
11
  import { chmod, mkdir, readFile, writeFile } from "node:fs/promises";
11
12
  import { provideKey, provisionOrder, publicEnvFor } from "@geekmidas/manifest";
@@ -261,22 +262,29 @@ function edgeCaddyfile(sites, options) {
261
262
  }` : options.email ? `{
262
263
  email ${options.email}
263
264
  }` : "";
264
- const blocks = sites.map(({ host, upstream }) => `https://${host} {${options.local ? `
265
+ const blocks = sites.map(({ host, upstream, rewrite }) => `https://${host} {${options.local ? `
265
266
  tls internal
266
- ` : ""}
267
+ ` : ""}${rewrite ? `
268
+ # A bucket, served at a host of its own: the bucket is a prefix on MinIO,
269
+ # which routes and signs on the Host header — so it is sent the upstream's.
270
+ reverse_proxy ${upstream} {
271
+ rewrite ${rewrite}
272
+ header_up Host {upstream_hostport}
273
+ }` : `
267
274
  # Host and X-Forwarded-* are passed through as Caddy does by default, so
268
275
  # an app builds redirects, cookies and links on the address a caller used.
269
276
  reverse_proxy ${upstream} {
270
277
  # Never buffer: a streamed response — server-sent events, a Next.js
271
278
  # RSC payload — reaches the client as each chunk is written.
272
279
  flush_interval -1
273
- }
280
+ }`}
274
281
  }`);
275
282
  return `${HEADER}${global ? `${global}\n\n` : ""}${blocks.join("\n\n")}\n`;
276
283
  }
277
284
  const HEADER = `# Generated by gkm compose from the construct manifest — do not edit.
278
285
  #
279
- # One host per API and site, each in front of its own container.
286
+ # One host per API and site, each in front of its own container, and one per
287
+ # file server in front of the stack's MinIO.
280
288
  `;
281
289
  //#endregion
282
290
  //#region src/compose/stack.ts
@@ -317,42 +325,32 @@ const EDGE_PORT_ENV = {
317
325
  /**
318
326
  * Containers this target does not run.
319
327
  *
320
- * Object storage is the stage's own — an externally hosted bucket whose URL
321
- * the stage's secrets hold — and so is a deployed stage's mail. The AWS
322
- * emulator never applies: a stack is a server target, whose events are
323
- * pg-boss.
328
+ * The AWS emulator never applies: a stack is a server target, whose events
329
+ * are pg-boss. Reconcile's Caddy fronts `gkm dev`'s host processes; the stack
330
+ * brings its own edge.
331
+ *
332
+ * MinIO and Mailpit run on the local stage always, and on a deployed stage
333
+ * only where `--allow-dev-services` allowed them and its secrets configure no
334
+ * real bucket or mail server — see `devServices.ts`.
324
335
  */
325
336
  const NOT_RUN = {
326
- minio: true,
327
337
  localstack: true,
328
338
  caddy: true
329
339
  };
330
- /** Kinds whose `<ID>_URL` a stage must be given, because nothing here runs them. */
331
- const EXTERNAL_KINDS = {
332
- objects: true,
333
- "file-server": true
340
+ /** The kinds a deployed stage takes from its secrets, or a dev service. */
341
+ const SERVICE_KINDS = {
342
+ email: "email",
343
+ objects: "objects",
344
+ "file-server": "file-server"
334
345
  };
346
+ /** The region MinIO is addressed with. It has no regions; the SDK wants one. */
347
+ const MINIO_REGION = "us-east-1";
335
348
  /** The S3 client's credentials, read beside a bucket's URL when it has them. */
336
349
  const STORAGE_KEYS = [
337
350
  "AWS_ACCESS_KEY_ID",
338
351
  "AWS_SECRET_ACCESS_KEY",
339
352
  "AWS_REGION"
340
353
  ];
341
- /** A bucket the stack needs and the stage was not given. */
342
- var BucketNotConfigured = class extends Error {
343
- app;
344
- id;
345
- key;
346
- stage;
347
- constructor(app, id, key, stage) {
348
- super(`'${app}' uses the bucket '${id}', and gkm compose runs no object storage: the stage needs an externally hosted bucket. Set its URL in the stage's secrets: gkm secrets:set ${key} 's3://…' --stage ${stage}`);
349
- this.app = app;
350
- this.id = id;
351
- this.key = key;
352
- this.stage = stage;
353
- this.name = "BucketNotConfigured";
354
- }
355
- };
356
354
  /** A value only the stage's secrets can hold, and they do not. */
357
355
  var StageSecretMissing = class extends Error {
358
356
  app;
@@ -370,7 +368,7 @@ var StageSecretMissing = class extends Error {
370
368
  var StageSeedMissing = class extends Error {
371
369
  stage;
372
370
  constructor(stage) {
373
- super(`The stage '${stage}' has no seed in its secrets, and a deployed stage's database passwords are derived from it. Run gkm compose without --dry-run once, or gkm deploy, to generate it.`);
371
+ super(`The stage '${stage}' has no seed in its secrets, and a deployed stage's database passwords are derived from it. Run gkm compose --stage ${stage} without --dry-run once, or gkm deploy, to generate it.`);
374
372
  this.stage = stage;
375
373
  this.name = "StageSeedMissing";
376
374
  }
@@ -439,7 +437,6 @@ function composeStack(input) {
439
437
  master: local ? "geekmidas" : localRolePassword(workspace.name, plan, "master", seed),
440
438
  ...seed ? { seed } : {}
441
439
  };
442
- const infra = plan.containers.filter((c) => !NOT_RUN[c]).filter((c) => local || c !== "mailpit").sort();
443
440
  const apps = stackApps(workspace, manifest, plan, {
444
441
  local,
445
442
  stage,
@@ -448,10 +445,47 @@ function composeStack(input) {
448
445
  images: input.images
449
446
  });
450
447
  if (apps.length === 0) throw new NothingToCompose(workspace.root);
448
+ const owners = /* @__PURE__ */ new Map();
449
+ for (const resource of plan.resources) {
450
+ owners.set(resource.envKey, resource);
451
+ if (resource.kind === "email") {
452
+ owners.set(provideKey(resource.id, "from"), resource);
453
+ owners.set(provideKey(resource.id, "inboxUrl"), resource);
454
+ }
455
+ if (resource.kind === "external-api") owners.set(provideKey(resource.id, "credentials"), resource);
456
+ }
457
+ const reads = /* @__PURE__ */ new Map();
458
+ for (const app of apps) {
459
+ const site = manifest[app.id];
460
+ reads.set(app.name, site?.kind === "site" ? Object.values(publicEnvFor(site, manifest)) : [...appEnvKeys(manifest, app.name, input.runnables) ?? []]);
461
+ }
462
+ const domain = workspace.deploy?.domains?.[stage];
463
+ const services = local ? {
464
+ missing: [],
465
+ minio: plan.resources.filter((r) => r.kind === "objects").map((r) => r.id),
466
+ mailpit: plan.resources.filter((r) => r.kind === "email").map((r) => r.id)
467
+ } : assertExternalServices({
468
+ stage,
469
+ declarations: serviceDeclarations(plan, reads, owners),
470
+ supplied: custom,
471
+ allow: input.allowDevServices ?? [],
472
+ ...domain ? { domain } : {}
473
+ });
474
+ const infra = plan.containers.filter((c) => !NOT_RUN[c]).filter((c) => c !== "minio" || services.minio.length > 0).filter((c) => c !== "mailpit" || services.mailpit.length > 0).sort();
475
+ const storage = infra.includes("minio") ? (() => {
476
+ const buckets = services.minio.map((id) => byId.get(id)?.name).filter((name) => Boolean(name)).sort();
477
+ return {
478
+ user: custom.AWS_ACCESS_KEY_ID ?? (local ? "geekmidas" : `${workspace.name}-minio`),
479
+ password: custom.AWS_SECRET_ACCESS_KEY ?? (local ? "geekmidas" : localRolePassword(workspace.name, plan, "minio", seed)),
480
+ buckets,
481
+ policies: bucketPolicies(plan).filter((p) => buckets.includes(p.bucket))
482
+ };
483
+ })() : void 0;
451
484
  const derivation = {
452
485
  project: workspace.name,
453
486
  master: credential.master,
454
- ...credential.seed ? { seed: credential.seed } : {}
487
+ ...credential.seed ? { seed: credential.seed } : {},
488
+ ...!local && domain ? { mailFrom: `noreply@${domain}` } : {}
455
489
  };
456
490
  const outside = networkEnv(plan, {
457
491
  ...derivation,
@@ -461,14 +495,27 @@ function composeStack(input) {
461
495
  ...derivation,
462
496
  addresses: Object.fromEntries(apps.map((app) => [app.id, `http://${app.name}:${app.port}`]))
463
497
  });
464
- const owners = /* @__PURE__ */ new Map();
465
- for (const resource of plan.resources) {
466
- owners.set(resource.envKey, resource);
467
- if (resource.kind === "email") {
468
- owners.set(provideKey(resource.id, "from"), resource);
469
- owners.set(provideKey(resource.id, "inboxUrl"), resource);
498
+ const fileServers = /* @__PURE__ */ new Map();
499
+ if (storage) for (const resource of plan.resources) {
500
+ if (resource.kind !== "file-server" || !resource.of) continue;
501
+ if (!services.minio.includes(resource.of)) continue;
502
+ if (custom[resource.envKey] !== void 0) continue;
503
+ const bucket = byId.get(resource.of);
504
+ if (!bucket) continue;
505
+ let host;
506
+ if (local) host = hostFor(resource, workspace.name);
507
+ else {
508
+ if (!domain) throw new NoDomainForStage(stage);
509
+ host = `${resource.subdomain ?? appKey(resource.id)}.${domain}`;
470
510
  }
471
- if (resource.kind === "external-api") owners.set(provideKey(resource.id, "credentials"), resource);
511
+ fileServers.set(resource.id, {
512
+ url: `https://${host}${local && https !== 443 ? `:${https}` : ""}`,
513
+ site: {
514
+ host,
515
+ upstream: "minio:9000",
516
+ rewrite: `/${bucket.name}{uri}`
517
+ }
518
+ });
472
519
  }
473
520
  const surfaceUrls = new Map(plan.resources.filter((r) => r.kind === "rest-api" || r.kind === "site").map((r) => [r.envKey, r.id]));
474
521
  const appById = new Map(apps.map((app) => [app.id, app]));
@@ -477,12 +524,12 @@ function composeStack(input) {
477
524
  * they can hold it, and the derivations otherwise.
478
525
  */
479
526
  const valueFor = (app, key, owner) => {
480
- if (owner && EXTERNAL_KINDS[owner.kind] && key === owner.envKey) {
481
- const value = custom[key];
482
- if (value === void 0) throw new BucketNotConfigured(app.name, owner.id, key, stage);
483
- return value;
527
+ if (owner && SERVICE_KINDS[owner.kind]) {
528
+ if (custom[key] !== void 0) return custom[key];
529
+ if (owner.kind === "file-server") return fileServers.get(owner.id)?.url;
530
+ return outside[key];
484
531
  }
485
- if (owner && suppliedBy(owner, key, local)) {
532
+ if (owner && suppliedOnly(owner.kind, owner.id, key, local)) {
486
533
  const value = custom[key];
487
534
  if (value === void 0) throw new StageSecretMissing(app.name, key, stage);
488
535
  return value;
@@ -510,7 +557,8 @@ function composeStack(input) {
510
557
  const owner = owners.get(key) ?? storageOwner(key, plan);
511
558
  if (!local && owner?.kind === "email" && key.endsWith("_INBOX_URL")) continue;
512
559
  if (STORAGE_KEYS.includes(key)) {
513
- if (custom[key] !== void 0) values[key] = custom[key];
560
+ const value = custom[key] ?? storageValue(key, storage);
561
+ if (value !== void 0) values[key] = value;
514
562
  continue;
515
563
  }
516
564
  const value = valueFor(app, key, owner);
@@ -537,16 +585,17 @@ function composeStack(input) {
537
585
  infra,
538
586
  apps,
539
587
  master: credential.master,
588
+ ...storage ? { storage } : {},
540
589
  https,
541
590
  http,
542
591
  composeDir: join(workspace.root, stackDir(stage)),
543
592
  buildRoot: layout.buildRoot,
544
593
  workspaceRoot: workspace.root
545
594
  });
546
- const caddyfile = edgeCaddyfile(apps.map((app) => ({
595
+ const caddyfile = edgeCaddyfile([...apps.map((app) => ({
547
596
  host: app.host,
548
597
  upstream: `${app.name}:${app.port}`
549
- })), { local });
598
+ })), ...[...fileServers.values()].map(({ site }) => site)], { local });
550
599
  const dockerfiles = {};
551
600
  for (const app of apps) {
552
601
  if (!app.build) continue;
@@ -566,16 +615,49 @@ function composeStack(input) {
566
615
  caddyfile,
567
616
  dockerfiles,
568
617
  plan,
569
- credential
618
+ credential,
619
+ ...storage ? { storage } : {},
620
+ devServices: local ? [] : devServicesUsed(services)
621
+ };
622
+ }
623
+ /**
624
+ * The mail and storage constructs a deployed stage's apps read, with who
625
+ * reads each — and every file server's bucket, which serves it whether or
626
+ * not an app reads it directly.
627
+ */
628
+ function serviceDeclarations(plan, reads, owners) {
629
+ const byId = new Map(plan.resources.map((r) => [r.id, r]));
630
+ const readers = /* @__PURE__ */ new Map();
631
+ const add = (id, app) => {
632
+ const set = readers.get(id) ?? /* @__PURE__ */ new Set();
633
+ if (app) set.add(app);
634
+ readers.set(id, set);
570
635
  };
571
- /** Whether only the stage's secrets can supply `key` to `owner`. */
572
- function suppliedBy(owner, key, isLocal) {
573
- if (owner.kind === "credential") return true;
574
- if (owner.kind === "external-api") return key === provideKey(owner.id, "credentials");
575
- if (isLocal) return false;
576
- if (owner.kind === "secret" || owner.kind === "encryption") return true;
577
- return owner.kind === "email" && !key.endsWith("_INBOX_URL");
636
+ for (const [app, keys] of reads) for (const key of keys) {
637
+ const owner = owners.get(key);
638
+ if (!owner || !SERVICE_KINDS[owner.kind]) continue;
639
+ if (owner.kind === "email" && key.endsWith("_INBOX_URL")) continue;
640
+ add(owner.id, app);
641
+ if (owner.kind === "file-server" && owner.of) add(owner.of);
578
642
  }
643
+ return [...readers].flatMap(([id, apps]) => {
644
+ const resource = byId.get(id);
645
+ const kind = resource && SERVICE_KINDS[resource.kind];
646
+ if (!resource || !kind) return [];
647
+ return [{
648
+ id,
649
+ kind,
650
+ ...resource.of ? { of: resource.of } : {},
651
+ apps: [...apps].sort()
652
+ }];
653
+ });
654
+ }
655
+ /** The stack's MinIO's value for one of the S3 client's keys. */
656
+ function storageValue(key, storage) {
657
+ if (!storage) return void 0;
658
+ if (key === "AWS_ACCESS_KEY_ID") return storage.user;
659
+ if (key === "AWS_SECRET_ACCESS_KEY") return storage.password;
660
+ if (key === "AWS_REGION") return MINIO_REGION;
579
661
  }
580
662
  /** Which bucket an S3 credential key belongs to — the first one declared. */
581
663
  function storageOwner(key, plan) {
@@ -651,6 +733,15 @@ function stackFile(options) {
651
733
  };
652
734
  postgres.ports = ["127.0.0.1::5432"];
653
735
  }
736
+ const minio = services.minio;
737
+ if (minio && options.storage) {
738
+ minio.environment = {
739
+ ...minio.environment,
740
+ MINIO_ROOT_USER: options.storage.user,
741
+ MINIO_ROOT_PASSWORD: options.storage.password
742
+ };
743
+ minio.ports = ["127.0.0.1::9000"];
744
+ }
654
745
  for (const app of apps) services[app.name] = {
655
746
  image: app.ref,
656
747
  ...app.build ? { build: {
@@ -925,6 +1016,7 @@ const defaultDeps = {
925
1016
  docker: dockerCompose,
926
1017
  revision: gitRevision,
927
1018
  sql: pgClient,
1019
+ buckets: bucketClient,
928
1020
  migrate: migrateDatabases,
929
1021
  probe: httpsProbe,
930
1022
  healthAttempts: 90,
@@ -947,6 +1039,13 @@ async function validateCompose(ctx, deps, forced) {
947
1039
  runnables
948
1040
  });
949
1041
  const { secrets, generated } = await stageSecrets(ctx, manifest);
1042
+ await assertStageCredentials({
1043
+ root,
1044
+ patterns: constructGlobs(workspace),
1045
+ manifest,
1046
+ stage,
1047
+ supplied: secrets?.custom ?? {}
1048
+ });
950
1049
  const masterKeys = {};
951
1050
  const layout = mode === "build" ? imageLayout(workspace) : void 0;
952
1051
  const composed = composeStack({
@@ -961,9 +1060,11 @@ async function validateCompose(ctx, deps, forced) {
961
1060
  ...workspace.deploy?.registry ? { registry: workspace.deploy.registry } : {}
962
1061
  },
963
1062
  secrets,
1063
+ allowDevServices: ctx.allowDevServices,
964
1064
  ports: edgePorts(deps.env),
965
1065
  ...layout ? { layout } : {}
966
1066
  });
1067
+ reportDevServices(ctx, composed.devServices);
967
1068
  const credentials = {};
968
1069
  if (mode === "build") for (const app of composed.apps) {
969
1070
  if (app.kind !== "rest-api" || !app.env || !app.build) continue;
@@ -1097,6 +1198,20 @@ async function provisionCompose(ctx, run, deps) {
1097
1198
  });
1098
1199
  }
1099
1200
  if (stack.infra.includes("postgres")) await prepareDatabases(ctx, run, deps);
1201
+ if (stack.storage && stack.infra.includes("minio")) await prepareBuckets(ctx, run, deps);
1202
+ }
1203
+ /**
1204
+ * Create each bucket the stack's MinIO serves, and the open paths of each
1205
+ * one a file server fronts — from this machine, on MinIO's loopback port,
1206
+ * the way `gkm dev` creates them.
1207
+ */
1208
+ async function prepareBuckets(ctx, run, deps) {
1209
+ const storage = run.stack.storage;
1210
+ if (storage.buckets.length === 0) return;
1211
+ const port = await deps.docker.port(run.ref, "minio", 9e3);
1212
+ const client = deps.buckets(port, storage);
1213
+ const created = [...await applyBuckets(client, storage.buckets), ...await applyPolicies(client, storage.policies)].filter((entry) => entry.created).length;
1214
+ ctx.logger.info(`🪣 Buckets: ${storage.buckets.join(", ")}${created ? ` (${created} change(s))` : ""}`);
1100
1215
  }
1101
1216
  /** Create what the plan names in the stack's Postgres, then migrate it. */
1102
1217
  async function prepareDatabases(ctx, run, deps) {
@@ -1341,7 +1456,7 @@ async function writeStack(root, dir, stack, credentials = {}) {
1341
1456
  return files;
1342
1457
  }
1343
1458
  function composeYaml(stack) {
1344
- const stage = stack.local ? "" : ` --stage ${stack.stage}`;
1459
+ const stage = ` --stage ${stack.stage}`;
1345
1460
  return `# Generated by gkm compose from the construct manifest — do not edit.
1346
1461
  # The ${stack.stage} stage's APIs and sites behind one Caddy. Each backend
1347
1462
  # reads exactly the keys in its own env file beside this one.
@@ -1420,4 +1535,4 @@ function composeTarget(options = {}) {
1420
1535
  //#endregion
1421
1536
  export { dockerCompose as a, stackDir as i, compose_exports as n, composeProject as r, composeTarget as t };
1422
1537
 
1423
- //# sourceMappingURL=compose-BjToENzG.mjs.map
1538
+ //# sourceMappingURL=compose-CZtinmtw.mjs.map