@geekmidas/cli 10.0.0-alpha.84 → 10.0.0-alpha.86

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 (177) hide show
  1. package/dist/{HostingerProvider-dxkwynSs.cjs → HostingerProvider-BCB1QJj1.cjs} +2 -2
  2. package/dist/{HostingerProvider-dxkwynSs.cjs.map → HostingerProvider-BCB1QJj1.cjs.map} +1 -1
  3. package/dist/{HostingerProvider-D85ayzTi.mjs → HostingerProvider-CVc3rLPB.mjs} +2 -2
  4. package/dist/{HostingerProvider-D85ayzTi.mjs.map → HostingerProvider-CVc3rLPB.mjs.map} +1 -1
  5. package/dist/{LegacyStateStore-CTsUFLNQ.mjs → LegacyStateStore-B0R4LlX_.mjs} +2 -2
  6. package/dist/{LegacyStateStore-CTsUFLNQ.mjs.map → LegacyStateStore-B0R4LlX_.mjs.map} +1 -1
  7. package/dist/{LegacyStateStore-EDT2TUSe.cjs → LegacyStateStore-CWG_HVC0.cjs} +7 -7
  8. package/dist/{LegacyStateStore-EDT2TUSe.cjs.map → LegacyStateStore-CWG_HVC0.cjs.map} +1 -1
  9. package/dist/{LocalStateStore-CdpzTvPn.cjs → LocalStateStore-CLwa2DpK.cjs} +16 -14
  10. package/dist/LocalStateStore-CLwa2DpK.cjs.map +1 -0
  11. package/dist/{LocalStateStore-D1VfxRuK.mjs → LocalStateStore-Q4vXg2ua.mjs} +12 -10
  12. package/dist/LocalStateStore-Q4vXg2ua.mjs.map +1 -0
  13. package/dist/{S3StateStore-BPM74zwv.cjs → S3StateStore-Cea9_5gF.cjs} +5 -5
  14. package/dist/{S3StateStore-BPM74zwv.cjs.map → S3StateStore-Cea9_5gF.cjs.map} +1 -1
  15. package/dist/{S3StateStore-DVZ1YuGJ.mjs → S3StateStore-CksKYKRa.mjs} +2 -2
  16. package/dist/{S3StateStore-DVZ1YuGJ.mjs.map → S3StateStore-CksKYKRa.mjs.map} +1 -1
  17. package/dist/{SSMStateStore-DLvnGyyl.mjs → SSMStateStore-BszSCAS2.mjs} +2 -2
  18. package/dist/{SSMStateStore-DLvnGyyl.mjs.map → SSMStateStore-BszSCAS2.mjs.map} +1 -1
  19. package/dist/{SSMStateStore-qerm5c36.cjs → SSMStateStore-aBOTF018.cjs} +9 -9
  20. package/dist/{SSMStateStore-qerm5c36.cjs.map → SSMStateStore-aBOTF018.cjs.map} +1 -1
  21. package/dist/{add-BQ8v1ZII.cjs → add-5MZAXybv.cjs} +8 -5
  22. package/dist/add-5MZAXybv.cjs.map +1 -0
  23. package/dist/{add-DbnzC7Kl.mjs → add-D_jNqnfZ.mjs} +8 -5
  24. package/dist/add-D_jNqnfZ.mjs.map +1 -0
  25. package/dist/{bundler-DjrRffko.cjs → bundler-1REMhKps.cjs} +5 -5
  26. package/dist/{bundler-DjrRffko.cjs.map → bundler-1REMhKps.cjs.map} +1 -1
  27. package/dist/{bundler-D4030Afw.mjs → bundler-BMo-JP5s.mjs} +5 -5
  28. package/dist/{bundler-D4030Afw.mjs.map → bundler-BMo-JP5s.mjs.map} +1 -1
  29. package/dist/{cli-DwTa9d_8.mjs → cli-7edNFwGk.mjs} +5 -13
  30. package/dist/cli-7edNFwGk.mjs.map +1 -0
  31. package/dist/{cli-D8YQAYUX.cjs → cli-BL8ZHsLQ.cjs} +5 -13
  32. package/dist/cli-BL8ZHsLQ.cjs.map +1 -0
  33. package/dist/{compose-C5wz8mf9.mjs → compose-DbJhRQlh.mjs} +46 -15
  34. package/dist/compose-DbJhRQlh.mjs.map +1 -0
  35. package/dist/{compose-DqWh1TaO.cjs → compose-xw86kVYi.cjs} +55 -24
  36. package/dist/compose-xw86kVYi.cjs.map +1 -0
  37. package/dist/{config-BzIfP8a-.d.cts → config-CpjsVxru.d.cts} +3 -3
  38. package/dist/{config-BzIfP8a-.d.cts.map → config-CpjsVxru.d.cts.map} +1 -1
  39. package/dist/{config-DhdzmndX.d.mts → config-DrCAyoH5.d.mts} +3 -3
  40. package/dist/{config-DhdzmndX.d.mts.map → config-DrCAyoH5.d.mts.map} +1 -1
  41. package/dist/config.cjs +1 -1
  42. package/dist/config.d.cts +3 -3
  43. package/dist/config.d.mts +3 -3
  44. package/dist/config.mjs +1 -1
  45. package/dist/{credentials-pAtB70k3.mjs → credentials-BgJ5mS_d.mjs} +91 -181
  46. package/dist/credentials-BgJ5mS_d.mjs.map +1 -0
  47. package/dist/{credentials-BVs7mrtw.cjs → credentials-tr26-KML.cjs} +122 -188
  48. package/dist/credentials-tr26-KML.cjs.map +1 -0
  49. package/dist/deploy/public.cjs +24 -20
  50. package/dist/deploy/public.cjs.map +1 -1
  51. package/dist/deploy/public.d.cts +95 -13
  52. package/dist/deploy/public.d.cts.map +1 -1
  53. package/dist/deploy/public.d.mts +95 -13
  54. package/dist/deploy/public.d.mts.map +1 -1
  55. package/dist/deploy/public.mjs +11 -10
  56. package/dist/deploy/public.mjs.map +1 -1
  57. package/dist/{deploy-BwIKGxWH.mjs → deploy-BTfmXd3g.mjs} +9 -9
  58. package/dist/deploy-BTfmXd3g.mjs.map +1 -0
  59. package/dist/{deploy-C8sWg81_.cjs → deploy-CtbZj4zN.cjs} +13 -13
  60. package/dist/deploy-CtbZj4zN.cjs.map +1 -0
  61. package/dist/{dokploy-BkRxJbfi.cjs → dokploy-TGk3oPwr.cjs} +2 -2
  62. package/dist/{dokploy-BkRxJbfi.cjs.map → dokploy-TGk3oPwr.cjs.map} +1 -1
  63. package/dist/{dokploy-ijjms1-h.mjs → dokploy-r69yZaIt.mjs} +2 -2
  64. package/dist/{dokploy-ijjms1-h.mjs.map → dokploy-r69yZaIt.mjs.map} +1 -1
  65. package/dist/{encryption-BRcBAmjL.mjs → encryption-C-8Tt5Hh.mjs} +3 -3
  66. package/dist/{encryption-BRcBAmjL.mjs.map → encryption-C-8Tt5Hh.mjs.map} +1 -1
  67. package/dist/{encryption-g6PzwwfL.cjs → encryption-CDn7BH5O.cjs} +3 -3
  68. package/dist/{encryption-g6PzwwfL.cjs.map → encryption-CDn7BH5O.cjs.map} +1 -1
  69. package/dist/{engine-DLEJsZ3i.cjs → engine-Bt5k_UMq.cjs} +33 -23
  70. package/dist/engine-Bt5k_UMq.cjs.map +1 -0
  71. package/dist/{engine-BP0CZ5fh.mjs → engine-jAINgiOB.mjs} +28 -18
  72. package/dist/engine-jAINgiOB.mjs.map +1 -0
  73. package/dist/{github-Zs-dVJbR.mjs → github-CZHAtwPT.mjs} +3 -3
  74. package/dist/{github-Zs-dVJbR.mjs.map → github-CZHAtwPT.mjs.map} +1 -1
  75. package/dist/{github-B8YjwdLK.cjs → github-DFhuJQlp.cjs} +3 -3
  76. package/dist/{github-B8YjwdLK.cjs.map → github-DFhuJQlp.cjs.map} +1 -1
  77. package/dist/{index-f77HdqN5.d.mts → index-MnUJuUlj.d.mts} +2 -2
  78. package/dist/{index-D1ODeM9s.d.cts.map → index-MnUJuUlj.d.mts.map} +1 -1
  79. package/dist/{index-D1ODeM9s.d.cts → index-elsLJTUv.d.cts} +2 -2
  80. package/dist/{index-f77HdqN5.d.mts.map → index-elsLJTUv.d.cts.map} +1 -1
  81. package/dist/index.cjs +105 -67
  82. package/dist/index.cjs.map +1 -1
  83. package/dist/index.mjs +99 -61
  84. package/dist/index.mjs.map +1 -1
  85. package/dist/local-Ciq-bTPr.mjs +224 -0
  86. package/dist/local-Ciq-bTPr.mjs.map +1 -0
  87. package/dist/local-Cq8qdgNX.cjs +241 -0
  88. package/dist/local-Cq8qdgNX.cjs.map +1 -0
  89. package/dist/{migrate-FoL_mZIz.cjs → migrate-B16qgYTn.cjs} +3 -3
  90. package/dist/{migrate-FoL_mZIz.cjs.map → migrate-B16qgYTn.cjs.map} +1 -1
  91. package/dist/{migrate-BfZN1S32.cjs → migrate-B1rVlwj1.cjs} +3 -3
  92. package/dist/{migrate-BfZN1S32.cjs.map → migrate-B1rVlwj1.cjs.map} +1 -1
  93. package/dist/{migrate-DOMU4uKe.mjs → migrate-Dn7nK6sh.mjs} +3 -3
  94. package/dist/{migrate-DOMU4uKe.mjs.map → migrate-Dn7nK6sh.mjs.map} +1 -1
  95. package/dist/{migrate-CdXcX0XR.mjs → migrate-ggkPVJRC.mjs} +3 -3
  96. package/dist/{migrate-CdXcX0XR.mjs.map → migrate-ggkPVJRC.mjs.map} +1 -1
  97. package/dist/openapi.cjs +1 -1
  98. package/dist/openapi.d.cts +1 -1
  99. package/dist/openapi.d.mts +1 -1
  100. package/dist/openapi.mjs +1 -1
  101. package/dist/reconcile/public.d.cts +1 -1
  102. package/dist/reconcile/public.d.mts +1 -1
  103. package/dist/{resolve-r7PAs6ng.mjs → resolve-BSke45k6.mjs} +4 -4
  104. package/dist/{resolve-r7PAs6ng.mjs.map → resolve-BSke45k6.mjs.map} +1 -1
  105. package/dist/{resolve-T2I-5FcT.cjs → resolve-BXeXzpmU.cjs} +4 -4
  106. package/dist/{resolve-T2I-5FcT.cjs.map → resolve-BXeXzpmU.cjs.map} +1 -1
  107. package/dist/{rollback-BSdxnW3X.mjs → rollback-CEE6dse4.mjs} +6 -6
  108. package/dist/{rollback-BSdxnW3X.mjs.map → rollback-CEE6dse4.mjs.map} +1 -1
  109. package/dist/{rollback-CPSDjGse.cjs → rollback-DFMiBWf0.cjs} +8 -8
  110. package/dist/{rollback-CPSDjGse.cjs.map → rollback-DFMiBWf0.cjs.map} +1 -1
  111. package/dist/{runtime-CNu2C2hB.d.mts → runtime-D48ZKk8_.d.cts} +2 -2
  112. package/dist/{runtime-CNu2C2hB.d.mts.map → runtime-D48ZKk8_.d.cts.map} +1 -1
  113. package/dist/{runtime-DMLgL26v.d.cts → runtime-HabiNBtJ.d.mts} +2 -2
  114. package/dist/{runtime-DMLgL26v.d.cts.map → runtime-HabiNBtJ.d.mts.map} +1 -1
  115. package/dist/{seeds-BK8caVMm.mjs → seeds-BYdrtZsC.mjs} +1554 -54
  116. package/dist/seeds-BYdrtZsC.mjs.map +1 -0
  117. package/dist/{seeds-DQ0FG57Z.cjs → seeds-CsI0A_Ef.cjs} +1656 -65
  118. package/dist/seeds-CsI0A_Ef.cjs.map +1 -0
  119. package/dist/{sniffer-CqE_KTTp.mjs → sniffer-B0EU73bj.mjs} +5 -360
  120. package/dist/sniffer-B0EU73bj.mjs.map +1 -0
  121. package/dist/{sniffer-hSf5mRgV.cjs → sniffer-DJYupBeK.cjs} +4 -413
  122. package/dist/sniffer-DJYupBeK.cjs.map +1 -0
  123. package/dist/{sst-DRi3H5TB.mjs → sst-D1Qj4sIs.mjs} +3 -3
  124. package/dist/{sst-DRi3H5TB.mjs.map → sst-D1Qj4sIs.mjs.map} +1 -1
  125. package/dist/{sst-hBrKouBy.cjs → sst-DAqUoZKG.cjs} +5 -5
  126. package/dist/{sst-hBrKouBy.cjs.map → sst-DAqUoZKG.cjs.map} +1 -1
  127. package/dist/{stages-CS1sjcRb.cjs → stages-Brn3vcdy.cjs} +2 -2
  128. package/dist/{stages-CS1sjcRb.cjs.map → stages-Brn3vcdy.cjs.map} +1 -1
  129. package/dist/{stages-CSXt7N2b.mjs → stages-DoHHz6pS.mjs} +2 -2
  130. package/dist/{stages-CSXt7N2b.mjs.map → stages-DoHHz6pS.mjs.map} +1 -1
  131. package/dist/target/public.cjs +1 -1
  132. package/dist/target/public.d.cts +2 -2
  133. package/dist/target/public.d.mts +2 -2
  134. package/dist/target/public.mjs +1 -1
  135. package/dist/{terminal-BdvrmkaC.mjs → terminal-BTbZlrFV.mjs} +2 -2
  136. package/dist/{terminal-BdvrmkaC.mjs.map → terminal-BTbZlrFV.mjs.map} +1 -1
  137. package/dist/{terminal-BL5E0xbR.cjs → terminal-COv23heb.cjs} +2 -2
  138. package/dist/{terminal-BL5E0xbR.cjs.map → terminal-COv23heb.cjs.map} +1 -1
  139. package/dist/{types-BZn-8ssD.d.mts → types-0oAyFOfR.d.cts} +48 -49
  140. package/dist/types-0oAyFOfR.d.cts.map +1 -0
  141. package/dist/{types-DiZj6hqp.d.cts → types-BZ01tZs-.d.mts} +47 -50
  142. package/dist/types-BZ01tZs-.d.mts.map +1 -0
  143. package/dist/workspace/index.cjs +1 -1
  144. package/dist/workspace/index.d.cts +2 -2
  145. package/dist/workspace/index.d.mts +2 -2
  146. package/dist/workspace/index.mjs +1 -1
  147. package/dist/{workspace-Bic_R3tz.cjs → workspace-CT0JM3Y8.cjs} +333 -1
  148. package/dist/workspace-CT0JM3Y8.cjs.map +1 -0
  149. package/dist/{workspace-Drz4jsvy.mjs → workspace-D2CYLMCW.mjs} +250 -2
  150. package/dist/workspace-D2CYLMCW.mjs.map +1 -0
  151. package/package.json +15 -15
  152. package/dist/LocalStateStore-CdpzTvPn.cjs.map +0 -1
  153. package/dist/LocalStateStore-D1VfxRuK.mjs.map +0 -1
  154. package/dist/add-BQ8v1ZII.cjs.map +0 -1
  155. package/dist/add-DbnzC7Kl.mjs.map +0 -1
  156. package/dist/cli-D8YQAYUX.cjs.map +0 -1
  157. package/dist/cli-DwTa9d_8.mjs.map +0 -1
  158. package/dist/compose-C5wz8mf9.mjs.map +0 -1
  159. package/dist/compose-DqWh1TaO.cjs.map +0 -1
  160. package/dist/credentials-BVs7mrtw.cjs.map +0 -1
  161. package/dist/credentials-pAtB70k3.mjs.map +0 -1
  162. package/dist/deploy-BwIKGxWH.mjs.map +0 -1
  163. package/dist/deploy-C8sWg81_.cjs.map +0 -1
  164. package/dist/engine-BP0CZ5fh.mjs.map +0 -1
  165. package/dist/engine-DLEJsZ3i.cjs.map +0 -1
  166. package/dist/run-BJLIerMM.mjs +0 -133
  167. package/dist/run-BJLIerMM.mjs.map +0 -1
  168. package/dist/run-fVZ-rLnc.cjs +0 -174
  169. package/dist/run-fVZ-rLnc.cjs.map +0 -1
  170. package/dist/seeds-BK8caVMm.mjs.map +0 -1
  171. package/dist/seeds-DQ0FG57Z.cjs.map +0 -1
  172. package/dist/sniffer-CqE_KTTp.mjs.map +0 -1
  173. package/dist/sniffer-hSf5mRgV.cjs.map +0 -1
  174. package/dist/types-BZn-8ssD.d.mts.map +0 -1
  175. package/dist/types-DiZj6hqp.d.cts.map +0 -1
  176. package/dist/workspace-Bic_R3tz.cjs.map +0 -1
  177. package/dist/workspace-Drz4jsvy.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-D2CYLMCW.mjs","names":["z.union","z.string","z.array","z.object","z.boolean","z.number","z.enum","z.custom","z.tuple","z.unknown","z\n\t.object","z.url","z\n\t\t\t.record","z\n\t\t\t.object","z\n\t\t\t\t\t.string","z.literal","z.discriminatedUnion","z.record","z\n\t\t\t.union","z\n\t\t\t\t\t.object","z\n\t.record","z\n\t\t\t\t.object","z\n\t\t.record","z\n\t\t.string","z\n\t\t.object","z\n\t\t\t.array"],"sources":["../src/secrets/providers.ts","../src/compose/proxy.ts","../src/providers/s3/errors.ts","../src/providers/s3/config.ts","../src/providers/config.ts","../src/compose/logsConfig.ts","../src/telemetry/config.ts","../src/workspace/stages.ts","../src/workspace/schema.ts","../src/workspace/types.ts","../src/workspace/publicEnv.ts","../src/workspace/index.ts"],"sourcesContent":["/**\n * The secrets stores gkm ships, by name — with no imports, so the config\n * loader can check `secrets.store` without loading any store.\n */\n\n/** The stores this package ships, by the name `secrets.store` gives them. */\nexport const SECRETS_STORE_PROVIDERS = [\n\t'file',\n\t'ssm',\n\t'secrets-manager',\n] as const;\n\nexport type SecretsStoreProvider = (typeof SECRETS_STORE_PROVIDERS)[number];\n\n/**\n * `secrets.store` names a provider this package does not ship — a typo such\n * as `'secretsmanager'` or `'ssm-parameter'`. Refused rather than read as the\n * file, which would leave a deployed stage's secrets on one machine.\n */\nexport class UnknownSecretsStoreProvider extends Error {\n\tconstructor(readonly provider: unknown) {\n\t\tsuper(\n\t\t\t`secrets.store names the provider ${JSON.stringify(provider)}, which is not one gkm ships. ` +\n\t\t\t\t`Use 'file', { provider: 'ssm', region }, { provider: 'secrets-manager', region }, ` +\n\t\t\t\t'or { provider: <an object with name, read() and write()> } in gkm.config.ts.',\n\t\t);\n\t\tthis.name = 'UnknownSecretsStoreProvider';\n\t}\n}\n\n/**\n * Refuse a `secrets.store` naming a provider that is not one, before the\n * schema reduces it to a generic validation failure.\n */\nexport function assertKnownSecretsStore(store: unknown): void {\n\tif (store === undefined) return;\n\tconst provider =\n\t\ttypeof store === 'object' && store !== null && 'provider' in store\n\t\t\t? (store as { provider: unknown }).provider\n\t\t\t: store;\n\tif (\n\t\ttypeof provider === 'string' &&\n\t\t!(SECRETS_STORE_PROVIDERS as readonly string[]).includes(provider)\n\t) {\n\t\tthrow new UnknownSecretsStoreProvider(provider);\n\t}\n}\n","/**\n * Which proxy a stage's stack is served by, and with whose certificate —\n * `deploy.compose.proxy` and `deploy.compose.tls`, resolved for one stage.\n *\n * Pure, and kept apart from the stack so the workspace schema can check a\n * config with the same rules — and the same named errors — the stack applies.\n */\n\nimport { isAbsolute, resolve } from 'node:path';\nimport type {\n\tComposeProxy,\n\tComposeTlsConfig,\n\tComposeWorkspaceConfig,\n} from '../workspace/types.js';\n\n/** A per-stage setting keyed by a stage the workspace does not have. */\nexport class ComposeStageUnknown extends Error {\n\tconstructor(\n\t\treadonly setting: 'proxy' | 'tls',\n\t\treadonly stage: string,\n\t\treadonly stages: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`deploy.compose.${setting} names the stage '${stage}', which is not one ` +\n\t\t\t\t`of this workspace's (${stages.join(', ')}). Use a stage from ` +\n\t\t\t\t'stages.local or stages.deployed.',\n\t\t);\n\t\tthis.name = 'ComposeStageUnknown';\n\t}\n}\n\n/**\n * A certificate for the local stage, whose certificates are always Caddy's\n * own CA — the one `gkm trust` installs.\n */\nexport class ComposeTlsOnLocalStage extends Error {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`deploy.compose.tls sets a certificate for '${stage}', the local stage, ` +\n\t\t\t\t\"whose certificates always come from Caddy's local CA (`gkm trust` \" +\n\t\t\t\t'installs it). Remove it, or set it for a deployed stage.',\n\t\t);\n\t\tthis.name = 'ComposeTlsOnLocalStage';\n\t}\n}\n\n/** The stage's certificate or key file is not there to read. */\nexport class ComposeTlsFileMissing extends Error {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly file: string,\n\t) {\n\t\tsuper(\n\t\t\t`deploy.compose.tls for '${stage}' names ${file}, and there is no file ` +\n\t\t\t\t'there. Point certFile and keyFile at the PEM certificate (with its ' +\n\t\t\t\t'chain) and key — relative to the workspace root, or absolute.',\n\t\t);\n\t\tthis.name = 'ComposeTlsFileMissing';\n\t}\n}\n\n/**\n * The proxy a stage's stack is served by. The local stage is always Caddy —\n * its internal CA and `gkm trust` — whatever is configured.\n */\nexport function proxyFor(\n\tconfig: ComposeWorkspaceConfig | undefined,\n\tstage: string,\n\tlocal: boolean,\n): ComposeProxy {\n\tif (local) return 'caddy';\n\tconst proxy = config?.proxy;\n\tif (!proxy) return 'caddy';\n\tif (typeof proxy === 'string') return proxy;\n\treturn proxy[stage] ?? 'caddy';\n}\n\n/** A deployed stage's own certificate, its paths absolute, when it sets one. */\nexport function certificateFor(\n\tconfig: ComposeWorkspaceConfig | undefined,\n\tstage: string,\n\troot: string,\n): ComposeTlsConfig | undefined {\n\tconst tls = config?.tls?.[stage];\n\tif (!tls) return undefined;\n\tconst absolute = (file: string) =>\n\t\tisAbsolute(file) ? file : resolve(root, file);\n\treturn { certFile: absolute(tls.certFile), keyFile: absolute(tls.keyFile) };\n}\n\n/**\n * Throws the named error for the first thing wrong with the per-stage\n * settings: a stage the workspace does not have, or a certificate for the\n * local stage.\n */\nexport function checkComposeStages(\n\tconfig: ComposeWorkspaceConfig | undefined,\n\tstages: { local: string; deployed: readonly string[] },\n): void {\n\tconst known = [stages.local, ...stages.deployed];\n\tif (config?.proxy && typeof config.proxy === 'object') {\n\t\tfor (const stage of Object.keys(config.proxy)) {\n\t\t\tif (!known.includes(stage)) {\n\t\t\t\tthrow new ComposeStageUnknown('proxy', stage, known);\n\t\t\t}\n\t\t}\n\t}\n\tfor (const stage of Object.keys(config?.tls ?? {})) {\n\t\tif (stage === stages.local) throw new ComposeTlsOnLocalStage(stage);\n\t\tif (!known.includes(stage)) {\n\t\t\tthrow new ComposeStageUnknown('tls', stage, known);\n\t\t}\n\t}\n}\n","/** What the `s3` provider refuses, and how each is put right. */\n\n/** An `objects` entry for `s3` with a field it does not take or cannot use. */\nexport class S3ProviderConfigInvalid extends Error {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly field: string,\n\t\treadonly value: unknown,\n\t\treadonly expected: string,\n\t) {\n\t\tsuper(\n\t\t\t`deploy.objects.${stage}.${field} is ${JSON.stringify(value)}; ${expected}.`,\n\t\t);\n\t\tthis.name = 'S3ProviderConfigInvalid';\n\t}\n}\n\n/** No region anywhere to create the stage's buckets in. */\nexport class S3RegionRequired extends Error {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`The s3 provider has no region for the stage '${stage}'. Name one in ` +\n\t\t\t\t`gkm.config.ts: deploy: { objects: { ${stage}: { provider: 's3', ` +\n\t\t\t\t\"region: 'eu-west-1' } } } — or set AWS_REGION.\",\n\t\t);\n\t\tthis.name = 'S3RegionRequired';\n\t}\n}\n\n/** The good name and every suffixed one are taken by other accounts. */\nexport class BucketNameUnavailable extends Error {\n\tconstructor(\n\t\treadonly id: string,\n\t\treadonly tried: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`Every name tried for the bucket '${id}' is taken by another AWS ` +\n\t\t\t\t`account: ${tried.join(', ')}. Set deploy.namespace in gkm.config.ts ` +\n\t\t\t\t'to something more particular to you, and run the provision again.',\n\t\t);\n\t\tthis.name = 'BucketNameUnavailable';\n\t}\n}\n\n/** A bucket in this account carries another project's tag. */\nexport class BucketOwnedByAnotherProject extends Error {\n\tconstructor(\n\t\treadonly bucket: string,\n\t\treadonly owner: string,\n\t\treadonly project: string,\n\t) {\n\t\tsuper(\n\t\t\t`The bucket '${bucket}' is in this account and tagged gkm:project=` +\n\t\t\t\t`${owner}, not ${project}: another project's. gkm never adopts it. ` +\n\t\t\t\t'Set deploy.namespace in gkm.config.ts so this project names its ' +\n\t\t\t\t'buckets differently.',\n\t\t);\n\t\tthis.name = 'BucketOwnedByAnotherProject';\n\t}\n}\n\n/** The bucket the stage's state names exists, and this account cannot reach it. */\nexport class RecordedBucketUnreachable extends Error {\n\tconstructor(\n\t\treadonly id: string,\n\t\treadonly bucket: string,\n\t) {\n\t\tsuper(\n\t\t\t`The stage's state records '${bucket}' as the bucket for '${id}', and ` +\n\t\t\t\t'this account cannot reach it — it is in another account, or the ' +\n\t\t\t\t\"credentials are not the stage account's. Run with the account that \" +\n\t\t\t\t'created it (--profile).',\n\t\t);\n\t\tthis.name = 'RecordedBucketUnreachable';\n\t}\n}\n\n/** An IAM user by gkm's name exists, and gkm did not create it for this stage. */\nexport class IamUserNotOwned extends Error {\n\tconstructor(\n\t\treadonly user: string,\n\t\treadonly project: string,\n\t\treadonly stage: string,\n\t) {\n\t\tsuper(\n\t\t\t`The IAM user '${user}' exists and is not tagged gkm:project=${project}, ` +\n\t\t\t\t`gkm:stage=${stage}, so gkm leaves it alone. Rename or delete it, or ` +\n\t\t\t\t'set deploy.namespace in gkm.config.ts so gkm names its users differently.',\n\t\t);\n\t\tthis.name = 'IamUserNotOwned';\n\t}\n}\n\n/** The user already has the two access keys IAM allows. */\nexport class AccessKeyLimit extends Error {\n\tconstructor(\n\t\treadonly user: string,\n\t\treadonly keys: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`The IAM user '${user}' already has two access keys (${keys.join(', ')}), ` +\n\t\t\t\t'the most IAM allows, and gkm needs to issue one. Delete the one the ' +\n\t\t\t\t`stage does not use (aws iam delete-access-key --user-name ${user} ` +\n\t\t\t\t'--access-key-id …), then run the provision again.',\n\t\t);\n\t\tthis.name = 'AccessKeyLimit';\n\t}\n}\n\n/** `--rotate-keys` while the last rotation's old key is still active. */\nexport class RotationInProgress extends Error {\n\tconstructor(\n\t\treadonly id: string,\n\t\treadonly previous: string,\n\t\treadonly stage: string,\n\t) {\n\t\tsuper(\n\t\t\t`The bucket '${id}' is mid-rotation: its old key ${previous} is still ` +\n\t\t\t\t`active. Deploy '${stage}', then run gkm setup --stage ${stage} — ` +\n\t\t\t\t'or pass --retire-old-keys to delete it now — before rotating again.',\n\t\t);\n\t\tthis.name = 'RotationInProgress';\n\t}\n}\n\n/** The stage's secrets point the bucket's key at a bucket gkm did not provision. */\nexport class BucketKeySetElsewhere extends Error {\n\tconstructor(\n\t\treadonly key: string,\n\t\treadonly bucket: string,\n\t\treadonly found: string,\n\t\treadonly stage: string,\n\t) {\n\t\tsuper(\n\t\t\t`${key} in the stage '${stage}' points at the bucket '${found}', and ` +\n\t\t\t\t`the s3 provider manages '${bucket}' for it. Remove the key ` +\n\t\t\t\t`(gkm secrets:unset ${key} --stage ${stage}) to have the provider ` +\n\t\t\t\t`write its own, or set deploy.objects.${stage} to 'external' to keep yours.`,\n\t\t);\n\t\tthis.name = 'BucketKeySetElsewhere';\n\t}\n}\n\n/** `verify()`: a provisioned bucket the stage's key cannot reach. */\nexport class ProvisionedBucketUnreachable extends Error {\n\tconstructor(\n\t\treadonly id: string,\n\t\treadonly bucket: string,\n\t\treadonly stage: string,\n\t\treadonly reason: string,\n\t) {\n\t\tsuper(\n\t\t\t`The bucket '${bucket}' (${id}) cannot be reached with the key in the ` +\n\t\t\t\t`stage '${stage}''s secrets: ${reason}. Run gkm setup --stage ${stage} ` +\n\t\t\t\t\"with the stage account's credentials to create or repair it, then deploy again.\",\n\t\t);\n\t\tthis.name = 'ProvisionedBucketUnreachable';\n\t}\n}\n","/** `deploy.objects.<stage>` for the `s3` provider, checked. */\n\nimport type { S3ObjectsConfig } from '../../workspace/types.js';\nimport type { ProviderConfig } from '../types.js';\nimport { S3ProviderConfigInvalid } from './errors.js';\n\nconst FIELDS = ['provider', 'region', 'versioning'] as const;\n\n/** An AWS region code: `eu-west-1`, `us-gov-west-1`, `ap-southeast-4`. */\nconst REGION = /^[a-z]{2}(-[a-z]+)+-\\d+$/;\n\nexport function checkS3Config(\n\tstage: string,\n\tconfig: ProviderConfig,\n): S3ObjectsConfig {\n\tconst entry = config as unknown as Record<string, unknown>;\n\tfor (const field of Object.keys(entry)) {\n\t\tif (!(FIELDS as readonly string[]).includes(field)) {\n\t\t\tthrow new S3ProviderConfigInvalid(\n\t\t\t\tstage,\n\t\t\t\tfield,\n\t\t\t\tentry[field],\n\t\t\t\t`the s3 provider takes ${FIELDS.slice(1).join(' and ')} only`,\n\t\t\t);\n\t\t}\n\t}\n\tif (entry.region !== undefined) {\n\t\tif (typeof entry.region !== 'string' || !REGION.test(entry.region)) {\n\t\t\tthrow new S3ProviderConfigInvalid(\n\t\t\t\tstage,\n\t\t\t\t'region',\n\t\t\t\tentry.region,\n\t\t\t\t\"set it to an AWS region code, like 'eu-west-1'\",\n\t\t\t);\n\t\t}\n\t}\n\tif (entry.versioning !== undefined && typeof entry.versioning !== 'boolean') {\n\t\tthrow new S3ProviderConfigInvalid(\n\t\t\tstage,\n\t\t\t'versioning',\n\t\t\tentry.versioning,\n\t\t\t'set it to true to keep previous versions of every object, or leave it out',\n\t\t);\n\t}\n\treturn entry as unknown as S3ObjectsConfig;\n}\n\n/** How the s3 provider's provisioning credentials are supplied. */\nexport const S3_PROVISIONING = {\n\tenv: [\n\t\t'AWS_PROFILE',\n\t\t'AWS_ACCESS_KEY_ID',\n\t\t'AWS_SECRET_ACCESS_KEY',\n\t\t'AWS_SESSION_TOKEN',\n\t],\n\tdescribe:\n\t\t\"the stage's AWS account — --profile <name>, AWS_PROFILE, or AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY (in CI, what aws-actions/configure-aws-credentials exports)\",\n} as const;\n","/**\n * `deploy.<kind>.<stage>`, read: what backs one kind of construct on a stage.\n *\n * Kept free of the providers themselves — of the AWS SDK and the graph — so\n * the workspace schema can check an entry with the words a deploy would use,\n * the way `checkStageTelemetry` does for `deploy.telemetry`.\n */\n\nimport { checkS3Config, S3_PROVISIONING } from './s3/config.js';\nimport type {\n\tProviderConfig,\n\tProviderKind,\n\tResourceProvider,\n\tStageProviderEntry,\n} from './types.js';\n\n/** Each provider's config check, by kind and name. */\nconst CHECKS: {\n\treadonly [K in ProviderKind]: Readonly<\n\t\tRecord<string, (stage: string, config: ProviderConfig) => ProviderConfig>\n\t>;\n} = {\n\tobjects: { s3: checkS3Config },\n};\n\n/** How each provider's provisioning credentials are supplied, by kind and name. */\nconst PROVISIONING: {\n\treadonly [K in ProviderKind]: Readonly<\n\t\tRecord<string, ResourceProvider['provisioning']>\n\t>;\n} = {\n\tobjects: { s3: S3_PROVISIONING },\n};\n\n/** How `name`'s provisioning credentials are supplied, for messages. */\nexport function provisioningOf(\n\tkind: ProviderKind,\n\tname: string,\n): ResourceProvider['provisioning'] | undefined {\n\treturn Object.hasOwn(PROVISIONING[kind], name)\n\t\t? PROVISIONING[kind][name]\n\t\t: undefined;\n}\n\n/** The `provider:` names a kind accepts. */\nexport function providerNamesOf(kind: ProviderKind): string[] {\n\treturn Object.keys(CHECKS[kind]).sort();\n}\n\n/** What `deploy.<kind>.<stage>` may be, for messages. */\nfunction accepted(kind: ProviderKind): string {\n\treturn [\n\t\t\"'external'\",\n\t\t...providerNamesOf(kind).map((name) => `{ provider: '${name}' }`),\n\t\t'false',\n\t].join(', ');\n}\n\n/** An entry that names no provider gkm has, or is not an entry at all. */\nexport class UnknownStageProvider extends Error {\n\tconstructor(\n\t\treadonly kind: ProviderKind,\n\t\treadonly stage: string,\n\t\treadonly value: unknown,\n\t) {\n\t\tsuper(\n\t\t\t`deploy.${kind}.${stage} is ${JSON.stringify(value)}, which is not a ` +\n\t\t\t\t`provider gkm has for ${kind}. It takes ${accepted(kind)}.`,\n\t\t);\n\t\tthis.name = 'UnknownStageProvider';\n\t}\n}\n\n/**\n * One stage's entry checked: `'external'`, `false`, or a provider's object\n * that its own check accepts.\n *\n * @throws {UnknownStageProvider} for anything else — `'minio'` included\n */\nexport function checkStageProvider(\n\tkind: ProviderKind,\n\tstage: string,\n\tvalue: unknown,\n): StageProviderEntry {\n\tif (value === 'external' || value === false) return value;\n\tif (value && typeof value === 'object' && !Array.isArray(value)) {\n\t\tconst name = (value as { provider?: unknown }).provider;\n\t\tconst check =\n\t\t\ttypeof name === 'string' && Object.hasOwn(CHECKS[kind], name)\n\t\t\t\t? CHECKS[kind][name]\n\t\t\t\t: undefined;\n\t\tif (check) return check(stage, value as ProviderConfig);\n\t}\n\tthrow new UnknownStageProvider(kind, stage, value);\n}\n\n/** What one stage resolves `deploy.<kind>` to. */\nexport type StageProviderChoice =\n\t| { mode: 'external' }\n\t| { mode: 'none' }\n\t| { mode: 'provider'; name: string; config: ProviderConfig };\n\n/** What a stage's `deploy.<kind>` is read from. */\nexport interface StageProviderSource {\n\tdeploy?: Partial<Record<ProviderKind, Record<string, unknown>>> | undefined;\n\tstages?: { local?: string };\n}\n\n/**\n * What backs `kind` on `stage`: the local stage is always `external` here —\n * `gkm dev` runs its own — and a deployed stage that names nothing is too.\n */\nexport function stageProvider(\n\tworkspace: StageProviderSource,\n\tkind: ProviderKind,\n\tstage: string,\n): StageProviderChoice {\n\tif (stage === workspace.stages?.local) return { mode: 'external' };\n\tconst value = workspace.deploy?.[kind]?.[stage];\n\tif (value === undefined) return { mode: 'external' };\n\tconst entry = checkStageProvider(kind, stage, value);\n\tif (entry === 'external') return { mode: 'external' };\n\tif (entry === false) return { mode: 'none' };\n\treturn { mode: 'provider', name: entry.provider, config: entry };\n}\n","/**\n * The self-hosted telemetry provider's options, read: how the stack's\n * OpenObserve is run and reached — `deploy.telemetry.<stage>` with\n * `provider: 'self-hosted'`.\n *\n * Kept apart from the stack so the workspace schema can check a config with\n * the same rules — and the same named errors — the stack applies.\n */\n\nimport { isIP } from 'node:net';\nimport type { SelfHostedTelemetryConfig } from '../workspace/types.js';\n\n/** The port OpenObserve listens on, and the one it is published on by default. */\nexport const LOGS_PORT = 5080;\n\n/** How many days of data OpenObserve keeps unless told otherwise. */\nexport const LOGS_RETENTION_DAYS = 30;\n\n/** The fewest days OpenObserve accepts: below it, it refuses to start. */\nexport const LOGS_MIN_RETENTION_DAYS = 3;\n\n/** The self-hosted provider's options, with their defaults applied. */\nexport interface ResolvedLogs {\n\t/** The loopback port it is published on. Not published when `public` is set. */\n\tport: number;\n\tretentionDays: number;\n\t/** Served on `logs.<stage domain>`, to these addresses only. */\n\tpublic?: { allow: string[] };\n}\n\n/** `public` with no address to allow: nobody could reach it. */\nexport class LogsAllowEmpty extends Error {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`deploy.telemetry.${stage}.public.allow is empty, so the logs site would ` +\n\t\t\t\t'answer every request with 403. List the addresses that may reach it ' +\n\t\t\t\t\"(public: { allow: ['203.0.113.7', '10.0.0.0/8'] }), or remove public \" +\n\t\t\t\t'and reach it through an SSH tunnel.',\n\t\t);\n\t\tthis.name = 'LogsAllowEmpty';\n\t}\n}\n\n/** An `allow` entry that is not an IP address or a CIDR range. */\nexport class LogsAllowEntryInvalid extends Error {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly entry: string,\n\t) {\n\t\tsuper(\n\t\t\t`deploy.telemetry.${stage}.public.allow has '${entry}', which is not an IP ` +\n\t\t\t\t\"address or a CIDR range. Write one like '203.0.113.7', \" +\n\t\t\t\t\"'10.0.0.0/8' or '2001:db8::/32'.\",\n\t\t);\n\t\tthis.name = 'LogsAllowEntryInvalid';\n\t}\n}\n\n/** A retention OpenObserve would refuse to start with. */\nexport class LogsRetentionInvalid extends Error {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly retentionDays: unknown,\n\t) {\n\t\tsuper(\n\t\t\t`deploy.telemetry.${stage}.retentionDays is ${String(retentionDays)}; it must ` +\n\t\t\t\t`be a whole number of days, at least ${LOGS_MIN_RETENTION_DAYS} — ` +\n\t\t\t\t'OpenObserve refuses to start with fewer. Leave it out for ' +\n\t\t\t\t`${LOGS_RETENTION_DAYS}.`,\n\t\t);\n\t\tthis.name = 'LogsRetentionInvalid';\n\t}\n}\n\n/** A port that is not one. */\nexport class LogsPortInvalid extends Error {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly port: unknown,\n\t) {\n\t\tsuper(\n\t\t\t`deploy.telemetry.${stage}.port is ${String(port)}; set it to a number ` +\n\t\t\t\t`between 1 and 65535, or leave it out for ${LOGS_PORT}.`,\n\t\t);\n\t\tthis.name = 'LogsPortInvalid';\n\t}\n}\n\n/** Whether `entry` is an IP address, or an address and a prefix length. */\nfunction isAddressOrRange(entry: string): boolean {\n\tconst [address = '', prefix, ...rest] = entry.split('/');\n\tif (rest.length > 0) return false;\n\tconst family = isIP(address);\n\tif (family === 0) return false;\n\tif (prefix === undefined) return true;\n\tif (!/^\\d{1,3}$/.test(prefix)) return false;\n\treturn Number(prefix) <= (family === 4 ? 32 : 128);\n}\n\n/**\n * The self-hosted provider's options with their defaults — `{}` for all of\n * them. Throws the named error for the first thing wrong with them.\n */\nexport function resolveSelfHosted(\n\tconfig: Omit<SelfHostedTelemetryConfig, 'provider' | 'sampleRate'>,\n\tstage: string,\n): ResolvedLogs {\n\tconst port = config.port ?? LOGS_PORT;\n\tif (!Number.isInteger(port) || port < 1 || port > 65_535) {\n\t\tthrow new LogsPortInvalid(stage, config.port);\n\t}\n\n\tconst retentionDays = config.retentionDays ?? LOGS_RETENTION_DAYS;\n\tif (\n\t\t!Number.isInteger(retentionDays) ||\n\t\tretentionDays < LOGS_MIN_RETENTION_DAYS\n\t) {\n\t\tthrow new LogsRetentionInvalid(stage, config.retentionDays);\n\t}\n\n\tif (config.public) {\n\t\tconst allow = config.public.allow ?? [];\n\t\tif (allow.length === 0) throw new LogsAllowEmpty(stage);\n\t\tfor (const entry of allow) {\n\t\t\tif (!isAddressOrRange(entry))\n\t\t\t\tthrow new LogsAllowEntryInvalid(stage, entry);\n\t\t}\n\t\treturn { port, retentionDays, public: { allow: [...allow] } };\n\t}\n\n\treturn { port, retentionDays };\n}\n","/**\n * `deploy.telemetry`, read: where one stage's telemetry goes, and how much.\n *\n * The application says what it emits (the `Telemetry` construct). This says\n * where it is sent, per stage, and is the only place that does:\n *\n * - **self-hosted** — the target runs OpenObserve beside the apps (#189's\n * stack service). A server target that can run it uses it when a stage\n * names nothing.\n * - **otlp** — any OTLP/HTTP endpoint, with its headers.\n * - **false** — nothing is sent.\n *\n * Vendor presets (a known endpoint and header shape, with the stage secret\n * `TELEMETRY_CREDENTIALS`) are one more entry in {@link PROVIDERS}: a\n * resolver from the stage's config to a {@link ResolvedTelemetry}, which\n * every target already consumes through {@link telemetryEnv}.\n *\n * The local stage ignores all of it: `gkm dev` and `gkm compose` on it run\n * OpenObserve and keep every trace.\n */\n\nimport { type ResolvedLogs, resolveSelfHosted } from '../compose/logsConfig.js';\nimport type { DeployRuntime } from '../target/types.js';\nimport type {\n\tOtlpTelemetryConfig,\n\tSelfHostedTelemetryConfig,\n\tStageTelemetryConfig,\n} from '../workspace/types.js';\n\n/** A stage's telemetry, its defaults applied and its config checked. */\nexport type ResolvedTelemetry =\n\t| {\n\t\t\tprovider: 'self-hosted';\n\t\t\t/** How the stack's OpenObserve is run and reached. */\n\t\t\tselfHosted: ResolvedLogs;\n\t\t\tsampleRate: number;\n\t }\n\t| {\n\t\t\tprovider: 'otlp';\n\t\t\tendpoint: string;\n\t\t\theaders: Readonly<Record<string, string>>;\n\t\t\tsampleRate: number;\n\t };\n\n/** The providers `deploy.telemetry` can name. */\nexport type TelemetryProviderName = Exclude<\n\tStageTelemetryConfig,\n\tfalse | 'self-hosted'\n>['provider'];\n\n/** A deployed stage uses telemetry and its target cannot choose for it. */\nexport class TelemetryProviderRequired extends Error {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly target: string,\n\t) {\n\t\tsuper(\n\t\t\t`The stage '${stage}' deploys through ${target}, which cannot run ` +\n\t\t\t\t'telemetry itself, and a process uses a Telemetry construct. Say ' +\n\t\t\t\t`where it goes in gkm.config.ts: deploy: { telemetry: { ${stage}: ` +\n\t\t\t\t\"{ provider: 'otlp', endpoint: 'https://…' } } } — or \" +\n\t\t\t\t`deploy: { telemetry: { ${stage}: false } } to send nothing.`,\n\t\t);\n\t\tthis.name = 'TelemetryProviderRequired';\n\t}\n}\n\n/** A stage asks for the self-hosted provider on a target that cannot run it. */\nexport class SelfHostedTelemetryUnavailable extends Error {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly target: string,\n\t) {\n\t\tsuper(\n\t\t\t`deploy.telemetry.${stage} is 'self-hosted', and ${target} does not ` +\n\t\t\t\t'run OpenObserve beside the apps — the compose target does. Deploy ' +\n\t\t\t\t`'${stage}' through compose, or send its telemetry elsewhere: ` +\n\t\t\t\t`deploy: { telemetry: { ${stage}: { provider: 'otlp', endpoint: ` +\n\t\t\t\t\"'https://…' } } }.\",\n\t\t);\n\t\tthis.name = 'SelfHostedTelemetryUnavailable';\n\t}\n}\n\n/** A sample rate that is not a fraction. */\nexport class TelemetrySampleRateInvalid extends Error {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly sampleRate: unknown,\n\t) {\n\t\tsuper(\n\t\t\t`deploy.telemetry.${stage}.sampleRate is ${String(sampleRate)}; set it ` +\n\t\t\t\t'to a number from 0 to 1 — the fraction of traces kept — or leave it ' +\n\t\t\t\t'out to keep every one.',\n\t\t);\n\t\tthis.name = 'TelemetrySampleRateInvalid';\n\t}\n}\n\n/** An OTLP endpoint that is not an http(s) URL. */\nexport class TelemetryEndpointInvalid extends Error {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly endpoint: unknown,\n\t) {\n\t\tsuper(\n\t\t\t`deploy.telemetry.${stage}.endpoint is ${JSON.stringify(endpoint)}; set ` +\n\t\t\t\t\"it to the collector's OTLP/HTTP base URL, like \" +\n\t\t\t\t\"'https://otlp.example.com' — /v1/traces and /v1/logs are added to it.\",\n\t\t);\n\t\tthis.name = 'TelemetryEndpointInvalid';\n\t}\n}\n\n/** The sample rate a stage keeps, checked. */\nfunction sampleRateOf(stage: string, sampleRate: number | undefined): number {\n\tconst rate = sampleRate ?? 1;\n\tif (!Number.isFinite(rate) || rate < 0 || rate > 1) {\n\t\tthrow new TelemetrySampleRateInvalid(stage, sampleRate);\n\t}\n\treturn rate;\n}\n\n/**\n * Each provider's resolver: a stage's config to what the targets consume.\n * A vendor preset is one more entry here.\n */\nconst PROVIDERS: {\n\t[P in TelemetryProviderName]: (\n\t\tconfig: Extract<StageTelemetryConfig, { provider: P }>,\n\t\tstage: string,\n\t) => ResolvedTelemetry;\n} = {\n\t'self-hosted': (config: SelfHostedTelemetryConfig, stage) => {\n\t\tconst { provider: _, sampleRate, ...options } = config;\n\t\treturn {\n\t\t\tprovider: 'self-hosted',\n\t\t\tselfHosted: resolveSelfHosted(options, stage),\n\t\t\tsampleRate: sampleRateOf(stage, sampleRate),\n\t\t};\n\t},\n\totlp: (config: OtlpTelemetryConfig, stage) => {\n\t\tif (typeof config.endpoint !== 'string' || !isHttpUrl(config.endpoint)) {\n\t\t\tthrow new TelemetryEndpointInvalid(stage, config.endpoint);\n\t\t}\n\t\treturn {\n\t\t\tprovider: 'otlp',\n\t\t\tendpoint: config.endpoint.replace(/\\/+$/, ''),\n\t\t\theaders: { ...config.headers },\n\t\t\tsampleRate: sampleRateOf(stage, config.sampleRate),\n\t\t};\n\t},\n};\n\nfunction isHttpUrl(value: string): boolean {\n\ttry {\n\t\tconst url = new URL(value);\n\t\treturn url.protocol === 'http:' || url.protocol === 'https:';\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * One stage's entry, checked on its own — what the workspace schema runs, so\n * a bad config fails to load with the words a deploy would fail with.\n */\nexport function checkStageTelemetry(\n\tstage: string,\n\tconfig: StageTelemetryConfig,\n): ResolvedTelemetry | undefined {\n\tif (config === false) return undefined;\n\tconst entry: Exclude<StageTelemetryConfig, false | 'self-hosted'> =\n\t\tconfig === 'self-hosted' ? { provider: 'self-hosted' } : config;\n\tconst resolve = PROVIDERS[entry.provider] as (\n\t\tconfig: typeof entry,\n\t\tstage: string,\n\t) => ResolvedTelemetry;\n\treturn resolve(entry, stage);\n}\n\n/** What a stage's telemetry is resolved from. */\nexport interface StageTelemetryInput {\n\t/** `deploy.telemetry`. */\n\ttelemetry?: Readonly<Record<string, StageTelemetryConfig>>;\n\tstage: string;\n\t/** Whether this is the project's local stage, which ignores the config. */\n\tlocal: boolean;\n\t/** The target's name, for messages. */\n\ttarget: string;\n\truntime: DeployRuntime;\n\t/** Whether the target runs the self-hosted provider. */\n\tselfHosted: boolean;\n\t/** Whether any process uses a `Telemetry` node. */\n\tused: boolean;\n}\n\n/**\n * Where a stage's telemetry goes, or undefined when nothing is sent — no\n * process uses a `Telemetry` node, or the stage opted out with `false`.\n *\n * The local stage always runs the self-hosted provider with its defaults and\n * keeps every trace, whatever `deploy.telemetry` says. A deployed stage that\n * names nothing gets the self-hosted provider where its target runs one, and\n * {@link TelemetryProviderRequired} where it cannot — AWS has none.\n */\nexport function resolveStageTelemetry(\n\tinput: StageTelemetryInput,\n): ResolvedTelemetry | undefined {\n\tconst { stage } = input;\n\tif (!input.used) return undefined;\n\n\tif (input.local) {\n\t\treturn {\n\t\t\tprovider: 'self-hosted',\n\t\t\tselfHosted: resolveSelfHosted({}, stage),\n\t\t\tsampleRate: 1,\n\t\t};\n\t}\n\n\tconst config = input.telemetry?.[stage];\n\tif (config === undefined) {\n\t\tif (input.runtime === 'server' && input.selfHosted) {\n\t\t\treturn checkStageTelemetry(stage, 'self-hosted');\n\t\t}\n\t\tthrow new TelemetryProviderRequired(stage, input.target);\n\t}\n\n\tconst resolved = checkStageTelemetry(stage, config);\n\tif (resolved?.provider === 'self-hosted' && !input.selfHosted) {\n\t\tthrow new SelfHostedTelemetryUnavailable(stage, input.target);\n\t}\n\treturn resolved;\n}\n\n/** The sampler every process with the edge runs, per the stage's rate. */\nexport const TRACES_SAMPLER = 'parentbased_traceidratio';\n\n/**\n * `OTEL_EXPORTER_OTLP_HEADERS` for a set of headers: `key=value` pairs,\n * comma-separated, each value percent-encoded — the W3C baggage format the\n * SDK parses.\n */\nexport function otlpHeaderString(\n\theaders: Readonly<Record<string, string>>,\n): string {\n\treturn Object.entries(headers)\n\t\t.map(([key, value]) => `${key}=${encodeURIComponent(value)}`)\n\t\t.join(',');\n}\n\n/**\n * The `OTEL_*` values a stage resolves for its `Telemetry` node, before any\n * one process is named: where to export, how to sign, and how much to keep.\n * `OTEL_SERVICE_NAME` is each process's own — see `scopeTelemetryEnv`.\n */\nexport function telemetryEnv(\n\tdestination: { endpoint: string; headers?: string },\n\tsampleRate: number,\n): Record<string, string> {\n\treturn {\n\t\tOTEL_EXPORTER_OTLP_ENDPOINT: destination.endpoint,\n\t\t...(destination.headers\n\t\t\t? { OTEL_EXPORTER_OTLP_HEADERS: destination.headers }\n\t\t\t: {}),\n\t\tOTEL_TRACES_SAMPLER: TRACES_SAMPLER,\n\t\tOTEL_TRACES_SAMPLER_ARG: String(sampleRate),\n\t};\n}\n\n/** What an `otlp` stage resolves, for every process with the edge. */\nexport function otlpTelemetryEnv(\n\tresolved: Extract<ResolvedTelemetry, { provider: 'otlp' }>,\n): Record<string, string> {\n\tconst headers = otlpHeaderString(resolved.headers);\n\treturn telemetryEnv(\n\t\t{ endpoint: resolved.endpoint, ...(headers ? { headers } : {}) },\n\t\tresolved.sampleRate,\n\t);\n}\n\n/**\n * The sample rate a stage's browsers trace page views at — a public value,\n * the same rate its servers sample at — or undefined when the stage sends no\n * telemetry. The local stage keeps every trace.\n *\n * Lenient where `resolveStageTelemetry` is strict: a site's client is built\n * before, and apart from, the deploy that refuses a stage with no provider.\n */\nexport function stageSampleRate(\n\ttelemetry: Readonly<Record<string, StageTelemetryConfig>> | undefined,\n\tstage: string,\n\tlocal: boolean,\n): number | undefined {\n\tif (local) return 1;\n\tconst config = telemetry?.[stage];\n\tif (config === false) return undefined;\n\tif (config === undefined || config === 'self-hosted') return 1;\n\treturn sampleRateOf(stage, config.sampleRate);\n}\n","import type { StagesConfig } from './types.js';\n\n/**\n * What `gkm test` runs as. The CLI's own, so no project stage may take it:\n * its containers are torn down and rebuilt between runs.\n */\nexport const TEST_STAGE = 'test';\n\n/** A stage becomes part of every physical name, so it has to fit in one. */\nconst STAGE_NAME = /^[a-z][a-z0-9-]*$/;\n\n/** Everything wrong with one stage name, or nothing. */\nfunction nameProblems(name: string): string[] {\n\tconst problems: string[] = [];\n\tif (!STAGE_NAME.test(name)) {\n\t\tproblems.push(\n\t\t\t`stage \"${name}\" must be lowercase letters, digits and hyphens, starting with a letter`,\n\t\t);\n\t}\n\tif (name === TEST_STAGE) {\n\t\tproblems.push(`stage \"${TEST_STAGE}\" is reserved for gkm test`);\n\t}\n\treturn problems;\n}\n\n/** Everything wrong with a list of deployed stages on its own. */\nexport function deployedProblems(deployed: readonly string[]): string[] {\n\tconst problems = deployed.flatMap(nameProblems);\n\tconst seen = new Set<string>();\n\tfor (const name of deployed) {\n\t\tif (seen.has(name)) problems.push(`stage \"${name}\" is listed twice`);\n\t\tseen.add(name);\n\t}\n\treturn problems;\n}\n\n/** Everything wrong with a stages block, or nothing. */\nexport function stageProblems(stages: StagesConfig | undefined): string[] {\n\tif (!stages) {\n\t\treturn [\n\t\t\t\"`stages` is required, e.g. stages: { local: 'dev', deployed: ['prod'] }\",\n\t\t];\n\t}\n\n\tconst { local, deployed, protected: kept = [] } = stages;\n\tconst problems = [...nameProblems(local), ...deployedProblems(deployed)];\n\n\tif (deployed.includes(local)) {\n\t\tproblems.push(\n\t\t\t`\"${local}\" is both the local stage and a deployed one; they would share secrets`,\n\t\t);\n\t}\n\n\tfor (const name of kept) {\n\t\tif (!deployed.includes(name)) {\n\t\t\tproblems.push(`protected stage \"${name}\" is not a deployed stage`);\n\t\t}\n\t}\n\n\treturn problems;\n}\n\n/** The stages block, or an error naming everything wrong with it. */\nexport function validateStages(stages: StagesConfig | undefined): StagesConfig {\n\tconst problems = stageProblems(stages);\n\tif (problems.length) {\n\t\tthrow new InvalidStages(problems);\n\t}\n\treturn stages!;\n}\n\n/** Refuses a stage the project does not deploy to. */\nexport function assertDeployedStage(stages: StagesConfig, stage: string): void {\n\tif (!stages.deployed.includes(stage)) {\n\t\tthrow new UndeclaredStage(stage, stages.deployed);\n\t}\n}\n\n/** A stages block — in gkm.config.ts or from `gkm init`'s flags — that breaks a rule. */\nexport class InvalidStages extends Error {\n\tconstructor(readonly problems: readonly string[]) {\n\t\tsuper(`Invalid stages:\\n ${problems.join('\\n ')}`);\n\t\tthis.name = 'InvalidStages';\n\t}\n}\n\n/**\n * A stage the project does not deploy to. Refused before anything is\n * provisioned, so a typo does not become a second environment.\n */\nexport class UndeclaredStage extends Error {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly deployed: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`\"${stage}\" is not a deployed stage. gkm.config.ts deploys to: ${deployed.join(', ') || 'nothing yet'}.`,\n\t\t);\n\t\tthis.name = 'UndeclaredStage';\n\t}\n}\n","import { z } from 'zod/v4';\nimport { checkComposeStages } from '../compose/proxy.js';\nimport { checkStageProvider } from '../providers/config.js';\nimport {\n\tBUILTIN_TARGETS,\n\tbuiltinTarget,\n\tconfigurableBuiltins,\n} from '../target/builtins.js';\nimport { isDeployTarget } from '../target/define.js';\nimport { checkStageTelemetry } from '../telemetry/config.js';\nimport { stageProblems } from './stages.js';\n\n/** Routes are a glob, or a list of them. */\nconst RoutesSchema = z.union([z.string(), z.array(z.string())]);\n\n/**\n * Telescope configuration schema.\n */\nconst TelescopeConfigSchema = z.object({\n\tenabled: z.boolean().optional(),\n\tport: z.number().optional(),\n\tpath: z.string().optional(),\n\tignore: z.array(z.string()).optional(),\n\trecordBody: z.boolean().optional(),\n\tmaxEntries: z.number().optional(),\n\twebsocket: z.boolean().optional(),\n});\n\n/**\n * OpenAPI configuration schema.\n */\nconst OpenApiConfigSchema = z.object({\n\tenabled: z.boolean().optional(),\n\ttitle: z.string().optional(),\n\tversion: z.string().optional(),\n\tdescription: z.string().optional(),\n});\n\n/**\n * Hooks configuration schema.\n */\nconst HooksConfigSchema = z.object({\n\tserver: z.string().optional(),\n});\n\n/**\n * Backend framework schema for non-gkm apps.\n */\nexport const BackendFrameworkSchema = z.enum([\n\t'hono',\n\t'better-auth',\n\t'express',\n\t'fastify',\n]);\n\n/**\n * Backend framework types for apps that don't use gkm routes.\n * Derived from {@link BackendFrameworkSchema}.\n *\n * Used with `entry` to specify the framework for proper Docker builds.\n *\n * @example\n * ```ts\n * // Better Auth server\n * {\n * entry: './src/index.ts',\n * framework: 'better-auth',\n * port: 3001,\n * }\n *\n * // Hono app without gkm routes\n * {\n * entry: './src/server.ts',\n * framework: 'hono',\n * port: 3000,\n * }\n * ```\n */\nexport type BackendFramework = z.infer<typeof BackendFrameworkSchema>;\n\n/**\n * Frontend framework schema.\n */\nexport const FrontendFrameworkSchema = z.enum([\n\t'nextjs',\n\t'remix',\n\t'vite',\n\t'tanstack-start',\n]);\n\n/**\n * Frontend framework types. Derived from {@link FrontendFrameworkSchema}.\n *\n * @example\n * ```ts\n * // Next.js app\n * {\n * type: 'web',\n * framework: 'nextjs',\n * port: 3000,\n * }\n *\n * // Vite SPA\n * {\n * type: 'web',\n * framework: 'vite',\n * port: 5173,\n * }\n *\n * // TanStack Start (Vite-based, full-stack)\n * {\n * type: 'web',\n * framework: 'tanstack-start',\n * port: 3000,\n * }\n * ```\n */\nexport type FrontendFramework = z.infer<typeof FrontendFrameworkSchema>;\n\n/**\n * Mobile framework schema.\n */\nexport const MobileFrameworkSchema = z.enum(['expo']);\n\n/**\n * Mobile framework types. Derived from {@link MobileFrameworkSchema}.\n *\n * Mobile apps deploy via their own toolchain (e.g. EAS Build for Expo)\n * rather than Docker/Dokploy. Only build-time public env vars\n * (e.g. `EXPO_PUBLIC_*`) reach the device — server secrets are dropped.\n *\n * @example\n * ```ts\n * {\n * type: 'mobile',\n * framework: 'expo',\n * path: 'apps/mobile',\n * port: 8081,\n * dependencies: ['api'],\n * }\n * ```\n */\nexport type MobileFramework = z.infer<typeof MobileFrameworkSchema>;\n\n/**\n * Combined framework schema (backend, frontend, or mobile).\n */\nexport const FrameworkSchema = z.union([\n\tBackendFrameworkSchema,\n\tFrontendFrameworkSchema,\n\tMobileFrameworkSchema,\n]);\n\n/**\n * Any framework value across backend, frontend, or mobile.\n * Derived from {@link FrameworkSchema}.\n */\nexport type Framework = z.infer<typeof FrameworkSchema>;\n\n/** Frontend framework values, kept in sync with FrontendFrameworkSchema. */\nconst FRONTEND_FRAMEWORKS = [\n\t'nextjs',\n\t'remix',\n\t'vite',\n\t'tanstack-start',\n] as const;\n\n/** Mobile framework values, kept in sync with MobileFrameworkSchema. */\nconst MOBILE_FRAMEWORKS = ['expo'] as const;\n\n/**\n * A deploy target's name. Which names exist is the workspace's to say — the\n * built-ins and its own `deploy.targets` — so it is checked against both\n * once the whole config is read.\n */\nconst DeployTargetSchema = z.string().min(1);\n\n/**\n * The built-in targets a config may name, both deployed by `gkm deploy`:\n * `dokploy`, and `sst`, which runs `sst deploy` on the manifest `gkm build`\n * writes.\n */\nconst SUPPORTED_DEPLOY_TARGETS = configurableBuiltins();\n\n/** Built-in names reserved for targets that do not exist yet. */\nconst PHASE_2_DEPLOY_TARGETS = Object.entries(BUILTIN_TARGETS)\n\t.filter(([, target]) => target.status === 'planned')\n\t.map(([name]) => name);\n\n/**\n * Whether a config may name `target`: a usable built-in, or one of its own\n * `deploy.targets`.\n */\nexport function isDeployTargetSupported(\n\ttarget: string,\n\ttargets: Record<string, unknown> = {},\n): boolean {\n\treturn (\n\t\tSUPPORTED_DEPLOY_TARGETS.includes(target) || Object.hasOwn(targets, target)\n\t);\n}\n\n/**\n * Check if a deploy target is planned for Phase 2.\n */\nexport function isPhase2DeployTarget(target: string): boolean {\n\treturn PHASE_2_DEPLOY_TARGETS.includes(target);\n}\n\n/**\n * Get error message for unsupported deploy targets.\n */\nexport function getDeployTargetError(target: string, appName?: string): string {\n\tif (isPhase2DeployTarget(target)) {\n\t\tconst context = appName ? ` for app \"${appName}\"` : '';\n\t\treturn `Deploy target \"${target}\"${context} is coming in Phase 2. Currently \"dokploy\" and \"sst\" are supported.`;\n\t}\n\treturn `Unknown deploy target: ${target}. Built in: ${SUPPORTED_DEPLOY_TARGETS.join(', ')}; any other is named in deploy.targets with the package that provides it. Coming in Phase 2: ${PHASE_2_DEPLOY_TARGETS.join(', ')}.`;\n}\n\n/**\n * One `deploy.targets` entry: a package name, a target object, or either\n * with options.\n */\nconst DeployTargetEntrySchema = z.union([\n\tz.string().min(1, 'A target package name cannot be empty'),\n\tz.custom((value) => isDeployTarget(value), {\n\t\tmessage:\n\t\t\t'Expected a package name, a target (defineTarget), or [package or target, options]',\n\t}),\n\tz.tuple([\n\t\tz.union([\n\t\t\tz.string().min(1),\n\t\t\tz.custom((value) => isDeployTarget(value), {\n\t\t\t\tmessage: 'Expected a package name or a target (defineTarget)',\n\t\t\t}),\n\t\t]),\n\t\tz.unknown(),\n\t]),\n]);\n\n/**\n * Dokploy workspace configuration schema.\n * Supports either a single endpoint or per-stage endpoints.\n */\nconst DokployWorkspaceConfigSchema = z\n\t.object({\n\t\t/** Single endpoint for all stages */\n\t\tendpoint: z.url('Dokploy endpoint must be a valid URL').optional(),\n\t\t/** Per-stage endpoints (stage name -> endpoint URL) */\n\t\tendpoints: z\n\t\t\t.record(z.string(), z.url('Endpoint must be a valid URL'))\n\t\t\t.optional(),\n\t\tregistryId: z.string().optional(),\n\t\tverify: z\n\t\t\t.object({\n\t\t\t\tdeploymentTimeoutMs: z.number().int().positive().optional(),\n\t\t\t\thealthCheckPath: z\n\t\t\t\t\t.string()\n\t\t\t\t\t.startsWith('/', 'healthCheckPath must start with /')\n\t\t\t\t\t.optional(),\n\t\t\t\thealthyAfter: z.number().int().positive().optional(),\n\t\t\t\tintervalMs: z.number().int().positive().optional(),\n\t\t\t\thealthTimeoutMs: z.number().int().positive().optional(),\n\t\t\t})\n\t\t\t.optional(),\n\t})\n\t.refine((data) => data.endpoint || data.endpoints, {\n\t\tmessage: 'Either endpoint or endpoints must be provided',\n\t});\n\n// =============================================================================\n// AWS Regions (needed by DNS and State providers)\n// =============================================================================\n\n/**\n * Valid AWS regions.\n */\nconst AwsRegionSchema = z.enum([\n\t'us-east-1',\n\t'us-east-2',\n\t'us-west-1',\n\t'us-west-2',\n\t'af-south-1',\n\t'ap-east-1',\n\t'ap-south-1',\n\t'ap-south-2',\n\t'ap-southeast-1',\n\t'ap-southeast-2',\n\t'ap-southeast-3',\n\t'ap-southeast-4',\n\t'ap-northeast-1',\n\t'ap-northeast-2',\n\t'ap-northeast-3',\n\t'ca-central-1',\n\t'eu-central-1',\n\t'eu-central-2',\n\t'eu-west-1',\n\t'eu-west-2',\n\t'eu-west-3',\n\t'eu-south-1',\n\t'eu-south-2',\n\t'eu-north-1',\n\t'me-south-1',\n\t'me-central-1',\n\t'sa-east-1',\n]);\n\n// =============================================================================\n// DNS Record Types (used by DnsProvider interface)\n// =============================================================================\n\n/**\n * DNS record types supported across providers.\n */\nexport const DnsRecordTypeSchema = z.enum([\n\t'A',\n\t'AAAA',\n\t'CNAME',\n\t'MX',\n\t'TXT',\n\t'NS',\n\t'SRV',\n\t'CAA',\n]);\n\n/**\n * A DNS record as returned by the provider.\n */\nexport const DnsRecordSchema = z.object({\n\t/** Subdomain name (e.g., 'api' for api.example.com, '@' for root) */\n\tname: z.string(),\n\t/** Record type */\n\ttype: DnsRecordTypeSchema,\n\t/** TTL in seconds */\n\tttl: z.number().int().positive(),\n\t/** Record values */\n\tvalues: z.array(z.string()),\n});\n\n/**\n * A DNS record to create or update.\n */\nexport const UpsertDnsRecordSchema = z.object({\n\t/** Subdomain name (e.g., 'api' for api.example.com, '@' for root) */\n\tname: z.string(),\n\t/** Record type */\n\ttype: DnsRecordTypeSchema,\n\t/** TTL in seconds */\n\tttl: z.number().int().positive(),\n\t/** Record value (IP address, hostname, etc.) */\n\tvalue: z.string(),\n});\n\n/**\n * Result of an upsert operation.\n */\nexport const UpsertResultSchema = z.object({\n\t/** The record that was upserted */\n\trecord: UpsertDnsRecordSchema,\n\t/** Whether the record was created (true) or updated (false) */\n\tcreated: z.boolean(),\n\t/** Whether the record already existed with the same value */\n\tunchanged: z.boolean(),\n});\n\n// =============================================================================\n// DNS Provider Configuration\n// =============================================================================\n\n/**\n * Hostinger DNS provider config (without domain - domain is the record key).\n */\nexport const HostingerDnsProviderSchema = z.object({\n\tprovider: z.literal('hostinger'),\n\t/** TTL in seconds (default: 300) */\n\tttl: z.number().int().positive().optional(),\n});\n\n/**\n * Route53 DNS provider config (without domain - domain is the record key).\n */\nexport const Route53DnsProviderSchema = z.object({\n\tprovider: z.literal('route53'),\n\t/** AWS region (optional - uses AWS_REGION env var if not provided) */\n\tregion: AwsRegionSchema.optional(),\n\t/** AWS profile name (optional - uses default credential chain if not provided) */\n\tprofile: z.string().optional(),\n\t/** Hosted zone ID (optional - auto-detected from domain if not provided) */\n\thostedZoneId: z.string().optional(),\n\t/** TTL in seconds (default: 300) */\n\tttl: z.number().int().positive().optional(),\n});\n\n/**\n * Cloudflare DNS provider config (placeholder for future).\n */\nexport const CloudflareDnsProviderSchema = z.object({\n\tprovider: z.literal('cloudflare'),\n\t/** TTL in seconds (default: 300) */\n\tttl: z.number().int().positive().optional(),\n});\n\n/**\n * Manual DNS configuration (user handles DNS themselves).\n */\nexport const ManualDnsProviderSchema = z.object({\n\tprovider: z.literal('manual'),\n});\n\n/**\n * Custom DNS provider config (user-provided implementation).\n */\nexport const CustomDnsProviderSchema = z.object({\n\t/** Custom DnsProvider implementation */\n\tprovider: z.custom<{\n\t\tname: string;\n\t\tgetRecords: Function;\n\t\tupsertRecords: Function;\n\t}>(\n\t\t(val) =>\n\t\t\ttypeof val === 'object' &&\n\t\t\tval !== null &&\n\t\t\ttypeof (val as any).name === 'string' &&\n\t\t\ttypeof (val as any).getRecords === 'function' &&\n\t\t\ttypeof (val as any).upsertRecords === 'function',\n\t\t{\n\t\t\tmessage:\n\t\t\t\t'Custom DNS provider must implement name, getRecords(), and upsertRecords() methods',\n\t\t},\n\t),\n\t/** TTL in seconds (default: 300) */\n\tttl: z.number().int().positive().optional(),\n});\n\n/**\n * Built-in DNS provider config (discriminated union).\n */\nexport const BuiltInDnsProviderSchema = z.discriminatedUnion('provider', [\n\tHostingerDnsProviderSchema,\n\tRoute53DnsProviderSchema,\n\tCloudflareDnsProviderSchema,\n\tManualDnsProviderSchema,\n]);\n\n/**\n * Single DNS provider config (for one domain).\n */\nexport const DnsProviderSchema = z.union([\n\tBuiltInDnsProviderSchema,\n\tCustomDnsProviderSchema,\n]);\n\nexport type DnsProvider = z.infer<typeof DnsProviderSchema>;\n\n/**\n * DNS configuration schema.\n *\n * Maps root domains to their DNS provider configuration.\n * Example:\n * ```\n * dns: {\n * 'geekmidas.dev': { provider: 'hostinger' },\n * 'geekmidas.com': { provider: 'route53' },\n * }\n * ```\n *\n * Supported providers:\n * - 'hostinger': Use Hostinger DNS API\n * - 'route53': Use AWS Route53\n * - 'cloudflare': Use Cloudflare DNS API (future)\n * - 'manual': Don't create records, just print required records\n * - Custom: Provide a DnsProvider implementation\n */\nexport const DnsConfigSchema = z.record(z.string(), DnsProviderSchema);\n\n// Legacy single-domain config schemas (for backwards compatibility)\nexport const HostingerDnsConfigSchema = HostingerDnsProviderSchema.extend({\n\tdomain: z.string().min(1, 'Domain is required'),\n});\nexport const Route53DnsConfigSchema = Route53DnsProviderSchema.extend({\n\tdomain: z.string().min(1, 'Domain is required'),\n});\nexport const CloudflareDnsConfigSchema = CloudflareDnsProviderSchema.extend({\n\tdomain: z.string().min(1, 'Domain is required'),\n});\nexport const ManualDnsConfigSchema = ManualDnsProviderSchema.extend({\n\tdomain: z.string().min(1, 'Domain is required'),\n});\nexport const CustomDnsConfigSchema = CustomDnsProviderSchema.extend({\n\tdomain: z.string().min(1, 'Domain is required'),\n});\nexport const BuiltInDnsConfigSchema = z.discriminatedUnion('provider', [\n\tHostingerDnsConfigSchema,\n\tRoute53DnsConfigSchema,\n\tCloudflareDnsConfigSchema,\n\tManualDnsConfigSchema,\n]);\nexport const LegacyDnsConfigSchema = z.union([\n\tBuiltInDnsConfigSchema,\n\tCustomDnsConfigSchema,\n]);\n\n/**\n * Combined DNS config that supports both new multi-domain and legacy single-domain formats.\n */\nexport const DnsConfigWithLegacySchema = z.union([\n\tDnsConfigSchema,\n\tLegacyDnsConfigSchema,\n]);\n\nexport type DnsConfig = z.infer<typeof DnsConfigWithLegacySchema>;\n\n/**\n * Backups configuration schema.\n *\n * Configures automatic backup destinations for database services.\n * On first deploy, creates S3 bucket with unique name and IAM credentials.\n */\nexport const BackupsConfigSchema = z.object({\n\t/** Backup storage type (currently only 's3' supported) */\n\ttype: z.literal('s3'),\n\t/** AWS profile name for creating bucket/IAM resources */\n\tprofile: z.string().optional(),\n\t/** AWS region for the backup bucket */\n\tregion: AwsRegionSchema,\n\t/** Cron schedule for backups (default: '0 2 * * *' = 2 AM daily) */\n\tschedule: z.string().optional(),\n\t/** Number of backups to retain (default: 30) */\n\tretention: z.number().optional(),\n});\n\nexport type BackupsConfig = z.infer<typeof BackupsConfigSchema>;\n\n/** `deploy.compose` — what the compose target runs beside the apps. */\nconst ComposeWorkspaceConfigSchema = z\n\t.object({\n\t\tproxy: z\n\t\t\t.union([\n\t\t\t\tz.enum(['caddy', 'traefik']),\n\t\t\t\tz.record(z.string(), z.enum(['caddy', 'traefik'])),\n\t\t\t])\n\t\t\t.optional(),\n\t\ttls: z\n\t\t\t.record(\n\t\t\t\tz.string(),\n\t\t\t\tz\n\t\t\t\t\t.object({ certFile: z.string().min(1), keyFile: z.string().min(1) })\n\t\t\t\t\t.strict(),\n\t\t\t)\n\t\t\t.optional(),\n\t})\n\t// Strict, so a key it no longer takes — `logs`, now a `Telemetry`\n\t// construct and `deploy.telemetry` — fails to load rather than being\n\t// dropped without a word.\n\t.strict();\n\nconst TelemetrySampleRate = z.number().optional();\n\n/**\n * `deploy.telemetry` — where each stage's telemetry goes. The rules past the\n * shape are `checkStageTelemetry`'s, so the schema and a deploy refuse the\n * same configs with the same words.\n */\nconst TelemetryConfigSchema = z\n\t.record(\n\t\tz.string(),\n\t\tz.union([\n\t\t\tz.literal(false),\n\t\t\tz.literal('self-hosted'),\n\t\t\tz\n\t\t\t\t.object({\n\t\t\t\t\tprovider: z.literal('self-hosted'),\n\t\t\t\t\tport: z.number().optional(),\n\t\t\t\t\tretentionDays: z.number().optional(),\n\t\t\t\t\tpublic: z.object({ allow: z.array(z.string()) }).optional(),\n\t\t\t\t\tsampleRate: TelemetrySampleRate,\n\t\t\t\t})\n\t\t\t\t.strict(),\n\t\t\tz\n\t\t\t\t.object({\n\t\t\t\t\tprovider: z.literal('otlp'),\n\t\t\t\t\tendpoint: z.string(),\n\t\t\t\t\theaders: z.record(z.string(), z.string()).optional(),\n\t\t\t\t\tsampleRate: TelemetrySampleRate,\n\t\t\t\t})\n\t\t\t\t.strict(),\n\t\t]),\n\t)\n\t.superRefine((telemetry, ctx) => {\n\t\tfor (const [stage, config] of Object.entries(telemetry)) {\n\t\t\ttry {\n\t\t\t\tcheckStageTelemetry(stage, config);\n\t\t\t} catch (error) {\n\t\t\t\tctx.addIssue({\n\t\t\t\t\tcode: 'custom',\n\t\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t\t\tpath: [stage],\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t});\n\n/**\n * `deploy.objects` — what backs each deployed stage's buckets. The rules are\n * `checkStageProvider`'s, so the schema and a provision run refuse the same\n * entries with the same words.\n */\nconst ObjectsConfigSchema = z\n\t.record(z.string(), z.unknown())\n\t.superRefine((entries, ctx) => {\n\t\tfor (const [stage, value] of Object.entries(entries)) {\n\t\t\ttry {\n\t\t\t\tcheckStageProvider('objects', stage, value);\n\t\t\t} catch (error) {\n\t\t\t\tctx.addIssue({\n\t\t\t\t\tcode: 'custom',\n\t\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t\t\tpath: [stage],\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t});\n\n/**\n * Deploy configuration schema.\n */\nconst DeployConfigSchema = z.object({\n\tdefault: DeployTargetSchema.optional(),\n\ttargets: z\n\t\t.record(z.string(), DeployTargetEntrySchema)\n\t\t.superRefine((targets, ctx) => {\n\t\t\tfor (const name of Object.keys(targets)) {\n\t\t\t\t// The built-ins resolve first, so an entry under one of their\n\t\t\t\t// names would never be used — say so rather than ignore it.\n\t\t\t\tif (builtinTarget(name)) {\n\t\t\t\t\tctx.addIssue({\n\t\t\t\t\t\tcode: 'custom',\n\t\t\t\t\t\tmessage: `\"${name}\" is a built-in target; give this one a name of its own`,\n\t\t\t\t\t\tpath: [name],\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t\t.optional(),\n\t/** Whose deploy this is, on a target shared with other workspaces. */\n\tnamespace: z\n\t\t.string()\n\t\t.regex(\n\t\t\t/^[a-z0-9]+(?:-[a-z0-9]+)*$/,\n\t\t\t\"deploy.namespace must be lowercase letters and digits with single '-' between them\",\n\t\t)\n\t\t.optional(),\n\t/** Each deployed stage's base domain (stage name -> domain). */\n\tdomains: z.record(z.string(), z.string()).optional(),\n\t/** Where every target pushes and pulls the apps' images. */\n\tregistry: z.string().min(1).optional(),\n\tdokploy: DokployWorkspaceConfigSchema.optional(),\n\tcompose: ComposeWorkspaceConfigSchema.optional(),\n\ttelemetry: TelemetryConfigSchema.optional(),\n\tobjects: ObjectsConfigSchema.optional(),\n\tdns: DnsConfigWithLegacySchema.optional(),\n\tbackups: BackupsConfigSchema.optional(),\n});\n\n/**\n * Models configuration schema.\n */\nconst ModelsConfigSchema = z.object({\n\tpath: z.string().optional(),\n\tschema: z.enum(['zod']).optional(),\n});\n\n/**\n * Shared configuration schema.\n */\nconst SharedConfigSchema = z.object({\n\tpackages: z.array(z.string()).optional(),\n\tmodels: ModelsConfigSchema.optional(),\n});\n\n/**\n * Secrets configuration schema.\n */\nconst StagesConfigSchema = z\n\t.object({\n\t\tlocal: z.string(),\n\t\tdeployed: z.array(z.string()),\n\t\tprotected: z.array(z.string()).optional(),\n\t})\n\t.superRefine((stages, ctx) => {\n\t\tfor (const message of stageProblems(stages)) {\n\t\t\tctx.addIssue({ code: 'custom', message });\n\t\t}\n\t});\n\nconst SecretsStoreSchema = z.union([\n\tz.literal('file'),\n\tz.object({ provider: z.literal('ssm'), region: AwsRegionSchema }).strict(),\n\tz\n\t\t.object({\n\t\t\tprovider: z.literal('secrets-manager'),\n\t\t\tregion: AwsRegionSchema,\n\t\t\tkmsKeyId: z.string().min(1).optional(),\n\t\t})\n\t\t.strict(),\n\tz.object({\n\t\t/** Any backend: an object implementing SecretsStore */\n\t\tprovider: z.custom<{ name: string; read: Function; write: Function }>(\n\t\t\t(val) =>\n\t\t\t\ttypeof val === 'object' &&\n\t\t\t\tval !== null &&\n\t\t\t\ttypeof (val as any).name === 'string' &&\n\t\t\t\ttypeof (val as any).read === 'function' &&\n\t\t\t\ttypeof (val as any).write === 'function',\n\t\t\t{ message: 'a secrets store has a name, read() and write()' },\n\t\t),\n\t}),\n]);\n\nconst SecretsConfigSchema = z.object({\n\tenabled: z.boolean().optional(),\n\tstore: SecretsStoreSchema.optional(),\n\talgorithm: z.string().optional(),\n\tkdf: z.enum(['scrypt', 'pbkdf2']).optional(),\n});\n\n// =============================================================================\n// State Provider Configuration\n// =============================================================================\n\n/**\n * Local state provider config.\n */\nconst LocalStateConfigSchema = z.object({\n\tprovider: z.literal('local'),\n});\n\n/**\n * SSM state provider config (requires region).\n */\nconst SSMStateConfigSchema = z.object({\n\tprovider: z.literal('ssm'),\n\t/** AWS region (required for SSM provider) */\n\tregion: AwsRegionSchema,\n\t/** AWS profile name (optional - uses default credential chain if not provided) */\n\tprofile: z.string().optional(),\n});\n\n/**\n * S3 state provider config: conditional writes and a lock object in a bucket.\n */\nconst S3StateConfigSchema = z.object({\n\tprovider: z.literal('s3'),\n\t/** Bucket the state lives in (must already exist) */\n\tbucket: z.string().min(1),\n\t/** AWS region of the bucket */\n\tregion: AwsRegionSchema,\n\t/** Key prefix inside the bucket (default: 'gkm') */\n\tprefix: z.string().optional(),\n\t/** AWS profile name (optional - uses default credential chain if not provided) */\n\tprofile: z.string().optional(),\n});\n\n/**\n * Custom state provider config (user-provided implementation).\n */\nconst CustomStateConfigSchema = z.object({\n\t/** Custom StateProvider implementation */\n\tprovider: z.custom<{ read: Function; write: Function }>(\n\t\t(val) =>\n\t\t\ttypeof val === 'object' &&\n\t\t\tval !== null &&\n\t\t\ttypeof (val as any).read === 'function' &&\n\t\t\ttypeof (val as any).write === 'function',\n\t\t{ message: 'Custom provider must implement read() and write() methods' },\n\t),\n});\n\n/**\n * Built-in state provider config (discriminated union).\n */\nconst BuiltInStateConfigSchema = z.discriminatedUnion('provider', [\n\tLocalStateConfigSchema,\n\tSSMStateConfigSchema,\n\tS3StateConfigSchema,\n]);\n\n/**\n * State configuration schema.\n *\n * Configures how deployment state is stored.\n * - 'local': Store in .gkm/deploy-{stage}.json (default)\n * - 'ssm': Store in AWS SSM Parameter Store (requires region)\n * - 's3': Store in an S3 bucket with conditional writes (requires bucket, region)\n * - Custom: Provide a StateStore, or a StateProvider with read/write methods\n */\nconst StateConfigSchema = z.union([\n\tBuiltInStateConfigSchema,\n\tCustomStateConfigSchema,\n]);\n\n/**\n * App configuration schema.\n */\nconst AppConfigSchema = z\n\t.object({\n\t\t// Core properties\n\t\ttype: z.enum(['backend', 'web', 'mobile']).optional().default('backend'),\n\t\tpath: z.string().min(1, 'App path is required'),\n\t\tport: z.number().int().positive('Port must be a positive integer'),\n\t\tdependencies: z.array(z.string()).optional(),\n\t\tdeploy: DeployTargetSchema.optional(),\n\n\t\t// Backend-specific (from GkmConfig)\n\t\tconstructs: RoutesSchema.optional(),\n\t\troutes: RoutesSchema.optional(),\n\t\tfunctions: RoutesSchema.optional(),\n\t\tcrons: RoutesSchema.optional(),\n\t\tqueues: RoutesSchema.optional(),\n\t\ttopics: RoutesSchema.optional(),\n\t\tsubscribers: RoutesSchema.optional(),\n\t\tenvParser: z.string().optional(),\n\t\tlogger: z.string().optional(),\n\t\thooks: HooksConfigSchema.optional(),\n\t\ttelescope: z\n\t\t\t.union([z.string(), z.boolean(), TelescopeConfigSchema])\n\t\t\t.optional(),\n\t\topenapi: z.union([z.boolean(), OpenApiConfigSchema]).optional(),\n\t\truntime: z.enum(['node', 'bun']).optional(),\n\t\tenv: z.union([z.string(), z.array(z.string())]).optional(),\n\n\t\t// Entry point for non-gkm apps (used by dev and docker build)\n\t\tentry: z.string().optional(),\n\n\t\t// Framework (backend, web, or mobile)\n\t\tframework: FrameworkSchema.optional(),\n\t\t// Web/mobile: config file paths for env sniffing (calls .parse() at import)\n\t\tconfig: z\n\t\t\t.object({\n\t\t\t\tclient: z.string().optional(),\n\t\t\t\tserver: z.string().optional(),\n\t\t\t})\n\t\t\t.optional(),\n\t})\n\t// Note: routes is optional for backend apps - some backends like auth servers don't use routes\n\t.refine(\n\t\t(data) => {\n\t\t\t// Web apps must have a web framework\n\t\t\tif (data.type === 'web') {\n\t\t\t\tif (\n\t\t\t\t\t!data.framework ||\n\t\t\t\t\t!(FRONTEND_FRAMEWORKS as readonly string[]).includes(data.framework)\n\t\t\t\t) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t},\n\t\t{\n\t\t\tmessage: `Web apps must have a valid web framework (${FRONTEND_FRAMEWORKS.join(', ')})`,\n\t\t\tpath: ['framework'],\n\t\t},\n\t)\n\t.refine(\n\t\t(data) => {\n\t\t\t// Mobile apps must have a mobile framework\n\t\t\tif (data.type === 'mobile') {\n\t\t\t\tif (\n\t\t\t\t\t!data.framework ||\n\t\t\t\t\t!(MOBILE_FRAMEWORKS as readonly string[]).includes(data.framework)\n\t\t\t\t) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t},\n\t\t{\n\t\t\tmessage: `Mobile apps must have a valid mobile framework (${MOBILE_FRAMEWORKS.join(', ')})`,\n\t\t\tpath: ['framework'],\n\t\t},\n\t);\n\n/** What `gkm test` hands a feature test. */\nconst TestConfigSchema = z\n\t.object({\n\t\t/** The folder of per-database factories; `test/factories` by default. */\n\t\tfactories: z.string().optional(),\n\t})\n\t.strict();\n\n/** How `gkm dev` is reached from outside the apps it runs. */\nconst DevConfigSchema = z\n\t.object({\n\t\t/** Browser origins that may read the discovery endpoint. */\n\t\tallowedOrigins: z\n\t\t\t.array(\n\t\t\t\tz.url().refine(\n\t\t\t\t\t// Checked even when `z.url()` has already failed it.\n\t\t\t\t\t(value) => URL.canParse(value) && new URL(value).origin === value,\n\t\t\t\t\t{\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t'An origin is a scheme, host and port only, e.g. https://console.example.com — no path or trailing slash',\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t)\n\t\t\t.optional(),\n\t\t/** The discovery endpoint's loopback port; 4983 by default. */\n\t\tdiscoveryPort: z.number().int().min(0).max(65535).optional(),\n\t})\n\t.strict();\n\n/**\n * Workspace configuration schema.\n */\nexport const WorkspaceConfigSchema = z\n\t.object({\n\t\tname: z.string().optional(),\n\t\t/**\n\t\t * Where the workspace's own constructs live, relative to its root.\n\t\t *\n\t\t * Infrastructure is a fact about the product, not about the process that\n\t\t * happens to import it — so a database two apps share is declared once,\n\t\t * at the top, and both find it. An app-level glob still works and is\n\t\t * additive, for something only one app uses.\n\t\t */\n\t\tconstructs: RoutesSchema.optional(),\n\t\t/**\n\t\t * Apps that nothing declares.\n\t\t *\n\t\t * Optional, and normally absent: a `site` is an app and so is a\n\t\t * `rest-api` that named one, so the list is read off the graph. This is\n\t\t * the escape hatch for what no construct describes — a mobile app, a\n\t\t * process someone runs by hand — and for overriding one field of a\n\t\t * derived app without restating it.\n\t\t */\n\t\tapps: z.record(z.string(), AppConfigSchema).optional(),\n\t\tshared: SharedConfigSchema.optional(),\n\t\tdeploy: DeployConfigSchema.optional(),\n\t\tstages: StagesConfigSchema,\n\t\tsecrets: SecretsConfigSchema.optional(),\n\t\tstate: StateConfigSchema.optional(),\n\t\ttest: TestConfigSchema.optional(),\n\t\tdev: DevConfigSchema.optional(),\n\t})\n\t// Strict, so a key this schema no longer has fails instead of being\n\t// dropped: a leftover `services:` block is a question the config used to\n\t// answer, and silently ignoring it would hide that the answer moved.\n\t.strict()\n\t.refine(\n\t\t(data) => {\n\t\t\t// Validate dependencies reference existing apps\n\t\t\tconst appNames = Object.keys(data.apps ?? {});\n\t\t\tfor (const [appName, app] of Object.entries(data.apps ?? {})) {\n\t\t\t\tfor (const dep of app.dependencies ?? []) {\n\t\t\t\t\tif (!appNames.includes(dep)) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\t// Prevent self-dependency\n\t\t\t\t\tif (dep === appName) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t},\n\t\t{\n\t\t\tmessage:\n\t\t\t\t'App dependencies must reference existing apps and cannot be self-referential',\n\t\t},\n\t)\n\t.refine(\n\t\t(data) => {\n\t\t\t// Check for circular dependencies\n\t\t\tconst appNames = Object.keys(data.apps ?? {});\n\t\t\tconst visited = new Set<string>();\n\t\t\tconst recStack = new Set<string>();\n\n\t\t\tfunction hasCycle(app: string): boolean {\n\t\t\t\tif (recStack.has(app)) return true;\n\t\t\t\tif (visited.has(app)) return false;\n\n\t\t\t\tvisited.add(app);\n\t\t\t\trecStack.add(app);\n\n\t\t\t\tconst deps = data.apps?.[app]?.dependencies ?? [];\n\t\t\t\tfor (const dep of deps) {\n\t\t\t\t\tif (hasCycle(dep)) return true;\n\t\t\t\t}\n\n\t\t\t\trecStack.delete(app);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tfor (const app of appNames) {\n\t\t\t\tvisited.clear();\n\t\t\t\trecStack.clear();\n\t\t\t\tif (hasCycle(app)) return false;\n\t\t\t}\n\t\t\treturn true;\n\t\t},\n\t\t{\n\t\t\tmessage: 'Circular dependencies detected between apps',\n\t\t},\n\t)\n\t.superRefine((data, ctx) => {\n\t\t// Validate deploy targets are supported\n\t\tconst defaultTarget = data.deploy?.default;\n\t\tconst ownTargets = data.deploy?.targets ?? {};\n\t\tif (defaultTarget && !isDeployTargetSupported(defaultTarget, ownTargets)) {\n\t\t\tctx.addIssue({\n\t\t\t\tcode: 'custom',\n\t\t\t\tmessage: getDeployTargetError(defaultTarget),\n\t\t\t\tpath: ['deploy', 'default'],\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tfor (const [appName, app] of Object.entries(data.apps ?? {})) {\n\t\t\tif (app.deploy && !isDeployTargetSupported(app.deploy, ownTargets)) {\n\t\t\t\tctx.addIssue({\n\t\t\t\t\tcode: 'custom',\n\t\t\t\t\tmessage: getDeployTargetError(app.deploy, appName),\n\t\t\t\t\tpath: ['apps', appName, 'deploy'],\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// The compose target's per-stage settings name stages this workspace has.\n\t\ttry {\n\t\t\tcheckComposeStages(data.deploy?.compose, data.stages);\n\t\t} catch (error) {\n\t\t\tctx.addIssue({\n\t\t\t\tcode: 'custom',\n\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t\tpath: ['deploy', 'compose'],\n\t\t\t});\n\t\t}\n\n\t\t// Validate workspace name is required for SSM state provider\n\t\tif (data.state?.provider === 'ssm' && !data.name) {\n\t\t\tctx.addIssue({\n\t\t\t\tcode: 'custom',\n\t\t\t\tmessage:\n\t\t\t\t\t'Workspace name is required when using SSM state provider. Add \"name\" to your gkm.config.ts.',\n\t\t\t\tpath: ['name'],\n\t\t\t});\n\t\t}\n\t});\n\n/**\n * Validate workspace configuration.\n * Throws ZodError with detailed messages on validation failure.\n */\nexport function validateWorkspaceConfig(\n\tconfig: unknown,\n): z.infer<typeof WorkspaceConfigSchema> {\n\treturn WorkspaceConfigSchema.parse(config);\n}\n\n/**\n * Safe validation that returns result instead of throwing.\n */\nexport function safeValidateWorkspaceConfig(config: unknown): {\n\tsuccess: boolean;\n\tdata?: z.infer<typeof WorkspaceConfigSchema>;\n\terror?: z.ZodError;\n} {\n\tconst result = WorkspaceConfigSchema.safeParse(config);\n\tif (result.success) {\n\t\treturn { success: true, data: result.data };\n\t}\n\treturn { success: false, error: result.error };\n}\n\n/**\n * Format Zod errors into user-friendly messages.\n */\nexport function formatValidationErrors(error: z.ZodError): string {\n\tconst messages = error.issues.map((issue: z.core.$ZodIssue) => {\n\t\tconst path = issue.path.join('.');\n\t\treturn path ? ` - ${path}: ${issue.message}` : ` - ${issue.message}`;\n\t});\n\n\treturn `Workspace configuration validation failed:\\n${messages.join('\\n')}`;\n}\n\nexport type ValidatedWorkspaceConfig = z.infer<typeof WorkspaceConfigSchema>;\n\nexport { SUPPORTED_DEPLOY_TARGETS, PHASE_2_DEPLOY_TARGETS };\n","import type { ConstructManifest } from '@geekmidas/manifest';\nimport type { AwsRegion, StateConfig } from '../deploy/StateProvider.js';\nimport type { DeployTargetEntry } from '../target/types';\n\nexport type { AwsRegion };\n\nimport type {\n\tGkmConfig,\n\tHooksConfig,\n\tOpenApiConfig,\n\tRoutes,\n\tRuntime,\n\tTelescopeConfig,\n} from '../types.js';\nimport type {\n\tBackendFramework,\n\tBackupsConfig,\n\tDnsConfig,\n\tDnsProvider,\n\tFramework,\n\tFrontendFramework,\n\tMobileFramework,\n} from './schema.js';\n\nexport type {\n\tBackendFramework,\n\tBackupsConfig,\n\tDnsConfig,\n\tDnsProvider,\n\tFramework,\n\tFrontendFramework,\n\tMobileFramework,\n};\n\n/**\n * Where a stage — or one app — deploys, by target name: a built-in\n * (`dokploy`, `sst`) or a key of `deploy.targets`.\n *\n * @example\n * ```ts\n * deploy: 'dokploy'\n * deploy: 'acme' // deploy: { targets: { acme: '@acme/gkm-target' } }\n * ```\n */\nexport type DeployTargetName = 'dokploy' | 'compose' | 'sst' | (string & {});\n\n/**\n * Each deployed stage's base domain — `deploy.domains`.\n *\n * A fact about the deployment rather than the platform, so every target reads\n * it. The root site answers on the base domain; every other surface on\n * `{subdomain}.{domain}`, where the subdomain is the construct's own\n * `subdomain`, or its id kebab-cased.\n *\n * @example\n * ```ts\n * deploy: {\n * domains: { production: 'myapp.com', staging: 'staging.myapp.com' },\n * }\n *\n * // production:\n * // - new StaticSite('Web', { root: true }) → myapp.com\n * // - new RestApi('Api', { subdomain: 'api' }) → api.myapp.com\n * // - new RestApi('Webhooks', { … }) → webhooks.myapp.com\n * ```\n */\nexport type DomainsConfig = Record<string, string>;\n\n/**\n * Per-app domain override configuration.\n *\n * Can be a single domain string (used for all stages) or\n * stage-specific domains.\n *\n * @example\n * ```ts\n * // Single domain for all stages\n * domain: 'api.custom.com'\n *\n * // Stage-specific domains\n * domain: {\n * development: 'api.dev.custom.com',\n * staging: 'api.staging.custom.com',\n * production: 'api.custom.com',\n * }\n * ```\n */\nexport type AppDomainConfig = string | Record<string, string>;\n\n/**\n * Dokploy workspace deployment configuration.\n *\n * Configures how the workspace is deployed to a Dokploy server.\n * One workspace maps to one Dokploy project with stage-based environments.\n * Project IDs are stored in deploy state (not config) and created on first deploy.\n *\n * @example Single endpoint for all stages:\n * ```ts\n * deploy: {\n * default: 'dokploy',\n * registry: 'ghcr.io/myorg',\n * dokploy: {\n * endpoint: 'https://dokploy.myserver.com',\n * },\n * }\n * ```\n *\n * @example Per-stage endpoints (different Dokploy servers):\n * ```ts\n * deploy: {\n * default: 'dokploy',\n * registry: 'ghcr.io/myorg',\n * dokploy: {\n * endpoints: {\n * development: 'https://dev-dokploy.myserver.com',\n * production: 'https://dokploy.myserver.com',\n * },\n * },\n * }\n * ```\n */\nexport interface DokployWorkspaceConfig {\n\t/** Dokploy API endpoint for all stages */\n\tendpoint?: string;\n\t/** Per-stage Dokploy API endpoints (overrides endpoint) */\n\tendpoints?: Record<string, string>;\n\t/** Registry ID in Dokploy (auto-configured) */\n\tregistryId?: string;\n\t/** How a release is waited for and checked before it counts as live. */\n\tverify?: DokployVerifyConfig;\n}\n\n/**\n * How a Dokploy release is waited for and checked.\n *\n * A release counts once Dokploy's deployment has finished and the app has\n * answered its health route `healthyAfter` times in a row. One that does not\n * is rolled back.\n */\nexport interface DokployVerifyConfig {\n\t/** How long Dokploy may take to finish a deployment. Default 10 minutes. */\n\tdeploymentTimeoutMs?: number;\n\t/**\n\t * The path each backend is checked on, under its domain. Default\n\t * `/health`, which every production server build serves. A site is\n\t * checked at `/`.\n\t */\n\thealthCheckPath?: string;\n\t/** Consecutive 2xx answers that make an app healthy. Default 3. */\n\thealthyAfter?: number;\n\t/** Between health checks, and the first wait between polls. Default 2s. */\n\tintervalMs?: number;\n\t/** How long an app may take to become healthy. Default 5 minutes. */\n\thealthTimeoutMs?: number;\n}\n\n/**\n * The `compose` target's own settings — `deploy.compose`. Read by `gkm\n * compose` and `gkm deploy --target compose` only; the Dokploy target never\n * consults it.\n *\n * @example\n * ```ts\n * deploy: { compose: { proxy: 'traefik' } }\n * ```\n */\nexport interface ComposeWorkspaceConfig {\n\t/**\n\t * What serves a deployed stage's stack: `'caddy'` (the default) — the\n\t * stack's own Caddy on 80/443 — or `'traefik'`, the server's shared\n\t * Traefik edge, which every stack registers its routes with. One value\n\t * for every deployed stage, or one per stage. The local stage is always\n\t * Caddy.\n\t *\n\t * ```ts\n\t * proxy: 'traefik'\n\t * proxy: { staging: 'traefik', production: 'caddy' }\n\t * ```\n\t */\n\tproxy?: ComposeProxy | Record<string, ComposeProxy>;\n\t/**\n\t * A deployed stage's own certificate, by stage, instead of one from\n\t * Let's Encrypt: a PEM certificate (with its chain) and its key, relative\n\t * to the workspace root or absolute. Either proxy serves it.\n\t *\n\t * ```ts\n\t * tls: { production: { certFile: 'certs/origin.pem', keyFile: 'certs/origin.key' } }\n\t * ```\n\t */\n\ttls?: Record<string, ComposeTlsConfig>;\n}\n\n/** The proxies a compose stack can be served by — `deploy.compose.proxy`. */\nexport type ComposeProxy = 'caddy' | 'traefik';\n\n/** A stage's own certificate — `deploy.compose.tls.<stage>`. */\nexport interface ComposeTlsConfig {\n\tcertFile: string;\n\tkeyFile: string;\n}\n\n/**\n * Where one stage's telemetry goes, and how much of it — `deploy.telemetry`,\n * by stage.\n *\n * What is emitted is the application's (the `Telemetry` construct); where it\n * goes is this. A server target runs the self-hosted provider when a stage\n * names nothing; AWS has none, so a deployed AWS stage that uses telemetry\n * names a provider. `false` sends nothing. The local stage ignores this: `gkm\n * dev` and `gkm compose` on it always run OpenObserve, at 100%.\n *\n * @example\n * ```ts\n * telemetry: {\n * staging: 'self-hosted',\n * production: { provider: 'self-hosted', retentionDays: 14, sampleRate: 0.1 },\n * preview: { provider: 'otlp', endpoint: 'https://otlp.example.com', sampleRate: 0.5 },\n * scratch: false,\n * }\n * ```\n */\nexport type StageTelemetryConfig =\n\t| 'self-hosted'\n\t| SelfHostedTelemetryConfig\n\t| OtlpTelemetryConfig\n\t| false;\n\n/**\n * What backs one deployed stage's buckets — `deploy.objects`, by stage.\n *\n * - `'external'` (the default) — the stage's secrets hold each bucket's URL,\n * set by you: S3, R2, any S3-compatible store.\n * - `{ provider: 's3' }` — `gkm setup --stage <stage>` creates each bucket,\n * its IAM user and key in the stage's AWS account, and writes the bucket's\n * URL into the stage's secrets.\n * - `false` — the stage has no object storage; a bucket on it is refused.\n *\n * The local stage ignores this: `gkm dev` runs MinIO for every bucket.\n *\n * @example\n * ```ts\n * objects: {\n * production: { provider: 's3', region: 'eu-west-1', versioning: true },\n * staging: 'external',\n * }\n * ```\n */\nexport type StageObjectsConfig = 'external' | S3ObjectsConfig | false;\n\n/** `objects: { provider: 's3' }` — a bucket per construct in the stage's account. */\nexport interface S3ObjectsConfig {\n\tprovider: 's3';\n\t/**\n\t * Where the buckets are created. Defaults to the secrets store's region,\n\t * then `AWS_REGION`.\n\t */\n\tregion?: string;\n\t/** Keep previous versions of every object. Defaults to the construct's own `versioned`. */\n\tversioning?: boolean;\n}\n\n/** Shared by every provider. */\ninterface TelemetryProviderConfig {\n\t/**\n\t * The fraction of traces kept, decided where a trace starts and followed\n\t * by every service it reaches (`parentbased_traceidratio`). Logs are not\n\t * sampled. 0 to 1; default 1.\n\t */\n\tsampleRate?: number;\n}\n\n/**\n * The target runs the collector and its UI: OpenObserve, in a compose stack,\n * reached through an SSH tunnel by default.\n */\nexport interface SelfHostedTelemetryConfig extends TelemetryProviderConfig {\n\tprovider: 'self-hosted';\n\t/**\n\t * The port it is published on, on 127.0.0.1 only — reached from another\n\t * machine through an SSH tunnel. Default 5080.\n\t */\n\tport?: number;\n\t/**\n\t * How many days of data it keeps (OpenObserve's\n\t * `ZO_COMPACT_DATA_RETENTION_DAYS`). At least 3; default 30.\n\t */\n\tretentionDays?: number;\n\t/**\n\t * Serve it on `logs.<stage domain>` through the stack's proxy, to these\n\t * addresses only (IPs or CIDRs), instead of on a loopback port. Every\n\t * other address is answered 403.\n\t */\n\tpublic?: { allow: string[] };\n}\n\n/** Any OTLP/HTTP endpoint — a collector, a vendor's ingest. */\nexport interface OtlpTelemetryConfig extends TelemetryProviderConfig {\n\tprovider: 'otlp';\n\t/** The OTLP/HTTP base URL; `/v1/traces` and `/v1/logs` are added to it. */\n\tendpoint: string;\n\t/** Headers sent with every export — `{ 'x-team': 'shop' }`. */\n\theaders?: Record<string, string>;\n}\n\n/**\n * DNS provider types for automatic DNS record creation.\n */\nexport type DnsProviderType = 'hostinger' | 'route53' | 'cloudflare' | 'manual';\n\n/**\n * Deployment configuration for the workspace.\n *\n * @example\n * ```ts\n * // Minimal - just set default target\n * deploy: {\n * default: 'dokploy',\n * }\n *\n * // Full configuration with DNS and backups\n * deploy: {\n * default: 'dokploy',\n * registry: 'ghcr.io/myorg',\n * dokploy: {\n * endpoint: 'https://dokploy.myserver.com',\n * projectId: 'proj_abc123',\n * domains: {\n * production: 'myapp.com',\n * },\n * },\n * dns: {\n * provider: 'hostinger',\n * domain: 'myapp.com',\n * },\n * backups: {\n * type: 's3',\n * region: 'us-east-1',\n * },\n * }\n * ```\n */\nexport interface DeployConfig {\n\t/** Default deploy target for all apps (default: 'dokploy') */\n\tdefault?: DeployTargetName;\n\t/**\n\t * Targets that do not ship with the CLI, by the name `default` (or an\n\t * app's `deploy`) uses: a package whose default export is the target, a\n\t * target object, or either with its options.\n\t *\n\t * ```ts\n\t * targets: {\n\t * acme: '@acme/gkm-target',\n\t * fly: ['@acme/gkm-fly', { org: 'acme' }],\n\t * local: defineTarget({ … }),\n\t * }\n\t * ```\n\t *\n\t * A package is never guessed from a name: it is installed and listed here.\n\t * A built-in's name cannot be taken.\n\t */\n\ttargets?: Record<string, DeployTargetEntry>;\n\t/**\n\t * Whose deploy this is, on a target shared with other workspaces — an\n\t * organisation or a team, lowercase `[a-z0-9-]`. Two workspaces with the\n\t * same name deploying to one Dokploy are kept apart by it: it is in the\n\t * project's ownership marker, the image path\n\t * (`<registry>/<namespace>/<project>-<app>`) and every resource name.\n\t *\n\t * Defaults to the kebab-cased workspace name, which adds nothing to the\n\t * names a workspace was already deployed under.\n\t */\n\tnamespace?: string;\n\t/** Each deployed stage's base domain — see {@link DomainsConfig}. */\n\tdomains?: DomainsConfig;\n\t/**\n\t * The container registry the apps' images are pushed to and pulled from,\n\t * whatever the target — `ghcr.io/myorg`. An image is\n\t * `<registry>/<namespace>/<project>-<app>:<tag>`.\n\t */\n\tregistry?: string;\n\t/** Dokploy-specific configuration */\n\tdokploy?: DokployWorkspaceConfig;\n\t/** What the `compose` target runs beside the apps — see {@link ComposeWorkspaceConfig}. */\n\tcompose?: ComposeWorkspaceConfig;\n\t/**\n\t * Where each deployed stage's telemetry goes — see\n\t * {@link StageTelemetryConfig}.\n\t */\n\ttelemetry?: Record<string, StageTelemetryConfig>;\n\t/**\n\t * What backs each deployed stage's buckets — the `objects` kind that\n\t * `ObjectStorage` and `FileServer` produce. See {@link StageObjectsConfig}.\n\t */\n\tobjects?: Record<string, StageObjectsConfig>;\n\t/** DNS configuration for automatic record creation */\n\tdns?: DnsConfig;\n\t/** Backup destination configuration for database services */\n\tbackups?: BackupsConfig;\n}\n\n/**\n * Models package configuration for shared schemas.\n *\n * Configures a shared models package containing Zod schemas\n * that can be used across backend and frontend apps.\n *\n * @example\n * ```ts\n * shared: {\n * models: {\n * path: 'packages/models',\n * schema: 'zod',\n * },\n * }\n * ```\n */\nexport interface ModelsConfig {\n\t/** Path to models package relative to workspace root (default: 'packages/models') */\n\tpath?: string;\n\t/**\n\t * Schema library to use (default: 'zod').\n\t * Currently only 'zod' is supported.\n\t * Future: any StandardSchema-compatible library\n\t */\n\tschema?: 'zod';\n}\n\n/**\n * Shared packages configuration.\n *\n * Configures shared packages in the monorepo that are\n * used by multiple apps.\n *\n * @example\n * ```ts\n * shared: {\n * packages: ['packages/*', 'libs/*'],\n * models: {\n * path: 'packages/models',\n * schema: 'zod',\n * },\n * }\n * ```\n */\nexport interface SharedConfig {\n\t/** Glob patterns for shared packages (default: ['packages/*']) */\n\tpackages?: string[];\n\t/** Models package configuration */\n\tmodels?: ModelsConfig;\n}\n\n/**\n * The project's stages, named by the project.\n *\n * Stage names were literals scattered through the CLI — `gkm dev` ran as\n * `development` and looked for `dev` secrets first, a deploy script said\n * `production` — so a team that called its stages anything else was\n * working against the tool. They are declared once, here, and read.\n *\n * @example\n * ```ts\n * stages: { local: 'dev', deployed: ['staging', 'prod'], protected: ['prod'] }\n * ```\n */\nexport interface StagesConfig {\n\t/**\n\t * What `gkm dev`, `exec`, `setup` and `test` run as: the stage whose\n\t * secrets are read locally, and whose containers carry no suffix.\n\t *\n\t * Never also a deployed stage. Secrets are stored per stage name, so a\n\t * deployed stage sharing it would share its secrets with every laptop.\n\t */\n\tlocal: string;\n\t/** The stages `gkm deploy --stage` accepts. */\n\tdeployed: string[];\n\t/**\n\t * Deployed stages whose resources outlive the stack that made them —\n\t * retained on removal and protected, e.g. the stage users are on.\n\t */\n\tprotected?: string[];\n}\n\n/**\n * Secrets encryption configuration.\n *\n * Configures how secrets are encrypted for deployment.\n * Secrets are stored encrypted in `.gkm/secrets/{stage}.json`\n * with keys stored separately in `~/.gkm/keys/{namespace}/{project}/{stage}.key`.\n *\n * @example\n * ```ts\n * secrets: {\n * enabled: true,\n * algorithm: 'aes-256-gcm',\n * kdf: 'scrypt',\n * }\n * ```\n */\nexport interface SecretsConfig {\n\t/** Enable encrypted secrets (default: true) */\n\tenabled?: boolean;\n\t/**\n\t * Where deployed stages' secrets live, so a deploy can reach them from\n\t * anywhere: `'file'` (default — the encrypted `.gkm/secrets/<stage>.json`,\n\t * which cannot serve CI while `.gkm/` is gitignored), `{ provider: 'ssm',\n\t * region }` (each stage's secrets in an SSM parameter in its own AWS\n\t * account, up to 8 KB), `{ provider: 'secrets-manager', region, kmsKeyId? }`\n\t * (a Secrets Manager secret in its own account, up to 64 KB), or any object\n\t * implementing `SecretsStore`. The local stage always uses the file.\n\t */\n\tstore?: import('../secrets/store.js').SecretsStoreConfig;\n\t/** Encryption algorithm (default: 'aes-256-gcm') */\n\talgorithm?: string;\n\t/** Key derivation function (default: 'scrypt') */\n\tkdf?: 'scrypt' | 'pbkdf2';\n}\n\n/**\n * Base app configuration properties (shared between input and normalized).\n *\n * @example\n * ```ts\n * // Backend app with gkm routes\n * api: {\n * type: 'backend',\n * path: 'apps/api',\n * port: 3000,\n * routes: './src/endpoints/**\\/*.ts',\n * envParser: './src/config/env',\n * logger: './src/config/logger',\n * }\n *\n * // Backend app with entry point (e.g., Better Auth)\n * auth: {\n * type: 'backend',\n * path: 'apps/auth',\n * port: 3001,\n * entry: './src/index.ts',\n * framework: 'better-auth',\n * requiredEnv: ['DATABASE_URL', 'BETTER_AUTH_SECRET'],\n * }\n *\n * // Frontend app\n * web: {\n * type: 'web',\n * path: 'apps/web',\n * port: 3002,\n * framework: 'nextjs',\n * dependencies: ['api', 'auth'],\n * }\n * ```\n */\ninterface AppConfigBase {\n\t/**\n\t * App type.\n\t * - 'backend': Server-side app (API, auth service, etc.)\n\t * - 'web': Client-side web app (Next.js, Vite, TanStack Start, etc.)\n\t * - 'mobile': Native mobile app (Expo). Built/deployed via its own\n\t * toolchain (e.g. EAS Build) — Docker/Dokploy steps are skipped.\n\t * @default 'backend'\n\t */\n\ttype?: 'backend' | 'web' | 'mobile';\n\n\t/**\n\t * Path to the app relative to workspace root.\n\t * @example 'apps/api', 'apps/web', 'services/auth'\n\t */\n\tpath: string;\n\n\t/**\n\t * Development server port.\n\t * Must be unique across all apps in the workspace.\n\t * @example 3000, 3001, 3002\n\t */\n\tport: number;\n\n\t/**\n\t * Per-app deploy target override.\n\t * Overrides `deploy.default` for this specific app.\n\t * @example 'dokploy', 'vercel'\n\t */\n\tdeploy?: DeployTargetName;\n\n\t// ─────────────────────────────────────────────────────────────────\n\t// Backend-specific (gkm routes mode)\n\t// ─────────────────────────────────────────────────────────────────\n\n\t/**\n\t * Constructs glob pattern — one glob, every kind.\n\t *\n\t * A glob per kind cannot find a resource, because a declared `ObjectStorage`\n\t * has no kind to be listed under. This one is what reconcile reads to derive\n\t * the containers a stage needs, and it replaces the per-kind globs below.\n\t *\n\t * @example './src/**\\/*.ts'\n\t */\n\tconstructs?: Routes;\n\n\t/**\n\t * Routes glob pattern for gkm endpoints.\n\t * @example './src/endpoints/**\\/*.ts'\n\t */\n\troutes?: Routes;\n\n\t/**\n\t * Functions glob pattern for Lambda functions.\n\t * @example './src/functions/**\\/*.ts'\n\t */\n\tfunctions?: Routes;\n\n\t/**\n\t * Crons glob pattern for scheduled tasks.\n\t * @example './src/crons/**\\/*.ts'\n\t */\n\tcrons?: Routes;\n\n\t/**\n\t * Queues glob pattern for point-to-point workers.\n\t * @example './src/queues/**\\/*.ts'\n\t */\n\tqueues?: Routes;\n\n\t/**\n\t * Topics glob pattern for the topics subscribers bind to.\n\t * @example './src/topics/**\\/*.ts'\n\t */\n\ttopics?: Routes;\n\n\t/**\n\t * Subscribers glob pattern for event handlers.\n\t * @example './src/subscribers/**\\/*.ts'\n\t */\n\tsubscribers?: Routes;\n\n\t/**\n\t * Path to environment parser module.\n\t * @example './src/config/env'\n\t */\n\tenvParser?: string;\n\n\t/**\n\t * Path to logger module.\n\t * @example './src/config/logger'\n\t */\n\tlogger?: string;\n\n\t/**\n\t * Server lifecycle hooks.\n\t * @example { beforeSetup: './src/hooks/setup.ts' }\n\t */\n\thooks?: HooksConfig;\n\n\t/**\n\t * Telescope request recording configuration.\n\t * @example true, './src/config/telescope', { enabled: true, path: '/__telescope' }\n\t */\n\ttelescope?: string | boolean | TelescopeConfig;\n\n\t/**\n\t * OpenAPI documentation configuration.\n\t * @example true, { output: './src/openapi.ts' }\n\t */\n\topenapi?: boolean | OpenApiConfig;\n\n\t/**\n\t * Runtime environment.\n\t * @default 'node'\n\t */\n\truntime?: Runtime;\n\n\t/**\n\t * Environment file(s) to load during development.\n\t * @example '.env', ['.env', '.env.local']\n\t */\n\tenv?: string | string[];\n\n\t// ─────────────────────────────────────────────────────────────────\n\t// Entry point mode (non-gkm apps)\n\t// ─────────────────────────────────────────────────────────────────\n\n\t/**\n\t * Entry file path for apps that don't use gkm routes.\n\t *\n\t * When specified, the app is run directly with tsx in development\n\t * and bundled with esbuild for production Docker builds.\n\t *\n\t * Use this for:\n\t * - Better Auth servers\n\t * - Custom Hono/Express apps\n\t * - Any backend that doesn't use gkm's endpoint builder\n\t *\n\t * @example './src/index.ts', './src/server.ts'\n\t */\n\tentry?: string;\n\n\t// ─────────────────────────────────────────────────────────────────\n\t// Frontend-specific\n\t// ─────────────────────────────────────────────────────────────────\n\n\t/**\n\t * Framework for the app.\n\t *\n\t * Backend frameworks: 'hono', 'better-auth', 'express', 'fastify'\n\t * Frontend frameworks: 'nextjs', 'remix', 'vite', 'tanstack-start'\n\t * Mobile frameworks: 'expo'\n\t *\n\t * @example 'nextjs', 'better-auth', 'hono', 'expo'\n\t */\n\tframework?: Framework;\n\n\t/**\n\t * Config file paths for frontend environment sniffing.\n\t *\n\t * Points to file(s) that call EnvironmentParser.parse() at import time.\n\t * The sniffer imports these files and captures all env vars accessed.\n\t *\n\t * Dependencies are auto-generated as public-prefixed URL vars based on\n\t * the app's framework (e.g. `NEXT_PUBLIC_{DEP}_URL` for Next.js,\n\t * `VITE_{DEP}_URL` for Vite/TanStack Start, `EXPO_PUBLIC_{DEP}_URL`\n\t * for Expo).\n\t *\n\t * @example\n\t * ```ts\n\t * config: {\n\t * client: './src/config/client.ts', // public-prefixed vars for browser\n\t * server: './src/config/server.ts', // server-only vars for SSR\n\t * }\n\t * ```\n\t */\n\tconfig?: {\n\t\t/** Client-side config (public-prefixed vars, available in browser/device) */\n\t\tclient?: string;\n\t\t/** Server-side config (all env vars, for SSR/API routes) */\n\t\tserver?: string;\n\t};\n\n\t// ─────────────────────────────────────────────────────────────────\n\t// Deployment\n\t// ─────────────────────────────────────────────────────────────────\n\n\t/**\n\t * Override domain for this app.\n\t *\n\t * By default, apps get `{appName}.{baseDomain}` (or just `{baseDomain}`\n\t * for the main frontend). Use this to specify a custom domain.\n\t *\n\t * @example\n\t * ```ts\n\t * // Single domain for all stages\n\t * domain: 'api.custom.com'\n\t *\n\t * // Stage-specific domains\n\t * domain: {\n\t * production: 'api.custom.com',\n\t * staging: 'api.staging.custom.com',\n\t * }\n\t * ```\n\t */\n\tdomain?: AppDomainConfig;\n\n\t/**\n\t * Required environment variables for entry-based apps.\n\t *\n\t * Use this instead of envParser for apps that don't use gkm routes.\n\t * The deploy command uses this list to filter which secrets to embed\n\t * in the Docker image.\n\t *\n\t * @example ['DATABASE_URL', 'BETTER_AUTH_SECRET', 'REDIS_URL']\n\t */\n\trequiredEnv?: string[];\n}\n\n/**\n * App configuration input with type-safe dependencies.\n *\n * @template TAppNames - Union of valid app names in the workspace (auto-inferred)\n *\n * @example\n * ```ts\n * // Dependencies are type-checked against app names\n * apps: {\n * api: { path: 'apps/api', port: 3000 },\n * auth: { path: 'apps/auth', port: 3001 },\n * web: {\n * path: 'apps/web',\n * port: 3002,\n * type: 'web',\n * dependencies: ['api', 'auth'], // ✓ Valid\n * // dependencies: ['invalid'], // ✗ Type error\n * },\n * }\n * ```\n */\nexport interface AppConfigInput<TAppNames extends string = string>\n\textends AppConfigBase {\n\t/** Dependencies on other apps in the workspace (type-safe) */\n\tdependencies?: TAppNames[];\n}\n\n/**\n * App configuration (legacy, for backwards compatibility).\n * @deprecated Use AppConfigInput for new code\n */\nexport interface AppConfig extends AppConfigBase {\n\t/** Dependencies on other apps in the workspace */\n\tdependencies?: string[];\n}\n\n/**\n * Base app input type for type inference.\n */\nexport type AppInput = AppConfigBase & {\n\tdependencies?: readonly string[];\n};\n\n/**\n * Apps record type for workspace configuration.\n */\nexport type AppsRecord = Record<string, AppInput>;\n\n/**\n * Constrain apps so dependencies only reference valid app names.\n * Dependencies must be an array of valid app names from the workspace.\n */\nexport type ConstrainedApps<TApps extends AppsRecord> = {\n\t[K in keyof TApps]: Omit<TApps[K], 'dependencies'> & {\n\t\tdependencies?: readonly (keyof TApps & string)[];\n\t};\n};\n\n/**\n * Full workspace input type with constrained dependencies.\n *\n * @example\n * ```ts\n * import { defineWorkspace } from '@geekmidas/cli';\n *\n * export default defineWorkspace({\n * name: 'my-app',\n * apps: {\n * api: {\n * path: 'apps/api',\n * port: 3000,\n * routes: './src/endpoints/**\\/*.ts',\n * },\n * web: {\n * type: 'web',\n * path: 'apps/web',\n * port: 3001,\n * framework: 'nextjs',\n * dependencies: ['api'],\n * },\n * },\n * deploy: {\n * default: 'dokploy',\n * },\n * });\n * ```\n */\nexport type WorkspaceInput<TApps extends AppsRecord> = {\n\t/**\n\t * Where the workspace's own constructs live, relative to its root.\n\t *\n\t * The product's infrastructure, declared once for every app that consumes\n\t * it. Additive with an app's own glob rather than replacing it.\n\t */\n\tconstructs?: Routes;\n\t/** Workspace name (defaults to root package.json name) */\n\tname?: string;\n\t/**\n\t * Apps that nothing declares.\n\t *\n\t * Normally absent: a `site` is an app and so is a `rest-api` that named\n\t * one, so the list is read off the graph. This is the escape hatch for what\n\t * no construct describes.\n\t */\n\tapps?: ConstrainedApps<TApps>;\n\t/** Shared packages configuration */\n\tshared?: SharedConfig;\n\t/** Deployment configuration */\n\tdeploy?: DeployConfig;\n\t/** The project's stages: which one is local, which deploy */\n\tstages: StagesConfig;\n\t/** Encrypted secrets configuration */\n\tsecrets?: SecretsConfig;\n\t/** State provider configuration (local filesystem by default, or SSM for team collaboration) */\n\tstate?: StateConfig;\n\t/** How `gkm dev` is reached: the discovery endpoint's port and origins */\n\tdev?: DevConfig;\n};\n\n/**\n * How `gkm dev` is reached from outside the apps it runs.\n */\nexport interface DevConfig {\n\t/**\n\t * Browser origins allowed to read the discovery endpoint — and this\n\t * workspace in it — e.g. `['https://console.example.com']`.\n\t *\n\t * Empty by default: any page the developer visits can send requests to a\n\t * loopback port, so none is answered unless it is listed here. A request\n\t * with no `Origin` (curl, a local process) still needs the session token.\n\t */\n\tallowedOrigins?: string[];\n\t/**\n\t * The loopback port the discovery endpoint listens on. Defaults to 4983;\n\t * `GKM_DISCOVERY_PORT` overrides it.\n\t */\n\tdiscoveryPort?: number;\n}\n\n/**\n * How `gkm test` builds what a feature test is handed.\n */\nexport interface TestConfig {\n\t/**\n\t * The folder of test factories, relative to the root.\n\t *\n\t * Defaults to `test/factories`. A factory belongs to a database, not to an\n\t * app, so each file is named after one — `database.ts` for `Database`,\n\t * `auth-database.ts` for `AuthDatabase` — and exports `createFactory(db)`.\n\t * Every app's harness builds them on each test's transactions, and a test is\n\t * handed them as `factories`, keyed by service name.\n\t */\n\tfactories?: string;\n}\n\n/**\n * Extract app names from apps record.\n */\nexport type InferAppNames<TApps extends AppsRecord> = keyof TApps & string;\n\n/**\n * Inferred workspace config with proper app name types.\n */\nexport type InferredWorkspaceConfig<TApps extends AppsRecord> = {\n\t/**\n\t * Where the workspace's own constructs live, relative to its root.\n\t *\n\t * The product's infrastructure, declared once for every app that consumes\n\t * it. Additive with an app's own glob rather than replacing it.\n\t */\n\tconstructs?: Routes;\n\tname?: string;\n\tapps?: {\n\t\t[K in keyof TApps]: Omit<TApps[K], 'dependencies'> & {\n\t\t\tdependencies?: InferAppNames<TApps>[];\n\t\t};\n\t};\n\tshared?: SharedConfig;\n\tdeploy?: DeployConfig;\n\tstages: StagesConfig;\n\tsecrets?: SecretsConfig;\n\tstate?: StateConfig;\n\ttest?: TestConfig;\n\tdev?: DevConfig;\n};\n\n// Legacy types for backwards compatibility\n/** @deprecated Use WorkspaceInput */\nexport type RawWorkspaceInput = {\n\tname?: string;\n\tapps: AppsRecord;\n\tshared?: SharedConfig;\n\tdeploy?: DeployConfig;\n\tsecrets?: SecretsConfig;\n\tstate?: StateConfig;\n};\n\n/** @deprecated Use WorkspaceInput */\nexport type WorkspaceConfigInput<\n\tT extends RawWorkspaceInput = RawWorkspaceInput,\n> = WorkspaceInput<T['apps']>;\n\n/**\n * Workspace configuration for multi-app monorepos.\n *\n * Use `defineWorkspace()` helper for type-safe configuration with\n * auto-completion and dependency validation.\n *\n * @example\n * ```ts\n * // gkm.config.ts\n * import { defineWorkspace } from '@geekmidas/cli';\n *\n * export default defineWorkspace({\n * name: 'my-saas',\n *\n * // App definitions\n * apps: {\n * // Backend API with gkm routes\n * api: {\n * path: 'apps/api',\n * port: 3000,\n * routes: './src/endpoints/**\\/*.ts',\n * envParser: './src/config/env',\n * logger: './src/config/logger',\n * telescope: true,\n * },\n *\n * // Better Auth service\n * auth: {\n * path: 'apps/auth',\n * port: 3001,\n * entry: './src/index.ts',\n * framework: 'better-auth',\n * requiredEnv: ['DATABASE_URL', 'BETTER_AUTH_SECRET'],\n * },\n *\n * // Next.js frontend\n * web: {\n * type: 'web',\n * path: 'apps/web',\n * port: 3002,\n * framework: 'nextjs',\n * dependencies: ['api', 'auth'],\n * },\n * },\n *\n * // Deployment configuration\n * deploy: {\n * default: 'dokploy',\n * registry: 'ghcr.io/myorg',\n * domains: {\n * production: 'myapp.com',\n * staging: 'staging.myapp.com',\n * },\n * dokploy: {\n * endpoint: 'https://dokploy.myserver.com',\n * projectId: 'proj_abc123',\n * },\n * },\n *\n * // Shared packages\n * shared: {\n * packages: ['packages/*'],\n * models: { path: 'packages/models' },\n * },\n * });\n * ```\n *\n * @deprecated Use WorkspaceInput with defineWorkspace for type inference\n */\nexport interface WorkspaceConfig {\n\t/**\n\t * Where the workspace's own constructs live, relative to its root.\n\t *\n\t * The product's infrastructure, declared once for every app that consumes\n\t * it. Additive with an app's own glob rather than replacing it.\n\t */\n\tconstructs?: Routes;\n\t/** Workspace name (defaults to root package.json name) */\n\tname?: string;\n\n\t/** Apps that nothing declares. Normally absent — see `WorkspaceInput`. */\n\tapps?: Record<string, AppConfig>;\n\n\t/** Shared packages configuration */\n\tshared?: SharedConfig;\n\n\t/** Default deployment configuration */\n\tdeploy?: DeployConfig;\n\n\t/** The project's stages: which one is local, which deploy */\n\tstages: StagesConfig;\n\n\t/** Encrypted secrets configuration */\n\tsecrets?: SecretsConfig;\n\n\t/** State provider configuration (local filesystem by default, or SSM for team collaboration) */\n\tstate?: StateConfig;\n\n\t/** What `gkm test` hands a feature test: where the factories are. */\n\ttest?: TestConfig;\n\n\t/** How `gkm dev` is reached: the discovery endpoint's port and origins. */\n\tdev?: DevConfig;\n}\n\n/**\n * Normalized app configuration with resolved defaults.\n *\n * This is the internal representation after processing user input.\n * All optional fields have been resolved to their defaults.\n */\nexport interface NormalizedAppConfig extends Omit<AppConfigBase, 'type'> {\n\t/**\n\t * Whether the base domain points at this site.\n\t *\n\t * Carried from the `site` declaration rather than configured, because which\n\t * site is primary is structural — its *hostname* is what varies by stage,\n\t * and that is `domain`.\n\t */\n\troot?: boolean;\n\n\t/**\n\t * The label this app answers on under a stage's domain, carried from its\n\t * declaration — `api` for `api.myapp.com`. Absent, the app's own name.\n\t */\n\tsubdomain?: string;\n\n\t/** App type (always defined after normalization) */\n\ttype: 'backend' | 'web' | 'mobile';\n\t/** Path to the app */\n\tpath: string;\n\t/** Development server port */\n\tport: number;\n\t/** Resolved dependencies array (empty array if none) */\n\tdependencies: string[];\n\t/** Resolved deploy target (app.deploy > deploy.default > 'dokploy') */\n\tresolvedDeployTarget: DeployTargetName;\n\t/** Entry file path for non-gkm apps */\n\tentry?: string;\n\t/** Framework for the app */\n\tframework?: Framework;\n\t/** Override domain for this app */\n\tdomain?: AppDomainConfig;\n\t/** Required environment variables for entry-based apps */\n\trequiredEnv?: string[];\n}\n\n/**\n * Normalized workspace configuration with resolved defaults.\n *\n * This is the internal representation after processing user input.\n * All optional fields have been resolved to their defaults.\n */\nexport interface NormalizedWorkspace {\n\t/** Workspace name (resolved from package.json if not specified) */\n\tname: string;\n\t/** Absolute path to workspace root */\n\troot: string;\n\t/**\n\t * The workspace's own constructs glob, relative to `root`.\n\t *\n\t * Where the product's shared infrastructure is declared. Additive with each\n\t * app's glob, so an app can still declare something only it uses.\n\t */\n\tconstructs?: Routes;\n\t/** Normalized app configurations */\n\tapps: Record<string, NormalizedAppConfig>;\n\t/** Deploy configuration (empty object if not specified) */\n\tdeploy: DeployConfig;\n\t/** Shared packages configuration (empty object if not specified) */\n\tshared: SharedConfig;\n\t/** The project's stages, as declared */\n\tstages: StagesConfig;\n\t/** Secrets configuration (empty object if not specified) */\n\tsecrets: SecretsConfig;\n\t/** State provider configuration (undefined = local filesystem) */\n\tstate?: StateConfig;\n\t/** What `gkm test` hands a feature test (empty object if not specified) */\n\ttest: TestConfig;\n\t/** How `gkm dev` is reached, when configured */\n\tdev?: DevConfig;\n}\n\n/**\n * Result of loading and processing a configuration.\n */\nexport interface LoadedConfig {\n\t/** Whether this is a single-app or workspace config */\n\ttype: 'single' | 'workspace';\n\t/** The raw configuration as loaded */\n\traw: GkmConfig | WorkspaceConfig;\n\t/** Normalized workspace (always available) */\n\tworkspace: NormalizedWorkspace;\n\t/**\n\t * What the workspace's constructs declared, when it has any.\n\t *\n\t * Read once at load, because the apps above are derived from it and a\n\t * caller that re-discovers would be answering the same question twice with\n\t * two chances to disagree. Absent when the workspace declares no constructs.\n\t */\n\tmanifest?: ConstructManifest;\n\t/**\n\t * The files each Worker's crons, queues and subscribers are declared in,\n\t * read in the same discovery — what says which app a worker is built from.\n\t */\n\tbackground?: Record<string, string[]>;\n}\n\n/**\n * Type guard to check if a config is a WorkspaceConfig.\n *\n * @example\n * ```ts\n * const config = await loadConfig();\n * if (isWorkspaceConfig(config)) {\n * // config.apps is available\n * console.log(Object.keys(config.apps));\n * }\n * ```\n */\nexport function isWorkspaceConfig(\n\tconfig: GkmConfig | WorkspaceConfig,\n): config is WorkspaceConfig {\n\tif (typeof config !== 'object' || config === null) return false;\n\n\t// `apps` used to be the only tell, and it stopped being one when apps\n\t// became derived: a workspace that declares its apps through constructs has\n\t// none. `constructs` at the top level is the other tell — a single-app\n\t// `defineConfig` has no notion of a workspace-wide glob — and either alone\n\t// is enough.\n\tif ('apps' in config && typeof config.apps === 'object') return true;\n\n\t// Only when nothing else says it is one app. `openapi`, `telescope`,\n\t// `hooks` and the rest configure a single process, so a config that\n\t// sets any of them is that process's — the workspace schema is strict,\n\t// and reading one of these as a workspace would reject what it was never\n\t// meant to accept.\n\treturn (\n\t\t'constructs' in config &&\n\t\tObject.keys(config).every((key) => WORKSPACE_KEYS.has(key))\n\t);\n}\n\n/** Every top-level key a workspace config has — see `WorkspaceConfigSchema`. */\nconst WORKSPACE_KEYS: ReadonlySet<string> = new Set([\n\t'name',\n\t'constructs',\n\t'apps',\n\t'shared',\n\t'deploy',\n\t'stages',\n\t'secrets',\n\t'state',\n]);\n","import type { Framework } from './types.js';\n\n/**\n * All public env-var prefixes the toolbox understands.\n *\n * Used when resolving incoming var names back to a dependency\n * (e.g. `VITE_AUTH_URL` → `auth`). A var matching any of these\n * is considered safe to inline into a client bundle.\n */\nexport const PUBLIC_ENV_PREFIXES = [\n\t'NEXT_PUBLIC_',\n\t'VITE_',\n\t'EXPO_PUBLIC_',\n] as const;\n\n/**\n * Resolve the public env-var prefix that a frontend framework's bundler\n * inlines into client code at build time:\n *\n * - `nextjs` → `NEXT_PUBLIC_`\n * - `vite` → `VITE_`\n * - `tanstack-start` → `VITE_` (uses Vite under the hood)\n * - `expo` → `EXPO_PUBLIC_`\n * - `remix` → `''` (Remix exposes via loaders, no prefix convention)\n *\n * For backend frameworks or unspecified, falls back to `NEXT_PUBLIC_` to\n * preserve the historical default. Backend apps never read these vars\n * from a bundle, so the prefix only affects scaffolding/symmetry.\n */\nexport function getPublicEnvPrefix(framework?: Framework): string {\n\tswitch (framework) {\n\t\tcase 'vite':\n\t\tcase 'tanstack-start':\n\t\t\treturn 'VITE_';\n\t\tcase 'expo':\n\t\t\treturn 'EXPO_PUBLIC_';\n\t\tcase 'remix':\n\t\t\treturn '';\n\t\tcase 'nextjs':\n\t\t\treturn 'NEXT_PUBLIC_';\n\t\tdefault:\n\t\t\treturn 'NEXT_PUBLIC_';\n\t}\n}\n\n/**\n * Strip a known public prefix from a var name.\n * Returns the un-prefixed name, or `null` if no prefix matched.\n */\nexport function stripPublicPrefix(name: string): string | null {\n\tfor (const prefix of PUBLIC_ENV_PREFIXES) {\n\t\tif (prefix && name.startsWith(prefix)) {\n\t\t\treturn name.slice(prefix.length);\n\t\t}\n\t}\n\treturn null;\n}\n","import { basename, isAbsolute, join } from 'node:path';\nimport { assertKnownSecretsStore } from '../secrets/providers.js';\nimport type { GkmConfig, Routes } from '../types.js';\nimport {\n\tformatValidationErrors,\n\tsafeValidateWorkspaceConfig,\n} from './schema.js';\nimport { InvalidStages, stageProblems, validateStages } from './stages.js';\nimport type {\n\tAppConfig,\n\tAppsRecord,\n\tDeployTargetName,\n\tInferredWorkspaceConfig,\n\tLoadedConfig,\n\tNormalizedAppConfig,\n\tNormalizedWorkspace,\n\tWorkspaceConfig,\n\tWorkspaceInput,\n} from './types.js';\nimport { isWorkspaceConfig } from './types.js';\n\nexport {\n\tgetPublicEnvPrefix,\n\tPUBLIC_ENV_PREFIXES,\n\tstripPublicPrefix,\n} from './publicEnv.js';\nexport {\n\tformatValidationErrors,\n\tgetDeployTargetError,\n\tisDeployTargetSupported,\n\tisPhase2DeployTarget,\n\tPHASE_2_DEPLOY_TARGETS,\n\tSUPPORTED_DEPLOY_TARGETS,\n\tsafeValidateWorkspaceConfig,\n\tvalidateWorkspaceConfig,\n\tWorkspaceConfigSchema,\n} from './schema.js';\n// Re-export types\nexport type {\n\tAppConfig,\n\tAppConfigInput,\n\tAppInput,\n\tAppsRecord,\n\tBackendFramework,\n\tComposeWorkspaceConfig,\n\tConstrainedApps,\n\tDeployConfig,\n\tDeployTargetName,\n\tDokployWorkspaceConfig,\n\tFrontendFramework,\n\tInferAppNames,\n\tInferredWorkspaceConfig,\n\tLoadedConfig,\n\tMobileFramework,\n\tModelsConfig,\n\tNormalizedAppConfig,\n\tNormalizedWorkspace,\n\tOtlpTelemetryConfig,\n\tSecretsConfig,\n\tSelfHostedTelemetryConfig,\n\tSharedConfig,\n\tStageTelemetryConfig,\n\tWorkspaceConfig,\n\tWorkspaceInput,\n} from './types.js';\nexport { isWorkspaceConfig } from './types.js';\n\n/**\n * Validate that all dependencies reference existing apps.\n * Returns the config if valid, throws otherwise.\n */\nfunction validateDependencies<TApps extends AppsRecord>(apps: TApps): void {\n\tconst appNames = new Set(Object.keys(apps));\n\n\tfor (const [appName, app] of Object.entries(apps)) {\n\t\tfor (const dep of app.dependencies ?? []) {\n\t\t\tif (!appNames.has(dep)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Invalid dependency: App \"${appName}\" depends on \"${dep}\" which does not exist. ` +\n\t\t\t\t\t\t`Valid apps are: ${[...appNames].join(', ')}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (dep === appName) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Invalid dependency: App \"${appName}\" cannot depend on itself.`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Define workspace configuration with full TypeScript support and type inference.\n *\n * Uses `const` type parameter to infer literal app names, providing:\n * - Autocomplete for app names in dependencies\n * - Type errors for invalid dependency references\n * - Full type inference for the returned config\n *\n * @example\n * ```ts\n * // gkm.config.ts\n * import { defineWorkspace } from '@geekmidas/cli';\n *\n * export default defineWorkspace({\n * name: 'my-saas',\n * apps: {\n * api: {\n * type: 'backend',\n * path: 'apps/api',\n * port: 3000,\n * routes: './src/endpoints/**\\/*.ts',\n * envParser: './src/config/env',\n * logger: './src/logger',\n * },\n * web: {\n * type: 'web',\n * framework: 'nextjs',\n * path: 'apps/web',\n * port: 3001,\n * dependencies: ['api'], // <- autocomplete shows 'api' | 'web'\n * },\n * },\n * });\n *\n * // config.apps.api <- full type inference\n * // config.apps.foo <- TypeScript error\n * ```\n */\nexport function defineWorkspace<const TApps extends AppsRecord>(\n\tconfig: WorkspaceInput<TApps>,\n): InferredWorkspaceConfig<TApps> {\n\tassertNoMovedDeployKeys(config.deploy);\n\tassertKnownSecretsStore(config.secrets?.store);\n\n\t// Validate dependencies at runtime\n\tif (config.apps) validateDependencies(config.apps as unknown as TApps);\n\n\t// Validate with Zod schema\n\tconst result = safeValidateWorkspaceConfig(config);\n\tif (!result.success && result.error) {\n\t\tthrow new Error(formatValidationErrors(result.error));\n\t}\n\n\treturn config as unknown as InferredWorkspaceConfig<TApps>;\n}\n\n/**\n * Get the package name from package.json in the given directory.\n */\nfunction getPackageName(cwd: string): string | undefined {\n\ttry {\n\t\t// Dynamic import would be async, so we use require for sync operation\n\t\t// eslint-disable-next-line @typescript-eslint/no-require-imports\n\t\tconst pkg = require(`${cwd}/package.json`);\n\t\treturn pkg.name?.replace(/^@[^/]+\\//, ''); // Remove scope\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * Normalize a workspace configuration with resolved defaults.\n */\nexport function normalizeWorkspace(\n\tconfig: WorkspaceConfig,\n\tcwd: string,\n): NormalizedWorkspace {\n\tconst name = config.name ?? getPackageName(cwd) ?? basename(cwd);\n\tconst defaultTarget = config.deploy?.default ?? 'dokploy';\n\n\tconst normalizedApps: Record<string, NormalizedAppConfig> = {};\n\n\tfor (const [appName, app] of Object.entries(config.apps ?? {})) {\n\t\tnormalizedApps[appName] = normalizeAppConfig(app, defaultTarget);\n\t}\n\n\treturn {\n\t\tname,\n\t\troot: cwd,\n\t\t...(config.constructs ? { constructs: config.constructs } : {}),\n\t\tapps: normalizedApps,\n\t\tdeploy: config.deploy ?? { default: 'dokploy' },\n\t\tshared: config.shared ?? { packages: ['packages/*'] },\n\t\tstages: validateStages(config.stages),\n\t\tsecrets: config.secrets ?? {},\n\t\tstate: config.state,\n\t\ttest: config.test ?? {},\n\t\t...(config.dev ? { dev: config.dev } : {}),\n\t};\n}\n\n/**\n * Normalize an app configuration with resolved defaults.\n * @param app - App configuration\n * @param defaultTarget - Default deploy target from workspace config\n */\nfunction normalizeAppConfig(\n\tapp: AppConfig,\n\tdefaultTarget: DeployTargetName,\n): NormalizedAppConfig {\n\treturn {\n\t\t...app,\n\t\ttype: app.type ?? 'backend',\n\t\tport: app.port,\n\t\tpath: app.path,\n\t\tdependencies: app.dependencies ?? [],\n\t\tresolvedDeployTarget: app.deploy ?? defaultTarget,\n\t};\n}\n\n/**\n * Wrap a single-app GkmConfig as a workspace.\n * This allows existing single-app configs to work seamlessly.\n */\nexport function wrapSingleAppAsWorkspace(\n\tconfig: GkmConfig,\n\tcwd: string,\n): NormalizedWorkspace {\n\t// `config.name` first, the way `defineWorkspace` already reads it. It was\n\t// only honoured on the workspace path, so the same field decided the scope\n\t// in a monorepo and was ignored in a single-app project.\n\tconst name = config.name ?? getPackageName(cwd) ?? basename(cwd);\n\n\t// A projection, not a default-filled stand-in.\n\t//\n\t// Every hardcoded value here was a field the config could already state and\n\t// this quietly dropped: the port it serves on, and the target it deploys to.\n\t// `resolvedDeployTarget` was the worse of the two — a project deploying to\n\t// Vercel was normalised into one that deploys to Dokploy, and nothing said\n\t// so.\n\tconst app: NormalizedAppConfig = {\n\t\ttype: 'backend',\n\t\tpath: '.',\n\t\tport: 3000,\n\t\tdependencies: [],\n\t\tresolvedDeployTarget: config.deploy?.default ?? 'dokploy',\n\t\tconstructs: config.constructs,\n\t\thooks: config.hooks,\n\t\ttelescope: config.telescope,\n\t\topenapi: config.openapi,\n\t\truntime: config.runtime,\n\t\tenv: config.env,\n\t};\n\n\treturn {\n\t\tname,\n\t\troot: cwd,\n\t\t// Keyed `api` because that is what a single-app backend is, and because\n\t\t// the key is what names the application: `applicationName(stage, name,\n\t\t// 'api')` is `production-<name>-api`, beside the\n\t\t// `production-<name>-database` its constructs get. One model, so the\n\t\t// deploy asks the workspace what its apps are called rather than asking\n\t\t// the filesystem.\n\t\tapps: { api: app },\n\t\t// Carried rather than replaced. A single-app project configures its\n\t\t// deploy in the same shape a workspace does — endpoint, registry,\n\t\t// domains — and hardcoding the default here silently discarded all of it,\n\t\t// which surfaced as `resolveHost` refusing to name a host for a stage the\n\t\t// config had no way to describe.\n\t\tdeploy: { default: 'dokploy', ...config.deploy },\n\t\tshared: { packages: [] },\n\t\tstages: validateStages(config.stages),\n\t\tsecrets: config.secrets ?? {},\n\t\ttest: config.test ?? {},\n\t\t...(config.dev ? { dev: config.dev } : {}),\n\t};\n}\n\n/**\n * Process a loaded configuration (either single-app or workspace).\n * Returns a normalized workspace in both cases.\n */\n/**\n * `deploy.dokploy.registry`, which became `deploy.registry`: every target\n * pushes and pulls through the one registry, so it is not Dokploy's to hold.\n *\n * Refused by name rather than dropped: the schema strips keys it does not\n * know, and a registry silently gone would deploy images under no registry.\n */\nexport class DokployRegistryMoved extends Error {\n\tconstructor(readonly registry: unknown) {\n\t\tsuper(\n\t\t\t`deploy.dokploy.registry is now deploy.registry, read by every deploy target. ` +\n\t\t\t\t`In gkm.config.ts, move it up one level: deploy: { registry: ${JSON.stringify(registry)}, dokploy: { … } }.`,\n\t\t);\n\t\tthis.name = 'DokployRegistryMoved';\n\t}\n}\n\n/** Refuse a deploy key that has moved, naming where it lives now. */\nexport function assertNoMovedDeployKeys(deploy: unknown): void {\n\tconst dokploy = (deploy as { dokploy?: unknown } | undefined)?.dokploy;\n\tif (dokploy && typeof dokploy === 'object' && 'registry' in dokploy) {\n\t\tthrow new DokployRegistryMoved((dokploy as { registry: unknown }).registry);\n\t}\n}\n\nexport function processConfig(\n\tconfig: GkmConfig | WorkspaceConfig,\n\tcwd: string,\n): LoadedConfig {\n\tassertNoMovedDeployKeys(config.deploy);\n\n\tif (isWorkspaceConfig(config)) {\n\t\tassertKnownSecretsStore(config.secrets?.store);\n\t\t// Every command needs the stages, so a config without them is refused\n\t\t// by the error that says so and shows the shape, not as one schema\n\t\t// issue among others.\n\t\tif (config.stages === undefined) {\n\t\t\tthrow new InvalidStages(stageProblems(undefined));\n\t\t}\n\t\t// Validate workspace config\n\t\tconst result = safeValidateWorkspaceConfig(config);\n\t\tif (!result.success && result.error) {\n\t\t\tthrow new InvalidWorkspaceConfig(formatValidationErrors(result.error));\n\t\t}\n\n\t\treturn {\n\t\t\ttype: 'workspace',\n\t\t\traw: config,\n\t\t\tworkspace: normalizeWorkspace(config, cwd),\n\t\t};\n\t}\n\n\t// Single-app config - wrap as workspace\n\treturn {\n\t\ttype: 'single',\n\t\traw: config,\n\t\tworkspace: wrapSingleAppAsWorkspace(config, cwd),\n\t};\n}\n\n/** A workspace config the schema refuses; the message lists every issue. */\nexport class InvalidWorkspaceConfig extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = 'InvalidWorkspaceConfig';\n\t}\n}\n\n/**\n * The workspace's own constructs globs, absolute.\n *\n * Infrastructure two apps share is a fact about the product, and declaring it\n * inside one of them makes moving that app a change to the other's database.\n */\nexport function workspaceConstructGlobs(\n\tworkspace: NormalizedWorkspace,\n): string[] {\n\treturn constructPatterns(workspace.constructs).map((pattern) =>\n\t\tisAbsolute(pattern) ? pattern : join(workspace.root, pattern),\n\t);\n}\n\n/**\n * Every constructs glob an app can see, absolute.\n *\n * The workspace's own first, then the app's. Absolute because the app builds\n * from its own directory while the workspace's constructs live above it —\n * a relative glob resolved from the app would look for the product's\n * infrastructure inside one of its consumers and find nothing.\n *\n * Additive, not overriding: an app that declares its own constructs still sees\n * the ones the workspace declared for everybody.\n */\nexport function appConstructGlobs(\n\tworkspace: NormalizedWorkspace,\n\tappName: string,\n): string[] {\n\tconst globs: string[] = workspaceConstructGlobs(workspace);\n\n\tconst app = workspace.apps[appName];\n\tif (app) {\n\t\tconst root = isAbsolute(app.path)\n\t\t\t? app.path\n\t\t\t: join(workspace.root, app.path);\n\t\tfor (const pattern of constructPatterns(app.constructs)) {\n\t\t\tglobs.push(isAbsolute(pattern) ? pattern : join(root, pattern));\n\t\t}\n\t}\n\n\treturn globs;\n}\n/**\n * Every constructs glob in the workspace: its own, plus each app's.\n *\n * One list, so what a deploy provisions and what an app's entry point\n * registers drivers for are read off the same globs.\n */\nexport function allConstructGlobs(workspace: NormalizedWorkspace): string[] {\n\tconst globs = [\n\t\t...workspaceConstructGlobs(workspace),\n\t\t...Object.keys(workspace.apps).flatMap((appName) =>\n\t\t\tappConstructGlobs(workspace, appName),\n\t\t),\n\t];\n\n\t// Every app's list starts with the workspace's, so they repeat.\n\treturn [...new Set(globs)];\n}\n\n/** A `Routes` in any of its accepted shapes, as a flat list of patterns. */\nexport function constructPatterns(routes: Routes | undefined): string[] {\n\tif (!routes) return [];\n\treturn typeof routes === 'string' ? [routes] : routes;\n}\n\n/**\n * Get the GkmConfig for a specific app in a workspace.\n * Useful for running existing single-app commands on a specific app.\n */\nexport function getAppGkmConfig(\n\tworkspace: NormalizedWorkspace,\n\tappName: string,\n): GkmConfig | undefined {\n\tconst app = workspace.apps[appName];\n\tif (!app || app.type !== 'backend') {\n\t\treturn undefined;\n\t}\n\n\treturn {\n\t\t// Workspace-level, and carried here deliberately. The deploy target is\n\t\t// what decides the backend a cache resolves to, which the *entry point*\n\t\t// reads when it decides which drivers to register — and the local target\n\t\t// reads from the workspace when it composes the URLs those drivers\n\t\t// receive. The two have to answer from the same target, or a project on\n\t\t// a server is handed a `postgres://` cache URL by an entry that\n\t\t// registered only the Upstash driver (`UnregisteredCacheScheme`).\n\t\t...(workspace.deploy ? { deploy: workspace.deploy } : {}),\n\t\tstages: workspace.stages,\n\t\t// One glob, every kind, and the root's covers every app's code as well\n\t\t// as the constructs. Which surface an endpoint belongs to is the\n\t\t// endpoint's to say, not its file's, so the build sorts them there.\n\t\tconstructs: appConstructGlobs(workspace, appName),\n\t\thooks: app.hooks,\n\t\ttelescope: app.telescope,\n\t\topenapi: app.openapi,\n\t\truntime: app.runtime,\n\t\tenv: app.env,\n\t};\n}\n\n/**\n * Get topologically sorted app names based on dependencies.\n * Apps with no dependencies come first, then apps that depend on them.\n */\nexport function getAppBuildOrder(workspace: NormalizedWorkspace): string[] {\n\tconst appNames = Object.keys(workspace.apps);\n\tconst visited = new Set<string>();\n\tconst result: string[] = [];\n\n\tfunction visit(name: string) {\n\t\tif (visited.has(name)) return;\n\t\tvisited.add(name);\n\n\t\tconst app = workspace.apps[name];\n\t\tif (app) {\n\t\t\tfor (const dep of app.dependencies) {\n\t\t\t\tvisit(dep);\n\t\t\t}\n\t\t}\n\n\t\tresult.push(name);\n\t}\n\n\tfor (const name of appNames) {\n\t\tvisit(name);\n\t}\n\n\treturn result;\n}\n\n/**\n * Resolve the Dokploy endpoint for a specific stage.\n *\n * Uses per-stage endpoint from `endpoints` if available,\n * otherwise falls back to the global `endpoint`.\n *\n * @param dokployConfig - Dokploy workspace configuration\n * @param stage - Deployment stage (e.g., 'development', 'production')\n * @returns The endpoint URL for the stage, or undefined if not configured\n *\n * @example\n * ```ts\n * // With per-stage endpoints\n * const config = {\n * endpoints: {\n * development: 'https://dev.dokploy.example.com:3000',\n * production: 'https://prod.dokploy.example.com:3000',\n * },\n * };\n * getEndpointForStage(config, 'production'); // => 'https://prod.dokploy.example.com:3000'\n *\n * // With single endpoint\n * const config = { endpoint: 'https://dokploy.example.com:3000' };\n * getEndpointForStage(config, 'production'); // => 'https://dokploy.example.com:3000'\n * ```\n */\nexport function getEndpointForStage(\n\tdokployConfig:\n\t\t| { endpoint?: string; endpoints?: Record<string, string> }\n\t\t| undefined,\n\tstage: string,\n): string | undefined {\n\tif (!dokployConfig) return undefined;\n\n\t// Check per-stage endpoint first\n\tif (dokployConfig.endpoints?.[stage]) {\n\t\treturn dokployConfig.endpoints[stage];\n\t}\n\n\t// Fall back to global endpoint\n\treturn dokployConfig.endpoint;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geekmidas/cli",
3
- "version": "10.0.0-alpha.84",
3
+ "version": "10.0.0-alpha.86",
4
4
  "description": "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs",
5
5
  "private": false,
6
6
  "type": "module",
@@ -119,16 +119,16 @@
119
119
  "prompts": "~2.4.2",
120
120
  "tsx": "~4.23.15",
121
121
  "yaml": "~2.9.1",
122
- "@geekmidas/cache": "10.0.0-alpha.84",
123
- "@geekmidas/constructs": "~10.0.0-alpha.84",
124
- "@geekmidas/db": "10.0.0-alpha.84",
125
- "@geekmidas/envkit": "~10.0.0-alpha.84",
126
- "@geekmidas/errors": "~10.0.0-alpha.84",
127
- "@geekmidas/logger": "~10.0.0-alpha.84",
128
- "@geekmidas/manifest": "10.0.0-alpha.84",
129
- "@geekmidas/schema": "~10.0.0-alpha.84",
130
- "@geekmidas/services": "~10.0.0-alpha.84",
131
- "@geekmidas/storage": "~10.0.0-alpha.84"
122
+ "@geekmidas/cache": "10.0.0-alpha.86",
123
+ "@geekmidas/constructs": "~10.0.0-alpha.86",
124
+ "@geekmidas/db": "10.0.0-alpha.86",
125
+ "@geekmidas/envkit": "~10.0.0-alpha.86",
126
+ "@geekmidas/errors": "~10.0.0-alpha.86",
127
+ "@geekmidas/logger": "~10.0.0-alpha.86",
128
+ "@geekmidas/manifest": "10.0.0-alpha.86",
129
+ "@geekmidas/schema": "~10.0.0-alpha.86",
130
+ "@geekmidas/services": "~10.0.0-alpha.86",
131
+ "@geekmidas/storage": "~10.0.0-alpha.86"
132
132
  },
133
133
  "devDependencies": {
134
134
  "@hono/node-server": "~1.19.5",
@@ -143,12 +143,12 @@
143
143
  "typescript": "~7.0.2",
144
144
  "vitest": "~5.0.2",
145
145
  "zod": "~4.6.5",
146
- "@geekmidas/emailkit": "~10.0.0-alpha.84",
147
- "@geekmidas/events": "~10.0.0-alpha.84",
148
- "@geekmidas/testkit": "10.0.0-alpha.84"
146
+ "@geekmidas/emailkit": "~10.0.0-alpha.86",
147
+ "@geekmidas/events": "~10.0.0-alpha.86",
148
+ "@geekmidas/testkit": "10.0.0-alpha.86"
149
149
  },
150
150
  "peerDependencies": {
151
- "@geekmidas/telescope": "~10.0.0-alpha.84"
151
+ "@geekmidas/telescope": "~10.0.0-alpha.86"
152
152
  },
153
153
  "peerDependenciesMeta": {
154
154
  "@geekmidas/telescope": {
@@ -1 +0,0 @@
1
- {"version":3,"file":"LocalStateStore-CdpzTvPn.cjs","names":["DocumentStateStore","join","readFile","contentVersion","StateVersionConflict","randomUUID","open","rename","rm","writeFile","StateLocked","mkdir","unlink","StateStoreBusy","stat"],"sources":["../src/deploy/LocalStateStore.ts"],"sourcesContent":["/**\n * Local filesystem state store.\n *\n * `.gkm/deploy-<stage>.json` holds the state, `.gkm/deploy-<stage>.lock` the\n * lock. Every file is mode 0600: state holds database passwords and IAM keys.\n */\n\nimport { randomUUID } from 'node:crypto';\nimport {\n\tmkdir,\n\topen,\n\treadFile,\n\trename,\n\trm,\n\tstat,\n\tunlink,\n\twriteFile,\n} from 'node:fs/promises';\nimport { join } from 'node:path';\nimport {\n\tcontentVersion,\n\tDocumentStateStore,\n\ttype LockHolder,\n\ttype RawState,\n\tStateLocked,\n\tStateStoreBusy,\n\ttype StateVersion,\n\tStateVersionConflict,\n} from './StateStore';\n\nconst FILE_MODE = 0o600;\n\n/** How long a write waits for another process's write to finish. */\nconst MUTEX_TIMEOUT_MS = 5_000;\n/**\n * A write holds the mutex for milliseconds, so one this old was left by a\n * process that died mid-write and is safe to take over.\n */\nconst MUTEX_STALE_MS = 30_000;\n\nfunction isCode(error: unknown, code: string): boolean {\n\treturn (error as NodeJS.ErrnoException)?.code === code;\n}\n\nexport class LocalStateStore extends DocumentStateStore {\n\tconstructor(readonly workspaceRoot: string) {\n\t\tsuper();\n\t}\n\n\tprivate get dir(): string {\n\t\treturn join(this.workspaceRoot, '.gkm');\n\t}\n\n\tstatePath(stage: string): string {\n\t\treturn join(this.dir, `deploy-${stage}.json`);\n\t}\n\n\tlockPath(stage: string): string {\n\t\treturn join(this.dir, `deploy-${stage}.lock`);\n\t}\n\n\tbackupPath(stage: string): string {\n\t\treturn join(this.dir, `deploy-${stage}.v1.json`);\n\t}\n\n\tprivate mutexPath(stage: string): string {\n\t\treturn join(this.dir, `deploy-${stage}.json.mutex`);\n\t}\n\n\tprotected location(stage: string): string {\n\t\treturn this.statePath(stage);\n\t}\n\n\tprotected async readRaw(stage: string): Promise<RawState | null> {\n\t\ttry {\n\t\t\tconst body = await readFile(this.statePath(stage), 'utf-8');\n\t\t\treturn { body, version: contentVersion(body) };\n\t\t} catch (error) {\n\t\t\tif (isCode(error, 'ENOENT')) return null;\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprotected async writeRaw(\n\t\tstage: string,\n\t\tbody: string,\n\t\texpectedVersion: StateVersion | null,\n\t): Promise<StateVersion> {\n\t\tawait this.ensureDir();\n\t\treturn this.withMutex(stage, async () => {\n\t\t\tconst current = await this.readRaw(stage);\n\t\t\tconst actual = current?.version ?? null;\n\t\t\tif (actual !== expectedVersion) {\n\t\t\t\tthrow new StateVersionConflict(stage, expectedVersion, actual);\n\t\t\t}\n\n\t\t\t// Written beside the target and renamed over it: a reader sees the\n\t\t\t// old file or the new one, never half of either.\n\t\t\tconst target = this.statePath(stage);\n\t\t\tconst temp = `${target}.${randomUUID()}.tmp`;\n\t\t\ttry {\n\t\t\t\tconst handle = await open(temp, 'wx', FILE_MODE);\n\t\t\t\ttry {\n\t\t\t\t\tawait handle.writeFile(body);\n\t\t\t\t\tawait handle.sync();\n\t\t\t\t} finally {\n\t\t\t\t\tawait handle.close();\n\t\t\t\t}\n\t\t\t\tawait rename(temp, target);\n\t\t\t} catch (error) {\n\t\t\t\tawait rm(temp, { force: true });\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\treturn contentVersion(body);\n\t\t});\n\t}\n\n\tprotected async writeV1Backup(stage: string, body: string): Promise<void> {\n\t\ttry {\n\t\t\tawait writeFile(this.backupPath(stage), body, {\n\t\t\t\tflag: 'wx',\n\t\t\t\tmode: FILE_MODE,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\t// The first migration's backup is the one worth keeping.\n\t\t\tif (!isCode(error, 'EEXIST')) throw error;\n\t\t}\n\t}\n\n\tprotected async createLock(stage: string, holder: LockHolder): Promise<void> {\n\t\tawait this.ensureDir();\n\t\tlet handle: Awaited<ReturnType<typeof open>>;\n\t\ttry {\n\t\t\thandle = await open(this.lockPath(stage), 'wx', FILE_MODE);\n\t\t} catch (error) {\n\t\t\tif (isCode(error, 'EEXIST')) {\n\t\t\t\tthrow new StateLocked(\n\t\t\t\t\tstage,\n\t\t\t\t\tawait this.readLock(stage),\n\t\t\t\t\tthis.lockPath(stage),\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\ttry {\n\t\t\tawait handle.writeFile(JSON.stringify(holder, null, 2));\n\t\t} finally {\n\t\t\tawait handle.close();\n\t\t}\n\t}\n\n\tprotected async readLock(stage: string): Promise<LockHolder | null> {\n\t\ttry {\n\t\t\treturn JSON.parse(\n\t\t\t\tawait readFile(this.lockPath(stage), 'utf-8'),\n\t\t\t) as LockHolder;\n\t\t} catch {\n\t\t\t// Missing, or created by a process that died before writing to it.\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprotected async removeLock(\n\t\tstage: string,\n\t\tholder: LockHolder | null,\n\t): Promise<void> {\n\t\tif (holder) {\n\t\t\tconst current = await this.readLock(stage);\n\t\t\tif (current?.id !== holder.id) return;\n\t\t}\n\t\tawait rm(this.lockPath(stage), { force: true });\n\t}\n\n\tprivate async ensureDir(): Promise<void> {\n\t\tawait mkdir(this.dir, { recursive: true, mode: 0o700 });\n\t}\n\n\t/**\n\t * Serialises the check-then-rename of a conditional write across\n\t * processes. The stage lock is for whole runs; this is for the instant of\n\t * a single write, so even writers that skip the lock never interleave.\n\t */\n\tprivate async withMutex<T>(stage: string, fn: () => Promise<T>): Promise<T> {\n\t\tconst path = this.mutexPath(stage);\n\t\tconst deadline = Date.now() + MUTEX_TIMEOUT_MS;\n\n\t\tfor (;;) {\n\t\t\ttry {\n\t\t\t\tconst handle = await open(path, 'wx', FILE_MODE);\n\t\t\t\tawait handle.close();\n\t\t\t\tbreak;\n\t\t\t} catch (error) {\n\t\t\t\tif (!isCode(error, 'EEXIST')) throw error;\n\t\t\t\tif (await this.isStale(path)) {\n\t\t\t\t\tawait unlink(path).catch(() => {});\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (Date.now() > deadline) throw new StateStoreBusy(stage, path);\n\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, 10));\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\treturn await fn();\n\t\t} finally {\n\t\t\tawait rm(path, { force: true });\n\t\t}\n\t}\n\n\tprivate async isStale(path: string): Promise<boolean> {\n\t\ttry {\n\t\t\treturn Date.now() - (await stat(path)).mtimeMs > MUTEX_STALE_MS;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"LocalStateStore-D1VfxRuK.mjs","names":[],"sources":["../src/deploy/LocalStateStore.ts"],"sourcesContent":["/**\n * Local filesystem state store.\n *\n * `.gkm/deploy-<stage>.json` holds the state, `.gkm/deploy-<stage>.lock` the\n * lock. Every file is mode 0600: state holds database passwords and IAM keys.\n */\n\nimport { randomUUID } from 'node:crypto';\nimport {\n\tmkdir,\n\topen,\n\treadFile,\n\trename,\n\trm,\n\tstat,\n\tunlink,\n\twriteFile,\n} from 'node:fs/promises';\nimport { join } from 'node:path';\nimport {\n\tcontentVersion,\n\tDocumentStateStore,\n\ttype LockHolder,\n\ttype RawState,\n\tStateLocked,\n\tStateStoreBusy,\n\ttype StateVersion,\n\tStateVersionConflict,\n} from './StateStore';\n\nconst FILE_MODE = 0o600;\n\n/** How long a write waits for another process's write to finish. */\nconst MUTEX_TIMEOUT_MS = 5_000;\n/**\n * A write holds the mutex for milliseconds, so one this old was left by a\n * process that died mid-write and is safe to take over.\n */\nconst MUTEX_STALE_MS = 30_000;\n\nfunction isCode(error: unknown, code: string): boolean {\n\treturn (error as NodeJS.ErrnoException)?.code === code;\n}\n\nexport class LocalStateStore extends DocumentStateStore {\n\tconstructor(readonly workspaceRoot: string) {\n\t\tsuper();\n\t}\n\n\tprivate get dir(): string {\n\t\treturn join(this.workspaceRoot, '.gkm');\n\t}\n\n\tstatePath(stage: string): string {\n\t\treturn join(this.dir, `deploy-${stage}.json`);\n\t}\n\n\tlockPath(stage: string): string {\n\t\treturn join(this.dir, `deploy-${stage}.lock`);\n\t}\n\n\tbackupPath(stage: string): string {\n\t\treturn join(this.dir, `deploy-${stage}.v1.json`);\n\t}\n\n\tprivate mutexPath(stage: string): string {\n\t\treturn join(this.dir, `deploy-${stage}.json.mutex`);\n\t}\n\n\tprotected location(stage: string): string {\n\t\treturn this.statePath(stage);\n\t}\n\n\tprotected async readRaw(stage: string): Promise<RawState | null> {\n\t\ttry {\n\t\t\tconst body = await readFile(this.statePath(stage), 'utf-8');\n\t\t\treturn { body, version: contentVersion(body) };\n\t\t} catch (error) {\n\t\t\tif (isCode(error, 'ENOENT')) return null;\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprotected async writeRaw(\n\t\tstage: string,\n\t\tbody: string,\n\t\texpectedVersion: StateVersion | null,\n\t): Promise<StateVersion> {\n\t\tawait this.ensureDir();\n\t\treturn this.withMutex(stage, async () => {\n\t\t\tconst current = await this.readRaw(stage);\n\t\t\tconst actual = current?.version ?? null;\n\t\t\tif (actual !== expectedVersion) {\n\t\t\t\tthrow new StateVersionConflict(stage, expectedVersion, actual);\n\t\t\t}\n\n\t\t\t// Written beside the target and renamed over it: a reader sees the\n\t\t\t// old file or the new one, never half of either.\n\t\t\tconst target = this.statePath(stage);\n\t\t\tconst temp = `${target}.${randomUUID()}.tmp`;\n\t\t\ttry {\n\t\t\t\tconst handle = await open(temp, 'wx', FILE_MODE);\n\t\t\t\ttry {\n\t\t\t\t\tawait handle.writeFile(body);\n\t\t\t\t\tawait handle.sync();\n\t\t\t\t} finally {\n\t\t\t\t\tawait handle.close();\n\t\t\t\t}\n\t\t\t\tawait rename(temp, target);\n\t\t\t} catch (error) {\n\t\t\t\tawait rm(temp, { force: true });\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\treturn contentVersion(body);\n\t\t});\n\t}\n\n\tprotected async writeV1Backup(stage: string, body: string): Promise<void> {\n\t\ttry {\n\t\t\tawait writeFile(this.backupPath(stage), body, {\n\t\t\t\tflag: 'wx',\n\t\t\t\tmode: FILE_MODE,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\t// The first migration's backup is the one worth keeping.\n\t\t\tif (!isCode(error, 'EEXIST')) throw error;\n\t\t}\n\t}\n\n\tprotected async createLock(stage: string, holder: LockHolder): Promise<void> {\n\t\tawait this.ensureDir();\n\t\tlet handle: Awaited<ReturnType<typeof open>>;\n\t\ttry {\n\t\t\thandle = await open(this.lockPath(stage), 'wx', FILE_MODE);\n\t\t} catch (error) {\n\t\t\tif (isCode(error, 'EEXIST')) {\n\t\t\t\tthrow new StateLocked(\n\t\t\t\t\tstage,\n\t\t\t\t\tawait this.readLock(stage),\n\t\t\t\t\tthis.lockPath(stage),\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\ttry {\n\t\t\tawait handle.writeFile(JSON.stringify(holder, null, 2));\n\t\t} finally {\n\t\t\tawait handle.close();\n\t\t}\n\t}\n\n\tprotected async readLock(stage: string): Promise<LockHolder | null> {\n\t\ttry {\n\t\t\treturn JSON.parse(\n\t\t\t\tawait readFile(this.lockPath(stage), 'utf-8'),\n\t\t\t) as LockHolder;\n\t\t} catch {\n\t\t\t// Missing, or created by a process that died before writing to it.\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprotected async removeLock(\n\t\tstage: string,\n\t\tholder: LockHolder | null,\n\t): Promise<void> {\n\t\tif (holder) {\n\t\t\tconst current = await this.readLock(stage);\n\t\t\tif (current?.id !== holder.id) return;\n\t\t}\n\t\tawait rm(this.lockPath(stage), { force: true });\n\t}\n\n\tprivate async ensureDir(): Promise<void> {\n\t\tawait mkdir(this.dir, { recursive: true, mode: 0o700 });\n\t}\n\n\t/**\n\t * Serialises the check-then-rename of a conditional write across\n\t * processes. The stage lock is for whole runs; this is for the instant of\n\t * a single write, so even writers that skip the lock never interleave.\n\t */\n\tprivate async withMutex<T>(stage: string, fn: () => Promise<T>): Promise<T> {\n\t\tconst path = this.mutexPath(stage);\n\t\tconst deadline = Date.now() + MUTEX_TIMEOUT_MS;\n\n\t\tfor (;;) {\n\t\t\ttry {\n\t\t\t\tconst handle = await open(path, 'wx', FILE_MODE);\n\t\t\t\tawait handle.close();\n\t\t\t\tbreak;\n\t\t\t} catch (error) {\n\t\t\t\tif (!isCode(error, 'EEXIST')) throw error;\n\t\t\t\tif (await this.isStale(path)) {\n\t\t\t\t\tawait unlink(path).catch(() => {});\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (Date.now() > deadline) throw new StateStoreBusy(stage, path);\n\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, 10));\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\treturn await fn();\n\t\t} finally {\n\t\t\tawait rm(path, { force: true });\n\t\t}\n\t}\n\n\tprivate async isStale(path: string): Promise<boolean> {\n\t\ttry {\n\t\t\treturn Date.now() - (await stat(path)).mtimeMs > MUTEX_STALE_MS;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"add-BQ8v1ZII.cjs","names":["provideKey","appKey","appEnvKeys","requiredStageKeys","loadWorkspaceSettings","constructGlobs","discover","secretsStoreFor","loadCredentialSchemas","initStageSecrets","s3Parse","s3Build","issueLine"],"sources":["../src/secrets/stageKeys.ts","../src/secrets/add.ts"],"sourcesContent":["/**\n * The keys a workspace's stage must be given — across every app, each once,\n * with who reads it and whether the stage holds it.\n *\n * The keys themselves are `requiredStageKeys`'s, the list a deploy refuses a\n * stage by. What this adds is the workspace: which constructs its apps read\n * (`appEnvKeys`, the keys each app's environment is built from) and which of\n * the keys the stage has set.\n */\n\nimport { type ConstructManifest, provideKey } from '@geekmidas/manifest';\nimport {\n\ttype CredentialDeclaration,\n\trequiredStageKeys,\n\ttype ServiceDeclaration,\n\ttype StageKey,\n} from '../deploy/devServices';\nimport { appEnvKeys } from '../reconcile/apps';\nimport { appKey } from '../workspace/derive';\n\n/** A stage key, and whether the stage's secrets hold it. */\nexport interface WorkspaceStageKey extends StageKey {\n\tset: boolean;\n}\n\nexport interface WorkspaceStageKeysInput {\n\tmanifest: ConstructManifest;\n\t/** Each owner's runnables' edges, from discovery. */\n\trunnables?: Readonly<Record<string, readonly string[]>>;\n\t/** Whether the stage is the workspace's local one. */\n\tlocal: boolean;\n\t/** What the stage's secrets hold, by key. */\n\tsupplied: Readonly<Record<string, string>>;\n\t/** The stage's base domain, for the examples. */\n\tdomain?: string;\n}\n\n/** The keys of each kind of construct an app reads, that the stage supplies. */\nfunction keysOf(id: string, kind: string): string[] {\n\tswitch (kind) {\n\t\tcase 'email':\n\t\t\treturn [provideKey(id, 'url'), provideKey(id, 'from')];\n\t\tcase 'objects':\n\t\tcase 'file-server':\n\t\t\treturn [provideKey(id, 'url')];\n\t\tcase 'external-api':\n\t\tcase 'credential':\n\t\t\treturn [provideKey(id, 'credentials')];\n\t\tdefault:\n\t\t\treturn [];\n\t}\n}\n\n/**\n * Every key the stage must be given for the constructs its apps read — a\n * file server's whenever its bucket is read, since its client reads both —\n * and every file server's bucket, which serves it whether or not an app\n * reads it directly.\n */\nexport function workspaceStageKeys(\n\tinput: WorkspaceStageKeysInput,\n): WorkspaceStageKey[] {\n\tconst { manifest } = input;\n\n\t// What each app's environment holds.\n\tconst reads = new Map<string, Set<string>>();\n\tfor (const [id, declaration] of Object.entries(manifest)) {\n\t\tconst { kind } = declaration;\n\t\tif (kind !== 'rest-api' && kind !== 'site' && kind !== 'mobile-app') {\n\t\t\tcontinue;\n\t\t}\n\t\tconst name = appKey(id);\n\t\tconst keys = appEnvKeys(manifest, name, input.runnables);\n\t\tif (keys) reads.set(name, keys);\n\t}\n\n\tconst readers = (id: string, kind: string) => {\n\t\tconst keys = keysOf(id, kind);\n\t\treturn [...reads]\n\t\t\t.filter(([, env]) => keys.some((key) => env.has(key)))\n\t\t\t.map(([app]) => app)\n\t\t\t.sort();\n\t};\n\n\tconst services: ServiceDeclaration[] = [];\n\tconst credentials: CredentialDeclaration[] = [];\n\tconst buckets = new Set<string>();\n\tfor (const [id, declaration] of Object.entries(manifest)) {\n\t\tconst { kind } = declaration;\n\t\t// A file server's client reads its bucket's URL and its own, and an\n\t\t// app's edge is to the bucket: whoever reads the bucket reads both.\n\t\tconst apps =\n\t\t\tdeclaration.kind === 'file-server' && declaration.of\n\t\t\t\t? [\n\t\t\t\t\t\t...new Set([\n\t\t\t\t\t\t\t...readers(id, kind),\n\t\t\t\t\t\t\t...readers(declaration.of, 'objects'),\n\t\t\t\t\t\t]),\n\t\t\t\t\t].sort()\n\t\t\t\t: readers(id, kind);\n\t\tif (kind === 'external-api' || kind === 'credential') {\n\t\t\tif (apps.length > 0) credentials.push({ id, kind, apps });\n\t\t\tcontinue;\n\t\t}\n\t\tif (kind !== 'email' && kind !== 'objects' && kind !== 'file-server') {\n\t\t\tcontinue;\n\t\t}\n\t\tif (apps.length === 0) continue;\n\t\tconst of = declaration.kind === 'file-server' ? declaration.of : undefined;\n\t\tservices.push({ id, kind, ...(of ? { of } : {}), apps });\n\t\tif (of) buckets.add(of);\n\t}\n\tfor (const of of buckets) {\n\t\tif (services.some((s) => s.id === of)) continue;\n\t\tif (manifest[of]?.kind !== 'objects') continue;\n\t\tservices.push({ id: of, kind: 'objects', apps: readers(of, 'objects') });\n\t}\n\n\treturn requiredStageKeys({\n\t\tlocal: input.local,\n\t\tservices,\n\t\tcredentials,\n\t\t...(input.domain ? { domain: input.domain } : {}),\n\t}).map((key) => ({ ...key, set: input.supplied[key.key] !== undefined }));\n}\n","/**\n * `gkm secrets:add --stage <stage>` — the secrets a stage must be given,\n * built key by key.\n *\n * It offers exactly the keys a deploy would refuse the stage without\n * (`requiredStageKeys`), across every app in the workspace, each once: the\n * stage's buckets, mail and file servers where it is deployed, and every\n * third party's credentials. A key is built by what it is — a bucket from its\n * provider, mail from its SMTP server, credentials field by field from their\n * construct's schema — checked, and saved through the stage's own store, the\n * one `gkm secrets:set` writes. No value is ever printed.\n *\n * `--json` asks nothing: it prints the keys, for a script or an agent to set\n * with `gkm secrets:set`.\n */\n\nimport {\n\ttype S3Address,\n\tbuild as s3Build,\n\tparse as s3Parse,\n} from '@geekmidas/storage/s3-url';\nimport prompts, { type PromptObject } from 'prompts';\nimport { z } from 'zod';\nimport { loadWorkspaceSettings } from '../config';\nimport { discover } from '../reconcile/discover';\nimport { constructGlobs } from '../reconcile/workspace';\nimport { assertDeployedStage } from '../workspace/stages';\nimport {\n\ttype CredentialSchemaInfo,\n\ttype CredentialSchemas,\n\tissueLine,\n\tloadCredentialSchemas,\n} from './credentialSchemas';\nimport { type WorkspaceStageKey, workspaceStageKeys } from './stageKeys';\nimport { initStageSecrets } from './storage';\nimport { secretsStoreFor } from './store';\n\nexport interface SecretsAddOptions {\n\tstage: string;\n\t/** Only the keys the stage has not set. */\n\tmissing?: boolean;\n\t/** Print the keys as JSON and ask nothing. */\n\tjson?: boolean;\n\t/** The workspace — the current directory when absent. */\n\tcwd?: string;\n\t/** The CLI's home, for a file store's keys. Defaults to `GKM_HOME`. */\n\thome?: string;\n}\n\n/** Where the command writes, and whether there is anyone to ask. */\nexport interface SecretsAddIo {\n\t/** A line for the person at the terminal. */\n\tlog(line: string): void;\n\t/** `--json`'s output. */\n\twrite(chunk: string): void;\n\t/** Whether prompts can be answered. */\n\tinteractive: boolean;\n}\n\n/** What a run of the command came to. */\nexport interface SecretsAddResult {\n\t/** The keys offered. */\n\tkeys: WorkspaceStageKey[];\n\t/** The keys saved, in the order they were built. */\n\tsaved: string[];\n}\n\n/** One key as `--json` prints it. */\nexport interface StageKeyJson {\n\tkey: string;\n\tkind: WorkspaceStageKey['kind'];\n\tconstruct: string;\n\tapps: string[];\n\tset: boolean;\n}\n\n/** `gkm secrets:add` with nobody at a terminal to answer it. */\nexport class SecretsAddNeedsTerminal extends Error {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`gkm secrets:add asks for each key, and there is no terminal to ask ` +\n\t\t\t\t`at. List what the stage needs with ` +\n\t\t\t\t`gkm secrets:add --stage ${stage} --missing --json, and set each ` +\n\t\t\t\t`with gkm secrets:set <KEY> <value> --stage ${stage}.`,\n\t\t);\n\t\tthis.name = 'SecretsAddNeedsTerminal';\n\t}\n}\n\n/** The person stopped the builder; nothing was saved. */\nexport class SecretsAddCancelled extends Error {\n\tconstructor(readonly stage: string) {\n\t\tsuper(`Stopped. Nothing was saved to the stage '${stage}'.`);\n\t\tthis.name = 'SecretsAddCancelled';\n\t}\n}\n\nconst defaultIo = (): SecretsAddIo => ({\n\tlog: (line) => console.log(line),\n\twrite: (chunk) => {\n\t\tprocess.stdout.write(chunk);\n\t},\n\tinteractive: process.stdin.isTTY === true && process.stdout.isTTY === true,\n});\n\n/** `gkm secrets:add`. */\nexport async function secretsAddCommand(\n\toptions: SecretsAddOptions,\n\tio: SecretsAddIo = defaultIo(),\n): Promise<SecretsAddResult> {\n\tconst { stage } = options;\n\tconst workspace = await loadWorkspaceSettings(options.cwd ?? process.cwd());\n\tconst local = stage === workspace.stages.local;\n\tif (!local) assertDeployedStage(workspace.stages, stage);\n\n\tconst patterns = constructGlobs(workspace);\n\tconst runnables: Record<string, string[]> = {};\n\tconst manifest = await discover({\n\t\tpatterns,\n\t\tcwd: workspace.root,\n\t\trunnables,\n\t});\n\n\tconst store = await secretsStoreFor(workspace, stage, {\n\t\t...(options.home ? { home: options.home } : {}),\n\t});\n\tconst stored = await store.read(stage);\n\tconst domain = workspace.deploy?.domains?.[stage];\n\n\tconst all = workspaceStageKeys({\n\t\tmanifest,\n\t\trunnables,\n\t\tlocal,\n\t\tsupplied: stored?.custom ?? {},\n\t\t...(domain ? { domain } : {}),\n\t});\n\tconst keys = options.missing ? all.filter((k) => !k.set) : all;\n\n\tif (options.json) {\n\t\tconst listed: StageKeyJson[] = keys.map((k) => ({\n\t\t\tkey: k.key,\n\t\t\tkind: k.kind,\n\t\t\tconstruct: k.id,\n\t\t\tapps: [...(k.apps ?? [])],\n\t\t\tset: k.set,\n\t\t}));\n\t\tio.write(`${JSON.stringify(listed, null, 2)}\\n`);\n\t\treturn { keys, saved: [] };\n\t}\n\n\tif (!io.interactive) throw new SecretsAddNeedsTerminal(stage);\n\n\tif (keys.length === 0) {\n\t\tio.log(\n\t\t\tall.length === 0\n\t\t\t\t? `Nothing to add: no construct on '${stage}' needs a key only the stage can supply.`\n\t\t\t\t: `Nothing to add: the stage '${stage}' has every key it needs.`,\n\t\t);\n\t\treturn { keys, saved: [] };\n\t}\n\n\tconst ask = asker(stage);\n\tconst chosen: string[] = await ask({\n\t\ttype: 'multiselect',\n\t\tmessage: `Which keys for '${stage}'? (${store.name} store)`,\n\t\tchoices: keys.map((k) => ({\n\t\t\ttitle: `${k.key} ${KIND_LABEL[k.kind]} '${k.id}'${k.apps?.length ? ` · ${k.apps.join(', ')}` : ''}${k.set ? ' · set' : ''}`,\n\t\t\tvalue: k.key,\n\t\t\tselected: !k.set,\n\t\t})),\n\t\tinstructions: false,\n\t\thint: 'space to toggle, enter to continue',\n\t});\n\tconst selected = keys.filter((k) => (chosen ?? []).includes(k.key));\n\n\tconst values: Record<string, string> = {};\n\tconst has = (key: string) =>\n\t\tvalues[key] !== undefined || stored?.custom?.[key] !== undefined;\n\tlet schemas: CredentialSchemas | undefined;\n\n\tfor (const entry of selected) {\n\t\tif (entry.set) {\n\t\t\tconst overwrite: boolean = await ask({\n\t\t\t\ttype: 'confirm',\n\t\t\t\tmessage: `${entry.key} is set. Replace it?`,\n\t\t\t\tinitial: false,\n\t\t\t});\n\t\t\tif (!overwrite) continue;\n\t\t}\n\t\tio.log(`\\n${entry.key} — ${entry.what}`);\n\n\t\tswitch (entry.kind) {\n\t\t\tcase 'bucket':\n\t\t\t\tObject.assign(values, await buildBucket(ask, io, entry, has));\n\t\t\t\tbreak;\n\t\t\tcase 'email':\n\t\t\t\tvalues[entry.key] = entry.key.endsWith('_FROM')\n\t\t\t\t\t? await askFrom(ask, io)\n\t\t\t\t\t: await buildSmtpUrl(ask, io);\n\t\t\t\tbreak;\n\t\t\tcase 'file-server':\n\t\t\t\tvalues[entry.key] = await askHttpUrl(ask, io, 'Its public URL');\n\t\t\t\tbreak;\n\t\t\tcase 'external-api':\n\t\t\tcase 'credential':\n\t\t\t\tschemas ??= await loadCredentialSchemas({\n\t\t\t\t\troot: workspace.root,\n\t\t\t\t\tpatterns,\n\t\t\t\t});\n\t\t\t\tvalues[entry.key] = await buildCredentials(ask, io, entry, schemas);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tconst saved = Object.keys(values);\n\tif (saved.length === 0) {\n\t\tio.log('\\nNothing was saved.');\n\t\treturn { keys, saved };\n\t}\n\n\tconst base = stored ?? initStageSecrets(stage);\n\tawait store.write(stage, {\n\t\t...base,\n\t\tupdatedAt: new Date().toISOString(),\n\t\tcustom: { ...base.custom, ...values },\n\t});\n\tio.log(\n\t\t`\\n✓ Saved ${saved.join(', ')} to the stage '${stage}' (${store.name} store).`,\n\t);\n\treturn { keys, saved };\n}\n\nconst KIND_LABEL: Record<WorkspaceStageKey['kind'], string> = {\n\tbucket: 'bucket',\n\temail: 'email',\n\t'file-server': 'file server',\n\t'external-api': 'external API',\n\tcredential: 'credential',\n};\n\n/** One question, answered — or the run stopped. */\ntype Ask = <T = string>(question: Omit<PromptObject, 'name'>) => Promise<T>;\n\nfunction asker(stage: string): Ask {\n\treturn async <T>(question: Omit<PromptObject, 'name'>) => {\n\t\tconst answers = await prompts(\n\t\t\t{ ...question, name: 'value' } as PromptObject,\n\t\t\t{\n\t\t\t\tonCancel: () => {\n\t\t\t\t\tthrow new SecretsAddCancelled(stage);\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t\treturn answers.value as T;\n\t};\n}\n\n/** Ask until `accept` takes the answer; `accept` returns why it did not. */\nasync function askUntil<T>(\n\task: Ask,\n\tio: SecretsAddIo,\n\tquestion: Omit<PromptObject, 'name'>,\n\taccept: (value: T) => string | undefined,\n): Promise<T> {\n\tfor (;;) {\n\t\tconst value = await ask<T>(question);\n\t\tconst problem = accept(value);\n\t\tif (!problem) return value;\n\t\tio.log(` ✗ ${problem}`);\n\t}\n}\n\nconst required =\n\t(what: string) =>\n\t(value: string | undefined): string | undefined =>\n\t\tvalue?.trim() ? undefined : `${what} is required.`;\n\nconst text = (value: unknown) => String(value ?? '').trim();\n\n/** An `http(s)://` URL. */\nconst HttpUrl = z.url({ protocol: /^https?$/ });\n\nasync function askHttpUrl(\n\task: Ask,\n\tio: SecretsAddIo,\n\tmessage: string,\n): Promise<string> {\n\treturn text(\n\t\tawait askUntil<string>(ask, io, { type: 'text', message }, (value) =>\n\t\t\tHttpUrl.safeParse(text(value)).success\n\t\t\t\t? undefined\n\t\t\t\t: 'An http:// or https:// URL is required.',\n\t\t),\n\t);\n}\n\nasync function askFrom(ask: Ask, io: SecretsAddIo): Promise<string> {\n\treturn text(\n\t\tawait askUntil<string>(\n\t\t\task,\n\t\t\tio,\n\t\t\t{ type: 'text', message: 'The address it sends from' },\n\t\t\t(value) =>\n\t\t\t\tz.email().safeParse(text(value)).success\n\t\t\t\t\t? undefined\n\t\t\t\t\t: 'An email address is required, e.g. noreply@example.com.',\n\t\t),\n\t);\n}\n\n/**\n * An SMTP server as the URL `@geekmidas/emailkit` reads: `smtp://` for\n * STARTTLS, `smtps://` for TLS from the first byte, with the user and\n * password percent-encoded.\n */\nasync function buildSmtpUrl(ask: Ask, io: SecretsAddIo): Promise<string> {\n\tconst host = text(\n\t\tawait askUntil<string>(\n\t\t\task,\n\t\t\tio,\n\t\t\t{ type: 'text', message: 'SMTP host' },\n\t\t\trequired('The host'),\n\t\t),\n\t);\n\tconst port = await askUntil<string | number>(\n\t\task,\n\t\tio,\n\t\t{ type: 'text', message: 'Port', initial: '587' },\n\t\t(value) => {\n\t\t\tconst n = Number(value);\n\t\t\treturn Number.isInteger(n) && n > 0 && n < 65536\n\t\t\t\t? undefined\n\t\t\t\t: 'A port number is required.';\n\t\t},\n\t);\n\tconst user = text(\n\t\tawait ask<string>({ type: 'text', message: 'User (empty for none)' }),\n\t);\n\tconst password = user\n\t\t? await ask<string>({ type: 'password', message: 'Password' })\n\t\t: '';\n\tconst tls = await ask<'starttls' | 'tls'>({\n\t\ttype: 'select',\n\t\tmessage: 'TLS',\n\t\tchoices: [\n\t\t\t{ title: 'STARTTLS (smtp://, usually port 587)', value: 'starttls' },\n\t\t\t{ title: 'TLS on connect (smtps://, usually port 465)', value: 'tls' },\n\t\t],\n\t\tinitial: 0,\n\t});\n\n\tconst url = new URL(`${tls === 'tls' ? 'smtps' : 'smtp'}://${host}`);\n\turl.port = String(Number(port));\n\tif (user) {\n\t\turl.username = encodeURIComponent(user);\n\t\turl.password = encodeURIComponent(password ?? '');\n\t}\n\treturn url.toString().replace(/\\/$/, '');\n}\n\n/**\n * A bucket's URL, from its provider — and, optionally, a key for it alone in\n * the URL, or the stage's shared pair. Credentials are never required: without\n * them the SDK's own chain signs, a role included.\n */\nasync function buildBucket(\n\task: Ask,\n\tio: SecretsAddIo,\n\tentry: WorkspaceStageKey,\n\thas: (key: string) => boolean,\n): Promise<Record<string, string>> {\n\tconst provider = await ask<'s3' | 'r2' | 'compatible' | 'url'>({\n\t\ttype: 'select',\n\t\tmessage: 'Where is the bucket?',\n\t\tchoices: [\n\t\t\t{ title: 'AWS S3', value: 's3' },\n\t\t\t{ title: 'Cloudflare R2', value: 'r2' },\n\t\t\t{ title: 'MinIO, or another S3-compatible store', value: 'compatible' },\n\t\t\t{ title: 'Paste an s3:// URL', value: 'url' },\n\t\t],\n\t\tinitial: 0,\n\t});\n\n\tconst bucketName = async () =>\n\t\ttext(\n\t\t\tawait askUntil<string>(\n\t\t\t\task,\n\t\t\t\tio,\n\t\t\t\t{ type: 'text', message: 'Bucket name' },\n\t\t\t\trequired('The bucket name'),\n\t\t\t),\n\t\t);\n\n\tlet address: S3Address;\n\tswitch (provider) {\n\t\tcase 's3': {\n\t\t\tconst bucket = await bucketName();\n\t\t\tconst region = text(\n\t\t\t\tawait askUntil<string>(\n\t\t\t\t\task,\n\t\t\t\t\tio,\n\t\t\t\t\t{ type: 'text', message: 'Region', initial: 'eu-west-1' },\n\t\t\t\t\trequired('The region'),\n\t\t\t\t),\n\t\t\t);\n\t\t\taddress = { bucket, region };\n\t\t\tbreak;\n\t\t}\n\t\tcase 'r2': {\n\t\t\tconst account = text(\n\t\t\t\tawait askUntil<string>(\n\t\t\t\t\task,\n\t\t\t\t\tio,\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text',\n\t\t\t\t\t\tmessage: 'Cloudflare account id, or the R2 endpoint URL',\n\t\t\t\t\t},\n\t\t\t\t\trequired('The account id or endpoint'),\n\t\t\t\t),\n\t\t\t);\n\t\t\tconst bucket = await bucketName();\n\t\t\taddress = {\n\t\t\t\tbucket,\n\t\t\t\tregion: 'auto',\n\t\t\t\tendpoint: /^https?:\\/\\//.test(account)\n\t\t\t\t\t? account\n\t\t\t\t\t: `https://${account}.r2.cloudflarestorage.com`,\n\t\t\t};\n\t\t\tbreak;\n\t\t}\n\t\tcase 'compatible': {\n\t\t\tconst endpoint = await askHttpUrl(ask, io, 'Endpoint URL');\n\t\t\tconst bucket = await bucketName();\n\t\t\tconst region = text(\n\t\t\t\tawait ask<string>({\n\t\t\t\t\ttype: 'text',\n\t\t\t\t\tmessage: 'Region',\n\t\t\t\t\tinitial: 'us-east-1',\n\t\t\t\t}),\n\t\t\t);\n\t\t\tconst forcePathStyle = await ask<boolean>({\n\t\t\t\ttype: 'confirm',\n\t\t\t\tmessage: 'Path-style addressing? (MinIO needs it)',\n\t\t\t\tinitial: true,\n\t\t\t});\n\t\t\taddress = {\n\t\t\t\tbucket,\n\t\t\t\t...(region ? { region } : {}),\n\t\t\t\tendpoint,\n\t\t\t\t...(forcePathStyle ? { forcePathStyle } : {}),\n\t\t\t};\n\t\t\tbreak;\n\t\t}\n\t\tcase 'url': {\n\t\t\tconst pasted = await askUntil<string>(\n\t\t\t\task,\n\t\t\t\tio,\n\t\t\t\t{ type: 'password', message: 'The s3:// URL' },\n\t\t\t\t(value) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\ts3Parse(text(value));\n\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\treturn `Not an s3:// URL the storage client can read (${(error as Error).name}).`;\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t);\n\t\t\taddress = s3Parse(text(pasted));\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (!address.accessKeyId) {\n\t\tconst own = await ask<boolean>({\n\t\t\ttype: 'confirm',\n\t\t\tmessage: `A key for '${entry.id}' alone, in its URL? (optional)`,\n\t\t\tinitial: false,\n\t\t});\n\t\tif (own) {\n\t\t\tconst accessKeyId = text(\n\t\t\t\tawait askUntil<string>(\n\t\t\t\t\task,\n\t\t\t\t\tio,\n\t\t\t\t\t{ type: 'text', message: 'Access key id' },\n\t\t\t\t\trequired('The access key id'),\n\t\t\t\t),\n\t\t\t);\n\t\t\tconst secretAccessKey = await askUntil<string>(\n\t\t\t\task,\n\t\t\t\tio,\n\t\t\t\t{ type: 'password', message: 'Secret access key' },\n\t\t\t\trequired('The secret access key'),\n\t\t\t);\n\t\t\taddress = { ...address, accessKeyId, secretAccessKey };\n\t\t}\n\t}\n\n\tconst values: Record<string, string> = { [entry.key]: s3Build(address) };\n\n\tif (!address.accessKeyId && !has('AWS_ACCESS_KEY_ID')) {\n\t\tconst shared = await ask<boolean>({\n\t\t\ttype: 'confirm',\n\t\t\tmessage:\n\t\t\t\t\"Set the stage's shared AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY? \" +\n\t\t\t\t'(optional: without them a role signs)',\n\t\t\tinitial: false,\n\t\t});\n\t\tif (shared) {\n\t\t\tvalues.AWS_ACCESS_KEY_ID = text(\n\t\t\t\tawait askUntil<string>(\n\t\t\t\t\task,\n\t\t\t\t\tio,\n\t\t\t\t\t{ type: 'text', message: 'AWS_ACCESS_KEY_ID' },\n\t\t\t\t\trequired('The access key id'),\n\t\t\t\t),\n\t\t\t);\n\t\t\tvalues.AWS_SECRET_ACCESS_KEY = await askUntil<string>(\n\t\t\t\task,\n\t\t\t\tio,\n\t\t\t\t{ type: 'password', message: 'AWS_SECRET_ACCESS_KEY' },\n\t\t\t\trequired('The secret access key'),\n\t\t\t);\n\t\t}\n\t}\n\n\treturn values;\n}\n\n/** A field whose name says it is a secret, asked for without echo. */\nconst SECRET_FIELD = /secret|key|token|password/i;\n\ninterface JsonSchemaProperty {\n\ttype?: string | string[];\n\tenum?: unknown[];\n\tdescription?: string;\n}\n\n/** The properties of an object schema, or nothing for any other shape. */\nfunction objectProperties(\n\tinfo: CredentialSchemaInfo | undefined,\n):\n\t| { properties: Record<string, JsonSchemaProperty>; required: string[] }\n\t| undefined {\n\tconst schema = info?.jsonSchema;\n\tif (!schema || schema.type !== 'object') return undefined;\n\tconst properties = schema.properties;\n\tif (!properties || typeof properties !== 'object') return undefined;\n\treturn {\n\t\tproperties: properties as Record<string, JsonSchemaProperty>,\n\t\trequired: Array.isArray(schema.required)\n\t\t\t? (schema.required as string[])\n\t\t\t: [],\n\t};\n}\n\nfunction typesOf(property: JsonSchemaProperty): string[] {\n\tif (Array.isArray(property.type)) return property.type;\n\treturn property.type ? [property.type] : [];\n}\n\n/**\n * Credentials, field by field where the construct's schema is an object it\n * can describe, and as JSON otherwise — checked against the schema itself\n * before anything is kept, and asked again, with each issue's path, until\n * the schema takes it.\n */\nasync function buildCredentials(\n\task: Ask,\n\tio: SecretsAddIo,\n\tentry: WorkspaceStageKey,\n\tschemas: CredentialSchemas,\n): Promise<string> {\n\tconst info = schemas.schemas.find((s) => s.key === entry.key);\n\tconst shape = objectProperties(info);\n\tif (!info) {\n\t\tio.log(\n\t\t\t` '${entry.id}' exposes no schema to check against; the value is stored as given.`,\n\t\t);\n\t}\n\n\tfor (;;) {\n\t\tconst raw = shape\n\t\t\t? JSON.stringify(await askFields(ask, io, shape))\n\t\t\t: text(\n\t\t\t\t\tawait ask<string>({\n\t\t\t\t\t\ttype: 'password',\n\t\t\t\t\t\tmessage: 'The value, as JSON (or one opaque string)',\n\t\t\t\t\t}),\n\t\t\t\t);\n\n\t\tconst check = await schemas.check(entry.key, raw);\n\t\tif (check.ok) return raw;\n\t\tio.log(` ✗ The schema of '${entry.id}' refuses it:`);\n\t\tfor (const issue of check.issues) {\n\t\t\tio.log(` ${issueLine(entry.key, issue)}`);\n\t\t}\n\t\tio.log(' Again:');\n\t}\n}\n\nasync function askFields(\n\task: Ask,\n\tio: SecretsAddIo,\n\tshape: {\n\t\tproperties: Record<string, JsonSchemaProperty>;\n\t\trequired: string[];\n\t},\n): Promise<Record<string, unknown>> {\n\tconst value: Record<string, unknown> = {};\n\tfor (const [name, property] of Object.entries(shape.properties)) {\n\t\tconst optional = !shape.required.includes(name);\n\t\tconst label = `${name}${optional ? ' (optional)' : ''}${property.description ? ` — ${property.description}` : ''}`;\n\t\tconst types = typesOf(property);\n\n\t\tif (property.enum?.length) {\n\t\t\tconst choices = property.enum.map((option) => ({\n\t\t\t\ttitle: String(option),\n\t\t\t\tvalue: option,\n\t\t\t}));\n\t\t\tconst picked = await ask<unknown>({\n\t\t\t\ttype: 'select',\n\t\t\t\tmessage: label,\n\t\t\t\tchoices: optional\n\t\t\t\t\t? [{ title: '(none)', value: undefined }, ...choices]\n\t\t\t\t\t: choices,\n\t\t\t} as Omit<PromptObject, 'name'>);\n\t\t\tif (picked !== undefined) value[name] = picked;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (types.includes('boolean')) {\n\t\t\tvalue[name] = await ask<boolean>({\n\t\t\t\ttype: 'confirm',\n\t\t\t\tmessage: label,\n\t\t\t\tinitial: false,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst numeric = types.includes('number') || types.includes('integer');\n\t\tconst plain = types.length === 0 || types.includes('string');\n\t\tconst answer = await askUntil<string>(\n\t\t\task,\n\t\t\tio,\n\t\t\t{\n\t\t\t\ttype: SECRET_FIELD.test(name) ? 'password' : 'text',\n\t\t\t\tmessage: label,\n\t\t\t},\n\t\t\t(given) => {\n\t\t\t\tconst typed = text(given);\n\t\t\t\tif (!typed) return optional ? undefined : `${name} is required.`;\n\t\t\t\tif (numeric && !plain && Number.isNaN(Number(typed))) {\n\t\t\t\t\treturn `${name} is a number.`;\n\t\t\t\t}\n\t\t\t\tif (!numeric && !plain) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tJSON.parse(typed);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn `${name} is JSON (${types.join(' or ')}).`;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn undefined;\n\t\t\t},\n\t\t);\n\t\tconst typed = text(answer);\n\t\tif (!typed) continue;\n\t\tvalue[name] = plain ? typed : numeric ? Number(typed) : JSON.parse(typed);\n\t}\n\treturn value;\n}\n"],"mappings":"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"}