@geekmidas/cli 10.0.0-alpha.4 → 10.0.0-alpha.41

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 (257) hide show
  1. package/README.md +52 -74
  2. package/bin/adjacent-tsconfig.mjs +97 -0
  3. package/bin/gkm.mjs +7 -1
  4. package/dist/{HostingerProvider-h3EbEp-Z.cjs → HostingerProvider-BZFGYYVA.cjs} +21 -22
  5. package/dist/HostingerProvider-BZFGYYVA.cjs.map +1 -0
  6. package/dist/{HostingerProvider-Cft7lEYr.mjs → HostingerProvider-Bh6K7QYz.mjs} +20 -21
  7. package/dist/HostingerProvider-Bh6K7QYz.mjs.map +1 -0
  8. package/dist/{LocalStateProvider-Db9SEjyH.cjs → LocalStateProvider-7HR6SS2t.cjs} +11 -6
  9. package/dist/LocalStateProvider-7HR6SS2t.cjs.map +1 -0
  10. package/dist/{LocalStateProvider-Bn6tS-oL.mjs → LocalStateProvider-D7QyJoLM.mjs} +10 -4
  11. package/dist/LocalStateProvider-D7QyJoLM.mjs.map +1 -0
  12. package/dist/{openapi-CA0yJUTN.cjs → OpenApiTsGenerator-B4Kj_qNU.cjs} +142 -303
  13. package/dist/OpenApiTsGenerator-B4Kj_qNU.cjs.map +1 -0
  14. package/dist/{openapi-kkQbsRVj.mjs → OpenApiTsGenerator-CEh3EKN3.mjs} +113 -255
  15. package/dist/OpenApiTsGenerator-CEh3EKN3.mjs.map +1 -0
  16. package/dist/{Route53Provider-RdpUDJQk.mjs → Route53Provider-Cvj40A8I.mjs} +12 -12
  17. package/dist/Route53Provider-Cvj40A8I.mjs.map +1 -0
  18. package/dist/{Route53Provider-di2fMM5N.cjs → Route53Provider-DVILBsvV.cjs} +20 -21
  19. package/dist/Route53Provider-DVILBsvV.cjs.map +1 -0
  20. package/dist/{SSMStateProvider-BRt2py-A.mjs → SSMStateProvider-B0Hu1MKZ.mjs} +13 -4
  21. package/dist/SSMStateProvider-B0Hu1MKZ.mjs.map +1 -0
  22. package/dist/{SSMStateProvider-DyX8s9HI.cjs → SSMStateProvider-VrQ-yeqd.cjs} +17 -9
  23. package/dist/SSMStateProvider-VrQ-yeqd.cjs.map +1 -0
  24. package/dist/aws-DkHdTV-p.cjs +61 -0
  25. package/dist/aws-DkHdTV-p.cjs.map +1 -0
  26. package/dist/aws-DojzQnLd.mjs +61 -0
  27. package/dist/aws-DojzQnLd.mjs.map +1 -0
  28. package/dist/{backup-provisioner-BrsHwYG0.cjs → backup-provisioner-BmhIO_Yr.cjs} +21 -19
  29. package/dist/backup-provisioner-BmhIO_Yr.cjs.map +1 -0
  30. package/dist/{backup-provisioner-Bgj5Vi_L.mjs → backup-provisioner-DIVCbfee.mjs} +10 -7
  31. package/dist/backup-provisioner-DIVCbfee.mjs.map +1 -0
  32. package/dist/{bundler-Be8Zg4Wd.mjs → bundler--fyybf1N.mjs} +19 -30
  33. package/dist/bundler--fyybf1N.mjs.map +1 -0
  34. package/dist/{bundler-BY1OmV1e.cjs → bundler-By6_sXiL.cjs} +20 -32
  35. package/dist/bundler-By6_sXiL.cjs.map +1 -0
  36. package/dist/{config-WRdsQYZK.mjs → config-5watB1D0.mjs} +142 -154
  37. package/dist/config-5watB1D0.mjs.map +1 -0
  38. package/dist/{config-C21ocYNd.cjs → config-CJ-PAb7M.cjs} +209 -210
  39. package/dist/config-CJ-PAb7M.cjs.map +1 -0
  40. package/dist/config.cjs +9 -6
  41. package/dist/config.d.cts +62 -33
  42. package/dist/config.d.cts.map +1 -1
  43. package/dist/config.d.mts +62 -33
  44. package/dist/config.d.mts.map +1 -1
  45. package/dist/config.mjs +3 -5
  46. package/dist/deploy/sniffer-envkit-patch.cjs +18 -10
  47. package/dist/deploy/sniffer-envkit-patch.cjs.map +1 -1
  48. package/dist/deploy/sniffer-envkit-patch.d.cts +0 -2
  49. package/dist/deploy/sniffer-envkit-patch.d.cts.map +1 -1
  50. package/dist/deploy/sniffer-envkit-patch.d.mts +0 -2
  51. package/dist/deploy/sniffer-envkit-patch.d.mts.map +1 -1
  52. package/dist/deploy/sniffer-envkit-patch.mjs +10 -2
  53. package/dist/deploy/sniffer-envkit-patch.mjs.map +1 -1
  54. package/dist/deploy/sniffer-hooks.cjs +13 -7
  55. package/dist/deploy/sniffer-hooks.cjs.map +1 -1
  56. package/dist/deploy/sniffer-hooks.d.cts +1 -2
  57. package/dist/deploy/sniffer-hooks.d.cts.map +1 -1
  58. package/dist/deploy/sniffer-hooks.d.mts +1 -2
  59. package/dist/deploy/sniffer-hooks.d.mts.map +1 -1
  60. package/dist/deploy/sniffer-hooks.mjs +9 -2
  61. package/dist/deploy/sniffer-hooks.mjs.map +1 -1
  62. package/dist/deploy/sniffer-loader.cjs +16 -7
  63. package/dist/deploy/sniffer-loader.cjs.map +1 -1
  64. package/dist/deploy/sniffer-loader.d.cts +1 -1
  65. package/dist/deploy/sniffer-loader.d.mts +1 -1
  66. package/dist/deploy/sniffer-loader.mjs +13 -2
  67. package/dist/deploy/sniffer-loader.mjs.map +1 -1
  68. package/dist/deploy/sniffer-routes-worker.cjs +11 -11
  69. package/dist/deploy/sniffer-routes-worker.cjs.map +1 -1
  70. package/dist/deploy/sniffer-routes-worker.d.cts +1 -1
  71. package/dist/deploy/sniffer-routes-worker.d.mts +1 -1
  72. package/dist/deploy/sniffer-routes-worker.mjs +3 -3
  73. package/dist/deploy/sniffer-routes-worker.mjs.map +1 -1
  74. package/dist/deploy/sniffer-worker.cjs +15 -6
  75. package/dist/deploy/sniffer-worker.cjs.map +1 -1
  76. package/dist/deploy/sniffer-worker.d.cts +1 -3
  77. package/dist/deploy/sniffer-worker.d.cts.map +1 -1
  78. package/dist/deploy/sniffer-worker.d.mts +1 -3
  79. package/dist/deploy/sniffer-worker.d.mts.map +1 -1
  80. package/dist/deploy/sniffer-worker.mjs +15 -4
  81. package/dist/deploy/sniffer-worker.mjs.map +1 -1
  82. package/dist/{discover-DTmFeloL.cjs → discover-B0f_kuNN.cjs} +118 -66
  83. package/dist/discover-B0f_kuNN.cjs.map +1 -0
  84. package/dist/{discover-CAlFgYec.mjs → discover-BLhf7V_J.mjs} +67 -22
  85. package/dist/discover-BLhf7V_J.mjs.map +1 -0
  86. package/dist/{emit-CbcRolsa.cjs → emit-BdoPFjZf.cjs} +53 -36
  87. package/dist/emit-BdoPFjZf.cjs.map +1 -0
  88. package/dist/{emit-CiZS_5Ub.mjs → emit-Bzq7JsMP.mjs} +24 -6
  89. package/dist/emit-Bzq7JsMP.mjs.map +1 -0
  90. package/dist/github-BAr8WGkR.cjs +213 -0
  91. package/dist/github-BAr8WGkR.cjs.map +1 -0
  92. package/dist/github-D5MthUlR.mjs +213 -0
  93. package/dist/github-D5MthUlR.mjs.map +1 -0
  94. package/dist/{index-DCtipn2h.d.cts → index-B3PeGOQp.d.cts} +4 -23
  95. package/dist/index-B3PeGOQp.d.cts.map +1 -0
  96. package/dist/{index-BThUzEIp.d.mts → index-BOyaGcCm.d.mts} +4 -23
  97. package/dist/index-BOyaGcCm.d.mts.map +1 -0
  98. package/dist/index.cjs +7071 -4519
  99. package/dist/index.cjs.map +1 -1
  100. package/dist/index.d.cts +1 -1
  101. package/dist/index.d.mts +1 -1
  102. package/dist/index.mjs +7117 -4586
  103. package/dist/index.mjs.map +1 -1
  104. package/dist/migrate-BVI3yqcE.mjs +736 -0
  105. package/dist/migrate-BVI3yqcE.mjs.map +1 -0
  106. package/dist/migrate-CFOUApUt.cjs +743 -0
  107. package/dist/migrate-CFOUApUt.cjs.map +1 -0
  108. package/dist/openapi.cjs +197 -8
  109. package/dist/openapi.cjs.map +1 -0
  110. package/dist/openapi.d.cts +47 -14
  111. package/dist/openapi.d.cts.map +1 -1
  112. package/dist/openapi.d.mts +47 -14
  113. package/dist/openapi.d.mts.map +1 -1
  114. package/dist/openapi.mjs +191 -5
  115. package/dist/openapi.mjs.map +1 -0
  116. package/dist/ready-D8UXsP6q.mjs +516 -0
  117. package/dist/ready-D8UXsP6q.mjs.map +1 -0
  118. package/dist/ready-mcxamf73.cjs +563 -0
  119. package/dist/ready-mcxamf73.cjs.map +1 -0
  120. package/dist/reconcile/public.cjs +4 -4
  121. package/dist/reconcile/public.d.cts +27 -26
  122. package/dist/reconcile/public.d.cts.map +1 -1
  123. package/dist/reconcile/public.d.mts +27 -26
  124. package/dist/reconcile/public.d.mts.map +1 -1
  125. package/dist/reconcile/public.mjs +3 -4
  126. package/dist/{reconcile-CFs8C_1a.mjs → reconcile--I7V4Klj.mjs} +4 -7
  127. package/dist/reconcile--I7V4Klj.mjs.map +1 -0
  128. package/dist/{reconcile-D0pimNiy.cjs → reconcile-CuCQBa5-.cjs} +5 -8
  129. package/dist/reconcile-CuCQBa5-.cjs.map +1 -0
  130. package/dist/rolldown-runtime-BhDjJH2R.mjs +15 -0
  131. package/dist/{chunk-CUT6urMc.cjs → rolldown-runtime-C0BPl7ul.cjs} +23 -10
  132. package/dist/storage-BzaUxmqe.mjs +124 -0
  133. package/dist/storage-BzaUxmqe.mjs.map +1 -0
  134. package/dist/storage-CSGoC3bK.cjs +152 -0
  135. package/dist/storage-CSGoC3bK.cjs.map +1 -0
  136. package/dist/store-DQgxT_WR.cjs +211 -0
  137. package/dist/store-DQgxT_WR.cjs.map +1 -0
  138. package/dist/store-DeGATCS-.mjs +182 -0
  139. package/dist/store-DeGATCS-.mjs.map +1 -0
  140. package/dist/types-0R124hrY.d.mts +6324 -0
  141. package/dist/types-0R124hrY.d.mts.map +1 -0
  142. package/dist/types-DfdYe31u.d.cts +6325 -0
  143. package/dist/types-DfdYe31u.d.cts.map +1 -0
  144. package/dist/vitest.cjs +65 -0
  145. package/dist/vitest.cjs.map +1 -0
  146. package/dist/vitest.d.cts +29 -0
  147. package/dist/vitest.d.cts.map +1 -0
  148. package/dist/vitest.d.mts +29 -0
  149. package/dist/vitest.d.mts.map +1 -0
  150. package/dist/vitest.mjs +65 -0
  151. package/dist/vitest.mjs.map +1 -0
  152. package/dist/workspace/index.cjs +3 -4
  153. package/dist/workspace/index.d.cts +3 -3
  154. package/dist/workspace/index.d.mts +3 -3
  155. package/dist/workspace/index.mjs +2 -3
  156. package/dist/{credentials-G1JwBoXH.cjs → workspace-BIqeeqcR.cjs} +2248 -1809
  157. package/dist/workspace-BIqeeqcR.cjs.map +1 -0
  158. package/dist/{workspace-BFMrAKqI.mjs → workspace-DOmIhiau.mjs} +863 -952
  159. package/dist/workspace-DOmIhiau.mjs.map +1 -0
  160. package/dist/{credentials-BBIRJVp4.mjs → workspace-DwS1JK5u.mjs} +2114 -1706
  161. package/dist/workspace-DwS1JK5u.mjs.map +1 -0
  162. package/dist/{workspace-BcucjwvA.cjs → workspace-RYSYtHP7.cjs} +1032 -1098
  163. package/dist/workspace-RYSYtHP7.cjs.map +1 -0
  164. package/package.json +29 -20
  165. package/dist/CachedStateProvider-BLo7zv7S.cjs +0 -3
  166. package/dist/CachedStateProvider-BxoAm-B2.mjs +0 -3
  167. package/dist/CachedStateProvider-CqPdW7CB.cjs +0 -60
  168. package/dist/CachedStateProvider-CqPdW7CB.cjs.map +0 -1
  169. package/dist/CachedStateProvider-DTniehMP.mjs +0 -54
  170. package/dist/CachedStateProvider-DTniehMP.mjs.map +0 -1
  171. package/dist/HostingerProvider-Cft7lEYr.mjs.map +0 -1
  172. package/dist/HostingerProvider-h3EbEp-Z.cjs.map +0 -1
  173. package/dist/LocalStateProvider-Bn6tS-oL.mjs.map +0 -1
  174. package/dist/LocalStateProvider-Db9SEjyH.cjs.map +0 -1
  175. package/dist/Route53Provider-RdpUDJQk.mjs.map +0 -1
  176. package/dist/Route53Provider-di2fMM5N.cjs.map +0 -1
  177. package/dist/SSMStateProvider-BRt2py-A.mjs.map +0 -1
  178. package/dist/SSMStateProvider-DyX8s9HI.cjs.map +0 -1
  179. package/dist/backup-provisioner-Bgj5Vi_L.mjs.map +0 -1
  180. package/dist/backup-provisioner-BrsHwYG0.cjs.map +0 -1
  181. package/dist/bundler-BY1OmV1e.cjs.map +0 -1
  182. package/dist/bundler-Be8Zg4Wd.mjs.map +0 -1
  183. package/dist/chunk-Duj1WY3L.mjs +0 -7
  184. package/dist/config-C21ocYNd.cjs.map +0 -1
  185. package/dist/config-WRdsQYZK.mjs.map +0 -1
  186. package/dist/credentials-B5zRHdAl.mjs +0 -141
  187. package/dist/credentials-B5zRHdAl.mjs.map +0 -1
  188. package/dist/credentials-BBIRJVp4.mjs.map +0 -1
  189. package/dist/credentials-BapzjyV5.cjs +0 -195
  190. package/dist/credentials-BapzjyV5.cjs.map +0 -1
  191. package/dist/credentials-G1JwBoXH.cjs.map +0 -1
  192. package/dist/discover-CAlFgYec.mjs.map +0 -1
  193. package/dist/discover-DTmFeloL.cjs.map +0 -1
  194. package/dist/dokploy-api-BS2xnJLZ.mjs +0 -3
  195. package/dist/dokploy-api-BsVH0y_s.cjs +0 -718
  196. package/dist/dokploy-api-BsVH0y_s.cjs.map +0 -1
  197. package/dist/dokploy-api-BuxDRo3l.cjs +0 -3
  198. package/dist/dokploy-api-aTFPz7_4.mjs +0 -706
  199. package/dist/dokploy-api-aTFPz7_4.mjs.map +0 -1
  200. package/dist/emit-CbcRolsa.cjs.map +0 -1
  201. package/dist/emit-CiZS_5Ub.mjs.map +0 -1
  202. package/dist/encryption-C6jaU0hE.cjs +0 -54
  203. package/dist/encryption-C6jaU0hE.cjs.map +0 -1
  204. package/dist/encryption-CHzzeR8a.cjs +0 -4
  205. package/dist/encryption-CQUncu7v.mjs +0 -3
  206. package/dist/encryption-D-abyhZx.mjs +0 -42
  207. package/dist/encryption-D-abyhZx.mjs.map +0 -1
  208. package/dist/fullstack-secrets-C-GGohke.cjs +0 -111
  209. package/dist/fullstack-secrets-C-GGohke.cjs.map +0 -1
  210. package/dist/fullstack-secrets-D1o9-G5l.mjs +0 -87
  211. package/dist/fullstack-secrets-D1o9-G5l.mjs.map +0 -1
  212. package/dist/generator-CNzBw7g5.cjs +0 -289
  213. package/dist/generator-CNzBw7g5.cjs.map +0 -1
  214. package/dist/generator-VE2JrFlY.mjs +0 -253
  215. package/dist/generator-VE2JrFlY.mjs.map +0 -1
  216. package/dist/index-BThUzEIp.d.mts.map +0 -1
  217. package/dist/index-DCtipn2h.d.cts.map +0 -1
  218. package/dist/openapi-CA0yJUTN.cjs.map +0 -1
  219. package/dist/openapi-kkQbsRVj.mjs.map +0 -1
  220. package/dist/openapi-react-query-Byltvm_7.mjs +0 -167
  221. package/dist/openapi-react-query-Byltvm_7.mjs.map +0 -1
  222. package/dist/openapi-react-query-CmJ1xDbn.cjs +0 -173
  223. package/dist/openapi-react-query-CmJ1xDbn.cjs.map +0 -1
  224. package/dist/openapi-react-query.cjs +0 -4
  225. package/dist/openapi-react-query.d.cts +0 -11
  226. package/dist/openapi-react-query.d.cts.map +0 -1
  227. package/dist/openapi-react-query.d.mts +0 -11
  228. package/dist/openapi-react-query.d.mts.map +0 -1
  229. package/dist/openapi-react-query.mjs +0 -4
  230. package/dist/reconcile-CFs8C_1a.mjs.map +0 -1
  231. package/dist/reconcile-D0pimNiy.cjs.map +0 -1
  232. package/dist/setup-B5_oHHUj.cjs +0 -11
  233. package/dist/setup-Cm2UnAdh.cjs +0 -473
  234. package/dist/setup-Cm2UnAdh.cjs.map +0 -1
  235. package/dist/setup-DXvRLTZN.mjs +0 -443
  236. package/dist/setup-DXvRLTZN.mjs.map +0 -1
  237. package/dist/setup-ZijS4qeC.mjs +0 -11
  238. package/dist/storage-BJmzYxt8.cjs +0 -363
  239. package/dist/storage-BJmzYxt8.cjs.map +0 -1
  240. package/dist/storage-BM9hGpgv.cjs +0 -7
  241. package/dist/storage-Bi24WfoW.mjs +0 -297
  242. package/dist/storage-Bi24WfoW.mjs.map +0 -1
  243. package/dist/storage-CYlGwWTj.mjs +0 -3
  244. package/dist/sync-CmEuNZsB.mjs +0 -76
  245. package/dist/sync-CmEuNZsB.mjs.map +0 -1
  246. package/dist/sync-D306yOWu.cjs +0 -4
  247. package/dist/sync-SizaChmC.cjs +0 -93
  248. package/dist/sync-SizaChmC.cjs.map +0 -1
  249. package/dist/sync-hOqAWHR6.mjs +0 -3
  250. package/dist/types-BhIl-COG.d.cts +0 -2184
  251. package/dist/types-BhIl-COG.d.cts.map +0 -1
  252. package/dist/types-C-arnQ1r.d.mts +0 -2184
  253. package/dist/types-C-arnQ1r.d.mts.map +0 -1
  254. package/dist/workspace-BFMrAKqI.mjs.map +0 -1
  255. package/dist/workspace-BcucjwvA.cjs.map +0 -1
  256. package/dist/workspace-DdJss8I6.cjs +0 -9
  257. package/dist/workspace-k4l3rsFh.mjs +0 -8
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SSMStateProvider-B0Hu1MKZ.mjs","names":[],"sources":["../src/deploy/SSMStateProvider.ts"],"sourcesContent":["/**\n * AWS SSM Parameter Store State Provider\n *\n * Stores deployment state as SecureString parameters in AWS SSM.\n * Uses AWS-managed KMS key for encryption (free tier).\n *\n * Parameter naming: /gkm/{workspaceName}/{stage}/state\n */\n\nimport {\n\tGetParameterCommand,\n\tParameterNotFound,\n\tPutParameterCommand,\n\tSSMClient,\n\ttype SSMClientConfig,\n} from '@aws-sdk/client-ssm';\nimport type { AwsRegion, StateProvider } from './StateProvider';\nimport type { DokployStageState } from './state';\n\nexport interface SSMStateProviderOptions {\n\t/** Workspace name (used in parameter path) */\n\tworkspaceName: string;\n\t/** AWS region */\n\tregion?: AwsRegion;\n\t/** AWS profile name (optional - uses default credential chain if not provided) */\n\tprofile?: string;\n\t/** AWS credentials (optional - uses default credential chain if not provided) */\n\tcredentials?: SSMClientConfig['credentials'];\n\t/** Custom endpoint (for LocalStack or other S3-compatible services) */\n\tendpoint?: string;\n}\n\n/**\n * AWS SSM Parameter Store state provider.\n *\n * Stores state as encrypted SecureString parameters.\n * Parameter path: /gkm/{workspaceName}/{stage}/state\n */\nexport class SSMStateProvider implements StateProvider {\n\tconstructor(\n\t\treadonly workspaceName: string,\n\t\tprivate readonly client: SSMClient,\n\t) {}\n\n\t/**\n\t * Create an SSMStateProvider with a new SSMClient.\n\t */\n\tstatic create(options: SSMStateProviderOptions): SSMStateProvider {\n\t\tconst clientConfig: SSMClientConfig = {\n\t\t\tregion: options.region,\n\t\t\tendpoint: options.endpoint,\n\t\t};\n\n\t\t// Use profile credentials if specified, otherwise use provided credentials or default chain\n\t\tif (options.profile) {\n\t\t\t// Dynamic import to avoid requiring @aws-sdk/credential-providers when not using profiles\n\t\t\tconst { fromIni } = require('@aws-sdk/credential-providers');\n\t\t\tclientConfig.credentials = fromIni({ profile: options.profile });\n\t\t} else if (options.credentials) {\n\t\t\tclientConfig.credentials = options.credentials;\n\t\t}\n\n\t\tconst client = new SSMClient(clientConfig);\n\t\treturn new SSMStateProvider(options.workspaceName, client);\n\t}\n\n\t/**\n\t * Get the SSM parameter name for a stage.\n\t */\n\tprivate getParameterName(stage: string): string {\n\t\treturn `/gkm/${this.workspaceName}/${stage}/state`;\n\t}\n\n\tasync read(stage: string): Promise<DokployStageState | null> {\n\t\tconst parameterName = this.getParameterName(stage);\n\n\t\ttry {\n\t\t\tconst response = await this.client.send(\n\t\t\t\tnew GetParameterCommand({\n\t\t\t\t\tName: parameterName,\n\t\t\t\t\tWithDecryption: true,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tif (!response.Parameter?.Value) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn JSON.parse(response.Parameter.Value) as DokployStageState;\n\t\t} catch (error) {\n\t\t\t// Parameter doesn't exist - return null (new deployment)\n\t\t\tif (error instanceof ParameterNotFound) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// Re-throw other errors (permission denied, network, etc.)\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync write(stage: string, state: DokployStageState): Promise<void> {\n\t\tconst parameterName = this.getParameterName(stage);\n\n\t\t// Update last deployed timestamp\n\t\tstate.lastDeployedAt = new Date().toISOString();\n\n\t\tawait this.client.send(\n\t\t\tnew PutParameterCommand({\n\t\t\t\tName: parameterName,\n\t\t\t\tValue: JSON.stringify(state),\n\t\t\t\tType: 'SecureString',\n\t\t\t\tOverwrite: true,\n\t\t\t\tDescription: `GKM deployment state for ${this.workspaceName}/${stage}`,\n\t\t\t}),\n\t\t);\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAsCA,IAAa,mBAAb,MAAa,iBAA0C;CAE5C;CACQ;CAFlB,YACC,eACA,QACC;EAFQ,KAAA,gBAAA;EACQ,KAAA,SAAA;CACf;;;;CAKH,OAAO,OAAO,SAAoD;EACjE,MAAM,eAAgC;GACrC,QAAQ,QAAQ;GAChB,UAAU,QAAQ;EACnB;EAGA,IAAI,QAAQ,SAAS;GAEpB,MAAM,EAAE,YAAA,UAAoB,+BAA+B;GAC3D,aAAa,cAAc,QAAQ,EAAE,SAAS,QAAQ,QAAQ,CAAC;EAChE,OAAO,IAAI,QAAQ,aAClB,aAAa,cAAc,QAAQ;EAGpC,MAAM,SAAS,IAAI,UAAU,YAAY;EACzC,OAAO,IAAI,iBAAiB,QAAQ,eAAe,MAAM;CAC1D;;;;CAKA,iBAAyB,OAAuB;EAC/C,OAAO,QAAQ,KAAK,cAAc,GAAG,MAAM;CAC5C;CAEA,MAAM,KAAK,OAAkD;EAC5D,MAAM,gBAAgB,KAAK,iBAAiB,KAAK;EAEjD,IAAI;GACH,MAAM,WAAW,MAAM,KAAK,OAAO,KAClC,IAAI,oBAAoB;IACvB,MAAM;IACN,gBAAgB;GACjB,CAAC,CACF;GAEA,IAAI,CAAC,SAAS,WAAW,OACxB,OAAO;GAGR,OAAO,KAAK,MAAM,SAAS,UAAU,KAAK;EAC3C,SAAS,OAAO;GAEf,IAAI,iBAAiB,mBACpB,OAAO;GAIR,MAAM;EACP;CACD;CAEA,MAAM,MAAM,OAAe,OAAyC;EACnE,MAAM,gBAAgB,KAAK,iBAAiB,KAAK;EAGjD,MAAM,kCAAiB,IAAI,KAAK,EAAA,CAAE,YAAY;EAE9C,MAAM,KAAK,OAAO,KACjB,IAAI,oBAAoB;GACvB,MAAM;GACN,OAAO,KAAK,UAAU,KAAK;GAC3B,MAAM;GACN,WAAW;GACX,aAAa,4BAA4B,KAAK,cAAc,GAAG;EAChE,CAAC,CACF;CACD;AACD"}
@@ -1,14 +1,22 @@
1
- const require_chunk = require('./chunk-CUT6urMc.cjs');
2
- const __aws_sdk_client_ssm = require_chunk.__toESM(require("@aws-sdk/client-ssm"));
3
-
1
+ let _aws_sdk_client_ssm = require("@aws-sdk/client-ssm");
4
2
  //#region src/deploy/SSMStateProvider.ts
5
3
  /**
4
+ * AWS SSM Parameter Store State Provider
5
+ *
6
+ * Stores deployment state as SecureString parameters in AWS SSM.
7
+ * Uses AWS-managed KMS key for encryption (free tier).
8
+ *
9
+ * Parameter naming: /gkm/{workspaceName}/{stage}/state
10
+ */
11
+ /**
6
12
  * AWS SSM Parameter Store state provider.
7
13
  *
8
14
  * Stores state as encrypted SecureString parameters.
9
15
  * Parameter path: /gkm/{workspaceName}/{stage}/state
10
16
  */
11
17
  var SSMStateProvider = class SSMStateProvider {
18
+ workspaceName;
19
+ client;
12
20
  constructor(workspaceName, client) {
13
21
  this.workspaceName = workspaceName;
14
22
  this.client = client;
@@ -25,7 +33,7 @@ var SSMStateProvider = class SSMStateProvider {
25
33
  const { fromIni } = require("@aws-sdk/credential-providers");
26
34
  clientConfig.credentials = fromIni({ profile: options.profile });
27
35
  } else if (options.credentials) clientConfig.credentials = options.credentials;
28
- const client = new __aws_sdk_client_ssm.SSMClient(clientConfig);
36
+ const client = new _aws_sdk_client_ssm.SSMClient(clientConfig);
29
37
  return new SSMStateProvider(options.workspaceName, client);
30
38
  }
31
39
  /**
@@ -37,21 +45,21 @@ var SSMStateProvider = class SSMStateProvider {
37
45
  async read(stage) {
38
46
  const parameterName = this.getParameterName(stage);
39
47
  try {
40
- const response = await this.client.send(new __aws_sdk_client_ssm.GetParameterCommand({
48
+ const response = await this.client.send(new _aws_sdk_client_ssm.GetParameterCommand({
41
49
  Name: parameterName,
42
50
  WithDecryption: true
43
51
  }));
44
52
  if (!response.Parameter?.Value) return null;
45
53
  return JSON.parse(response.Parameter.Value);
46
54
  } catch (error) {
47
- if (error instanceof __aws_sdk_client_ssm.ParameterNotFound) return null;
55
+ if (error instanceof _aws_sdk_client_ssm.ParameterNotFound) return null;
48
56
  throw error;
49
57
  }
50
58
  }
51
59
  async write(stage, state) {
52
60
  const parameterName = this.getParameterName(stage);
53
61
  state.lastDeployedAt = (/* @__PURE__ */ new Date()).toISOString();
54
- await this.client.send(new __aws_sdk_client_ssm.PutParameterCommand({
62
+ await this.client.send(new _aws_sdk_client_ssm.PutParameterCommand({
55
63
  Name: parameterName,
56
64
  Value: JSON.stringify(state),
57
65
  Type: "SecureString",
@@ -60,7 +68,7 @@ var SSMStateProvider = class SSMStateProvider {
60
68
  }));
61
69
  }
62
70
  };
63
-
64
71
  //#endregion
65
72
  exports.SSMStateProvider = SSMStateProvider;
66
- //# sourceMappingURL=SSMStateProvider-DyX8s9HI.cjs.map
73
+
74
+ //# sourceMappingURL=SSMStateProvider-VrQ-yeqd.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SSMStateProvider-VrQ-yeqd.cjs","names":["SSMClient","GetParameterCommand","ParameterNotFound","PutParameterCommand"],"sources":["../src/deploy/SSMStateProvider.ts"],"sourcesContent":["/**\n * AWS SSM Parameter Store State Provider\n *\n * Stores deployment state as SecureString parameters in AWS SSM.\n * Uses AWS-managed KMS key for encryption (free tier).\n *\n * Parameter naming: /gkm/{workspaceName}/{stage}/state\n */\n\nimport {\n\tGetParameterCommand,\n\tParameterNotFound,\n\tPutParameterCommand,\n\tSSMClient,\n\ttype SSMClientConfig,\n} from '@aws-sdk/client-ssm';\nimport type { AwsRegion, StateProvider } from './StateProvider';\nimport type { DokployStageState } from './state';\n\nexport interface SSMStateProviderOptions {\n\t/** Workspace name (used in parameter path) */\n\tworkspaceName: string;\n\t/** AWS region */\n\tregion?: AwsRegion;\n\t/** AWS profile name (optional - uses default credential chain if not provided) */\n\tprofile?: string;\n\t/** AWS credentials (optional - uses default credential chain if not provided) */\n\tcredentials?: SSMClientConfig['credentials'];\n\t/** Custom endpoint (for LocalStack or other S3-compatible services) */\n\tendpoint?: string;\n}\n\n/**\n * AWS SSM Parameter Store state provider.\n *\n * Stores state as encrypted SecureString parameters.\n * Parameter path: /gkm/{workspaceName}/{stage}/state\n */\nexport class SSMStateProvider implements StateProvider {\n\tconstructor(\n\t\treadonly workspaceName: string,\n\t\tprivate readonly client: SSMClient,\n\t) {}\n\n\t/**\n\t * Create an SSMStateProvider with a new SSMClient.\n\t */\n\tstatic create(options: SSMStateProviderOptions): SSMStateProvider {\n\t\tconst clientConfig: SSMClientConfig = {\n\t\t\tregion: options.region,\n\t\t\tendpoint: options.endpoint,\n\t\t};\n\n\t\t// Use profile credentials if specified, otherwise use provided credentials or default chain\n\t\tif (options.profile) {\n\t\t\t// Dynamic import to avoid requiring @aws-sdk/credential-providers when not using profiles\n\t\t\tconst { fromIni } = require('@aws-sdk/credential-providers');\n\t\t\tclientConfig.credentials = fromIni({ profile: options.profile });\n\t\t} else if (options.credentials) {\n\t\t\tclientConfig.credentials = options.credentials;\n\t\t}\n\n\t\tconst client = new SSMClient(clientConfig);\n\t\treturn new SSMStateProvider(options.workspaceName, client);\n\t}\n\n\t/**\n\t * Get the SSM parameter name for a stage.\n\t */\n\tprivate getParameterName(stage: string): string {\n\t\treturn `/gkm/${this.workspaceName}/${stage}/state`;\n\t}\n\n\tasync read(stage: string): Promise<DokployStageState | null> {\n\t\tconst parameterName = this.getParameterName(stage);\n\n\t\ttry {\n\t\t\tconst response = await this.client.send(\n\t\t\t\tnew GetParameterCommand({\n\t\t\t\t\tName: parameterName,\n\t\t\t\t\tWithDecryption: true,\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\tif (!response.Parameter?.Value) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn JSON.parse(response.Parameter.Value) as DokployStageState;\n\t\t} catch (error) {\n\t\t\t// Parameter doesn't exist - return null (new deployment)\n\t\t\tif (error instanceof ParameterNotFound) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// Re-throw other errors (permission denied, network, etc.)\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync write(stage: string, state: DokployStageState): Promise<void> {\n\t\tconst parameterName = this.getParameterName(stage);\n\n\t\t// Update last deployed timestamp\n\t\tstate.lastDeployedAt = new Date().toISOString();\n\n\t\tawait this.client.send(\n\t\t\tnew PutParameterCommand({\n\t\t\t\tName: parameterName,\n\t\t\t\tValue: JSON.stringify(state),\n\t\t\t\tType: 'SecureString',\n\t\t\t\tOverwrite: true,\n\t\t\t\tDescription: `GKM deployment state for ${this.workspaceName}/${stage}`,\n\t\t\t}),\n\t\t);\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAsCA,IAAa,mBAAb,MAAa,iBAA0C;CAE5C;CACQ;CAFlB,YACC,eACA,QACC;EAFQ,KAAA,gBAAA;EACQ,KAAA,SAAA;CACf;;;;CAKH,OAAO,OAAO,SAAoD;EACjE,MAAM,eAAgC;GACrC,QAAQ,QAAQ;GAChB,UAAU,QAAQ;EACnB;EAGA,IAAI,QAAQ,SAAS;GAEpB,MAAM,EAAE,YAAY,QAAQ,+BAA+B;GAC3D,aAAa,cAAc,QAAQ,EAAE,SAAS,QAAQ,QAAQ,CAAC;EAChE,OAAO,IAAI,QAAQ,aAClB,aAAa,cAAc,QAAQ;EAGpC,MAAM,SAAS,IAAIA,oBAAAA,UAAU,YAAY;EACzC,OAAO,IAAI,iBAAiB,QAAQ,eAAe,MAAM;CAC1D;;;;CAKA,iBAAyB,OAAuB;EAC/C,OAAO,QAAQ,KAAK,cAAc,GAAG,MAAM;CAC5C;CAEA,MAAM,KAAK,OAAkD;EAC5D,MAAM,gBAAgB,KAAK,iBAAiB,KAAK;EAEjD,IAAI;GACH,MAAM,WAAW,MAAM,KAAK,OAAO,KAClC,IAAIC,oBAAAA,oBAAoB;IACvB,MAAM;IACN,gBAAgB;GACjB,CAAC,CACF;GAEA,IAAI,CAAC,SAAS,WAAW,OACxB,OAAO;GAGR,OAAO,KAAK,MAAM,SAAS,UAAU,KAAK;EAC3C,SAAS,OAAO;GAEf,IAAI,iBAAiBC,oBAAAA,mBACpB,OAAO;GAIR,MAAM;EACP;CACD;CAEA,MAAM,MAAM,OAAe,OAAyC;EACnE,MAAM,gBAAgB,KAAK,iBAAiB,KAAK;EAGjD,MAAM,kCAAiB,IAAI,KAAK,EAAA,CAAE,YAAY;EAE9C,MAAM,KAAK,OAAO,KACjB,IAAIC,oBAAAA,oBAAoB;GACvB,MAAM;GACN,OAAO,KAAK,UAAU,KAAK;GAC3B,MAAM;GACN,WAAW;GACX,aAAa,4BAA4B,KAAK,cAAc,GAAG;EAChE,CAAC,CACF;CACD;AACD"}
@@ -0,0 +1,61 @@
1
+ let _aws_sdk_client_ssm = require("@aws-sdk/client-ssm");
2
+ //#region src/secrets/aws.ts
3
+ /**
4
+ * A stage's secrets in SSM Parameter Store, in the stage's own AWS account.
5
+ *
6
+ * One parameter per stage, `/gkm/<project>/<stage>/secrets`, a SecureString —
7
+ * encrypted at rest by KMS, so nothing about it needs a key file anywhere.
8
+ * Which account it is in is whichever credentials are active: the stage's
9
+ * OIDC role in a deploy job, or `--profile` on a developer's machine. With
10
+ * staging and production in different accounts, each stage's secrets sit
11
+ * beside its infrastructure, and a staging role cannot read production's.
12
+ */
13
+ /** The parameter a stage's secrets are kept in. */
14
+ function secretsParameterName(project, stage) {
15
+ return `/gkm/${project}/${stage}/secrets`;
16
+ }
17
+ var AwsSecretsStore = class {
18
+ options;
19
+ name = "ssm";
20
+ client;
21
+ constructor(options) {
22
+ this.options = options;
23
+ }
24
+ async ssm() {
25
+ if (this.client) return this.client;
26
+ const config = { region: this.options.region };
27
+ if (this.options.profile) {
28
+ const { fromIni } = await import("@aws-sdk/credential-providers");
29
+ config.credentials = fromIni({ profile: this.options.profile });
30
+ }
31
+ if (this.options.endpoint) config.endpoint = this.options.endpoint;
32
+ this.client = new _aws_sdk_client_ssm.SSMClient(config);
33
+ return this.client;
34
+ }
35
+ async read(stage) {
36
+ const ssm = await this.ssm();
37
+ try {
38
+ const { Parameter } = await ssm.send(new _aws_sdk_client_ssm.GetParameterCommand({
39
+ Name: secretsParameterName(this.options.project, stage),
40
+ WithDecryption: true
41
+ }));
42
+ return Parameter?.Value ? JSON.parse(Parameter.Value) : null;
43
+ } catch (error) {
44
+ if (error instanceof _aws_sdk_client_ssm.ParameterNotFound) return null;
45
+ throw error;
46
+ }
47
+ }
48
+ async write(stage, secrets) {
49
+ await (await this.ssm()).send(new _aws_sdk_client_ssm.PutParameterCommand({
50
+ Name: secretsParameterName(this.options.project, stage),
51
+ Value: JSON.stringify(secrets),
52
+ Type: "SecureString",
53
+ Overwrite: true,
54
+ Description: `gkm secrets for ${this.options.project}/${stage}`
55
+ }));
56
+ }
57
+ };
58
+ //#endregion
59
+ exports.AwsSecretsStore = AwsSecretsStore;
60
+
61
+ //# sourceMappingURL=aws-DkHdTV-p.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aws-DkHdTV-p.cjs","names":["SSMClient","GetParameterCommand","ParameterNotFound","PutParameterCommand"],"sources":["../src/secrets/aws.ts"],"sourcesContent":["/**\n * A stage's secrets in SSM Parameter Store, in the stage's own AWS account.\n *\n * One parameter per stage, `/gkm/<project>/<stage>/secrets`, a SecureString —\n * encrypted at rest by KMS, so nothing about it needs a key file anywhere.\n * Which account it is in is whichever credentials are active: the stage's\n * OIDC role in a deploy job, or `--profile` on a developer's machine. With\n * staging and production in different accounts, each stage's secrets sit\n * beside its infrastructure, and a staging role cannot read production's.\n */\n\nimport {\n\tGetParameterCommand,\n\tParameterNotFound,\n\tPutParameterCommand,\n\tSSMClient,\n\ttype SSMClientConfig,\n} from '@aws-sdk/client-ssm';\nimport type { SecretsStore } from './store.js';\nimport type { StageSecrets } from './types.js';\n\nexport interface AwsSecretsStoreOptions {\n\t/** The workspace name, which scopes the parameter path. */\n\tproject: string;\n\tregion: string;\n\t/**\n\t * A named AWS profile, resolved with `fromIni` only. The default chain\n\t * reads AWS_* from the environment first, so exported staging keys would\n\t * win over `--profile prod` and put production's secrets in staging.\n\t */\n\tprofile?: string;\n\t/** For tests: an endpoint such as the AWS emulator. */\n\tendpoint?: string;\n}\n\n/** The parameter a stage's secrets are kept in. */\nexport function secretsParameterName(project: string, stage: string): string {\n\treturn `/gkm/${project}/${stage}/secrets`;\n}\n\nexport class AwsSecretsStore implements SecretsStore {\n\treadonly name = 'ssm';\n\n\tprivate client?: SSMClient;\n\n\tconstructor(private readonly options: AwsSecretsStoreOptions) {}\n\n\tprivate async ssm(): Promise<SSMClient> {\n\t\tif (this.client) return this.client;\n\n\t\tconst config: SSMClientConfig = { region: this.options.region };\n\t\tif (this.options.profile) {\n\t\t\tconst { fromIni } = await import('@aws-sdk/credential-providers');\n\t\t\tconfig.credentials = fromIni({ profile: this.options.profile });\n\t\t}\n\t\tif (this.options.endpoint) {\n\t\t\tconfig.endpoint = this.options.endpoint;\n\t\t}\n\n\t\tthis.client = new SSMClient(config);\n\t\treturn this.client;\n\t}\n\n\tasync read(stage: string): Promise<StageSecrets | null> {\n\t\tconst ssm = await this.ssm();\n\n\t\ttry {\n\t\t\tconst { Parameter } = await ssm.send(\n\t\t\t\tnew GetParameterCommand({\n\t\t\t\t\tName: secretsParameterName(this.options.project, stage),\n\t\t\t\t\tWithDecryption: true,\n\t\t\t\t}),\n\t\t\t);\n\t\t\treturn Parameter?.Value\n\t\t\t\t? (JSON.parse(Parameter.Value) as StageSecrets)\n\t\t\t\t: null;\n\t\t} catch (error) {\n\t\t\tif (error instanceof ParameterNotFound) return null;\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync write(stage: string, secrets: StageSecrets): Promise<void> {\n\t\tconst ssm = await this.ssm();\n\n\t\tawait ssm.send(\n\t\t\tnew PutParameterCommand({\n\t\t\t\tName: secretsParameterName(this.options.project, stage),\n\t\t\t\tValue: JSON.stringify(secrets),\n\t\t\t\tType: 'SecureString',\n\t\t\t\tOverwrite: true,\n\t\t\t\tDescription: `gkm secrets for ${this.options.project}/${stage}`,\n\t\t\t}),\n\t\t);\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;AAoCA,SAAgB,qBAAqB,SAAiB,OAAuB;CAC5E,OAAO,QAAQ,QAAQ,GAAG,MAAM;AACjC;AAEA,IAAa,kBAAb,MAAqD;CAKvB;CAJ7B,OAAgB;CAEhB;CAEA,YAAY,SAAkD;EAAjC,KAAA,UAAA;CAAkC;CAE/D,MAAc,MAA0B;EACvC,IAAI,KAAK,QAAQ,OAAO,KAAK;EAE7B,MAAM,SAA0B,EAAE,QAAQ,KAAK,QAAQ,OAAO;EAC9D,IAAI,KAAK,QAAQ,SAAS;GACzB,MAAM,EAAE,YAAY,MAAM,OAAO;GACjC,OAAO,cAAc,QAAQ,EAAE,SAAS,KAAK,QAAQ,QAAQ,CAAC;EAC/D;EACA,IAAI,KAAK,QAAQ,UAChB,OAAO,WAAW,KAAK,QAAQ;EAGhC,KAAK,SAAS,IAAIA,oBAAAA,UAAU,MAAM;EAClC,OAAO,KAAK;CACb;CAEA,MAAM,KAAK,OAA6C;EACvD,MAAM,MAAM,MAAM,KAAK,IAAI;EAE3B,IAAI;GACH,MAAM,EAAE,cAAc,MAAM,IAAI,KAC/B,IAAIC,oBAAAA,oBAAoB;IACvB,MAAM,qBAAqB,KAAK,QAAQ,SAAS,KAAK;IACtD,gBAAgB;GACjB,CAAC,CACF;GACA,OAAO,WAAW,QACd,KAAK,MAAM,UAAU,KAAK,IAC3B;EACJ,SAAS,OAAO;GACf,IAAI,iBAAiBC,oBAAAA,mBAAmB,OAAO;GAC/C,MAAM;EACP;CACD;CAEA,MAAM,MAAM,OAAe,SAAsC;EAGhE,OAAM,MAFY,KAAK,IAAI,EAAA,CAEjB,KACT,IAAIC,oBAAAA,oBAAoB;GACvB,MAAM,qBAAqB,KAAK,QAAQ,SAAS,KAAK;GACtD,OAAO,KAAK,UAAU,OAAO;GAC7B,MAAM;GACN,WAAW;GACX,aAAa,mBAAmB,KAAK,QAAQ,QAAQ,GAAG;EACzD,CAAC,CACF;CACD;AACD"}
@@ -0,0 +1,61 @@
1
+ import { GetParameterCommand, ParameterNotFound, PutParameterCommand, SSMClient } from "@aws-sdk/client-ssm";
2
+ //#region src/secrets/aws.ts
3
+ /**
4
+ * A stage's secrets in SSM Parameter Store, in the stage's own AWS account.
5
+ *
6
+ * One parameter per stage, `/gkm/<project>/<stage>/secrets`, a SecureString —
7
+ * encrypted at rest by KMS, so nothing about it needs a key file anywhere.
8
+ * Which account it is in is whichever credentials are active: the stage's
9
+ * OIDC role in a deploy job, or `--profile` on a developer's machine. With
10
+ * staging and production in different accounts, each stage's secrets sit
11
+ * beside its infrastructure, and a staging role cannot read production's.
12
+ */
13
+ /** The parameter a stage's secrets are kept in. */
14
+ function secretsParameterName(project, stage) {
15
+ return `/gkm/${project}/${stage}/secrets`;
16
+ }
17
+ var AwsSecretsStore = class {
18
+ options;
19
+ name = "ssm";
20
+ client;
21
+ constructor(options) {
22
+ this.options = options;
23
+ }
24
+ async ssm() {
25
+ if (this.client) return this.client;
26
+ const config = { region: this.options.region };
27
+ if (this.options.profile) {
28
+ const { fromIni } = await import("@aws-sdk/credential-providers");
29
+ config.credentials = fromIni({ profile: this.options.profile });
30
+ }
31
+ if (this.options.endpoint) config.endpoint = this.options.endpoint;
32
+ this.client = new SSMClient(config);
33
+ return this.client;
34
+ }
35
+ async read(stage) {
36
+ const ssm = await this.ssm();
37
+ try {
38
+ const { Parameter } = await ssm.send(new GetParameterCommand({
39
+ Name: secretsParameterName(this.options.project, stage),
40
+ WithDecryption: true
41
+ }));
42
+ return Parameter?.Value ? JSON.parse(Parameter.Value) : null;
43
+ } catch (error) {
44
+ if (error instanceof ParameterNotFound) return null;
45
+ throw error;
46
+ }
47
+ }
48
+ async write(stage, secrets) {
49
+ await (await this.ssm()).send(new PutParameterCommand({
50
+ Name: secretsParameterName(this.options.project, stage),
51
+ Value: JSON.stringify(secrets),
52
+ Type: "SecureString",
53
+ Overwrite: true,
54
+ Description: `gkm secrets for ${this.options.project}/${stage}`
55
+ }));
56
+ }
57
+ };
58
+ //#endregion
59
+ export { AwsSecretsStore };
60
+
61
+ //# sourceMappingURL=aws-DojzQnLd.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aws-DojzQnLd.mjs","names":[],"sources":["../src/secrets/aws.ts"],"sourcesContent":["/**\n * A stage's secrets in SSM Parameter Store, in the stage's own AWS account.\n *\n * One parameter per stage, `/gkm/<project>/<stage>/secrets`, a SecureString —\n * encrypted at rest by KMS, so nothing about it needs a key file anywhere.\n * Which account it is in is whichever credentials are active: the stage's\n * OIDC role in a deploy job, or `--profile` on a developer's machine. With\n * staging and production in different accounts, each stage's secrets sit\n * beside its infrastructure, and a staging role cannot read production's.\n */\n\nimport {\n\tGetParameterCommand,\n\tParameterNotFound,\n\tPutParameterCommand,\n\tSSMClient,\n\ttype SSMClientConfig,\n} from '@aws-sdk/client-ssm';\nimport type { SecretsStore } from './store.js';\nimport type { StageSecrets } from './types.js';\n\nexport interface AwsSecretsStoreOptions {\n\t/** The workspace name, which scopes the parameter path. */\n\tproject: string;\n\tregion: string;\n\t/**\n\t * A named AWS profile, resolved with `fromIni` only. The default chain\n\t * reads AWS_* from the environment first, so exported staging keys would\n\t * win over `--profile prod` and put production's secrets in staging.\n\t */\n\tprofile?: string;\n\t/** For tests: an endpoint such as the AWS emulator. */\n\tendpoint?: string;\n}\n\n/** The parameter a stage's secrets are kept in. */\nexport function secretsParameterName(project: string, stage: string): string {\n\treturn `/gkm/${project}/${stage}/secrets`;\n}\n\nexport class AwsSecretsStore implements SecretsStore {\n\treadonly name = 'ssm';\n\n\tprivate client?: SSMClient;\n\n\tconstructor(private readonly options: AwsSecretsStoreOptions) {}\n\n\tprivate async ssm(): Promise<SSMClient> {\n\t\tif (this.client) return this.client;\n\n\t\tconst config: SSMClientConfig = { region: this.options.region };\n\t\tif (this.options.profile) {\n\t\t\tconst { fromIni } = await import('@aws-sdk/credential-providers');\n\t\t\tconfig.credentials = fromIni({ profile: this.options.profile });\n\t\t}\n\t\tif (this.options.endpoint) {\n\t\t\tconfig.endpoint = this.options.endpoint;\n\t\t}\n\n\t\tthis.client = new SSMClient(config);\n\t\treturn this.client;\n\t}\n\n\tasync read(stage: string): Promise<StageSecrets | null> {\n\t\tconst ssm = await this.ssm();\n\n\t\ttry {\n\t\t\tconst { Parameter } = await ssm.send(\n\t\t\t\tnew GetParameterCommand({\n\t\t\t\t\tName: secretsParameterName(this.options.project, stage),\n\t\t\t\t\tWithDecryption: true,\n\t\t\t\t}),\n\t\t\t);\n\t\t\treturn Parameter?.Value\n\t\t\t\t? (JSON.parse(Parameter.Value) as StageSecrets)\n\t\t\t\t: null;\n\t\t} catch (error) {\n\t\t\tif (error instanceof ParameterNotFound) return null;\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync write(stage: string, secrets: StageSecrets): Promise<void> {\n\t\tconst ssm = await this.ssm();\n\n\t\tawait ssm.send(\n\t\t\tnew PutParameterCommand({\n\t\t\t\tName: secretsParameterName(this.options.project, stage),\n\t\t\t\tValue: JSON.stringify(secrets),\n\t\t\t\tType: 'SecureString',\n\t\t\t\tOverwrite: true,\n\t\t\t\tDescription: `gkm secrets for ${this.options.project}/${stage}`,\n\t\t\t}),\n\t\t);\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;AAoCA,SAAgB,qBAAqB,SAAiB,OAAuB;CAC5E,OAAO,QAAQ,QAAQ,GAAG,MAAM;AACjC;AAEA,IAAa,kBAAb,MAAqD;CAKvB;CAJ7B,OAAgB;CAEhB;CAEA,YAAY,SAAkD;EAAjC,KAAA,UAAA;CAAkC;CAE/D,MAAc,MAA0B;EACvC,IAAI,KAAK,QAAQ,OAAO,KAAK;EAE7B,MAAM,SAA0B,EAAE,QAAQ,KAAK,QAAQ,OAAO;EAC9D,IAAI,KAAK,QAAQ,SAAS;GACzB,MAAM,EAAE,YAAY,MAAM,OAAO;GACjC,OAAO,cAAc,QAAQ,EAAE,SAAS,KAAK,QAAQ,QAAQ,CAAC;EAC/D;EACA,IAAI,KAAK,QAAQ,UAChB,OAAO,WAAW,KAAK,QAAQ;EAGhC,KAAK,SAAS,IAAI,UAAU,MAAM;EAClC,OAAO,KAAK;CACb;CAEA,MAAM,KAAK,OAA6C;EACvD,MAAM,MAAM,MAAM,KAAK,IAAI;EAE3B,IAAI;GACH,MAAM,EAAE,cAAc,MAAM,IAAI,KAC/B,IAAI,oBAAoB;IACvB,MAAM,qBAAqB,KAAK,QAAQ,SAAS,KAAK;IACtD,gBAAgB;GACjB,CAAC,CACF;GACA,OAAO,WAAW,QACd,KAAK,MAAM,UAAU,KAAK,IAC3B;EACJ,SAAS,OAAO;GACf,IAAI,iBAAiB,mBAAmB,OAAO;GAC/C,MAAM;EACP;CACD;CAEA,MAAM,MAAM,OAAe,SAAsC;EAGhE,OAAM,MAFY,KAAK,IAAI,EAAA,CAEjB,KACT,IAAI,oBAAoB;GACvB,MAAM,qBAAqB,KAAK,QAAQ,SAAS,KAAK;GACtD,OAAO,KAAK,UAAU,OAAO;GAC7B,MAAM;GACN,WAAW;GACX,aAAa,mBAAmB,KAAK,QAAQ,QAAQ,GAAG;EACzD,CAAC,CACF;CACD;AACD"}
@@ -1,9 +1,13 @@
1
- const require_chunk = require('./chunk-CUT6urMc.cjs');
2
- const __aws_sdk_client_s3 = require_chunk.__toESM(require("@aws-sdk/client-s3"));
3
- const __aws_sdk_client_iam = require_chunk.__toESM(require("@aws-sdk/client-iam"));
4
-
1
+ let _aws_sdk_client_s3 = require("@aws-sdk/client-s3");
2
+ let _aws_sdk_client_iam = require("@aws-sdk/client-iam");
5
3
  //#region src/deploy/backup-provisioner.ts
6
4
  /**
5
+ * Backup Destination Provisioner
6
+ *
7
+ * Creates AWS resources (S3 bucket, IAM user, access keys) and configures
8
+ * Dokploy backup destinations for database backups.
9
+ */
10
+ /**
7
11
  * Generate a random suffix for unique resource names
8
12
  */
9
13
  function randomSuffix() {
@@ -33,8 +37,8 @@ async function createAwsClients(region, profile, endpoint) {
33
37
  };
34
38
  }
35
39
  return {
36
- s3: new __aws_sdk_client_s3.S3Client(config),
37
- iam: new __aws_sdk_client_iam.IAMClient(config)
40
+ s3: new _aws_sdk_client_s3.S3Client(config),
41
+ iam: new _aws_sdk_client_iam.IAMClient(config)
38
42
  };
39
43
  }
40
44
  /**
@@ -42,7 +46,7 @@ async function createAwsClients(region, profile, endpoint) {
42
46
  */
43
47
  async function bucketExists(s3, bucketName) {
44
48
  try {
45
- await s3.send(new __aws_sdk_client_s3.HeadBucketCommand({ Bucket: bucketName }));
49
+ await s3.send(new _aws_sdk_client_s3.HeadBucketCommand({ Bucket: bucketName }));
46
50
  return true;
47
51
  } catch (error) {
48
52
  if (error.name === "NotFound") return false;
@@ -55,7 +59,7 @@ async function bucketExists(s3, bucketName) {
55
59
  */
56
60
  async function userExists(iam, userName) {
57
61
  try {
58
- await iam.send(new __aws_sdk_client_iam.GetUserCommand({ UserName: userName }));
62
+ await iam.send(new _aws_sdk_client_iam.GetUserCommand({ UserName: userName }));
59
63
  return true;
60
64
  } catch (error) {
61
65
  const errorName = error.name;
@@ -81,22 +85,20 @@ async function provisionBackupDestination(options) {
81
85
  const aws = await createAwsClients(config.region, config.profile, awsEndpoint);
82
86
  const sanitizedProject = sanitizeName(projectName);
83
87
  const bucketName = existingState?.bucketName ?? `${sanitizedProject}-${stage}-backups-${randomSuffix()}`;
84
- const bucketAlreadyExists = await bucketExists(aws.s3, bucketName);
85
- if (!bucketAlreadyExists) {
88
+ if (!await bucketExists(aws.s3, bucketName)) {
86
89
  logger.log(` Creating S3 bucket: ${bucketName}`);
87
90
  const createBucketParams = { Bucket: bucketName };
88
91
  if (config.region !== "us-east-1") createBucketParams.CreateBucketConfiguration = { LocationConstraint: config.region };
89
- await aws.s3.send(new __aws_sdk_client_s3.CreateBucketCommand(createBucketParams));
90
- await aws.s3.send(new __aws_sdk_client_s3.PutBucketVersioningCommand({
92
+ await aws.s3.send(new _aws_sdk_client_s3.CreateBucketCommand(createBucketParams));
93
+ await aws.s3.send(new _aws_sdk_client_s3.PutBucketVersioningCommand({
91
94
  Bucket: bucketName,
92
95
  VersioningConfiguration: { Status: "Enabled" }
93
96
  }));
94
97
  } else logger.log(` Using existing S3 bucket: ${bucketName}`);
95
98
  const iamUserName = existingState?.iamUserName ?? `dokploy-backup-${sanitizedProject}-${stage}`;
96
- const iamUserAlreadyExists = await userExists(aws.iam, iamUserName);
97
- if (!iamUserAlreadyExists) {
99
+ if (!await userExists(aws.iam, iamUserName)) {
98
100
  logger.log(` Creating IAM user: ${iamUserName}`);
99
- await aws.iam.send(new __aws_sdk_client_iam.CreateUserCommand({ UserName: iamUserName }));
101
+ await aws.iam.send(new _aws_sdk_client_iam.CreateUserCommand({ UserName: iamUserName }));
100
102
  } else logger.log(` Using existing IAM user: ${iamUserName}`);
101
103
  const policyDocument = {
102
104
  Version: "2012-10-17",
@@ -113,7 +115,7 @@ async function provisionBackupDestination(options) {
113
115
  }]
114
116
  };
115
117
  logger.log(" Updating IAM policy");
116
- await aws.iam.send(new __aws_sdk_client_iam.PutUserPolicyCommand({
118
+ await aws.iam.send(new _aws_sdk_client_iam.PutUserPolicyCommand({
117
119
  UserName: iamUserName,
118
120
  PolicyName: "DokployBackupAccess",
119
121
  PolicyDocument: JSON.stringify(policyDocument)
@@ -126,7 +128,7 @@ async function provisionBackupDestination(options) {
126
128
  secretAccessKey = existingState.iamSecretAccessKey;
127
129
  } else {
128
130
  logger.log(" Creating IAM access key");
129
- const accessKeyResult = await aws.iam.send(new __aws_sdk_client_iam.CreateAccessKeyCommand({ UserName: iamUserName }));
131
+ const accessKeyResult = await aws.iam.send(new _aws_sdk_client_iam.CreateAccessKeyCommand({ UserName: iamUserName }));
130
132
  if (!accessKeyResult.AccessKey) throw new Error("Failed to create IAM access key");
131
133
  accessKeyId = accessKeyResult.AccessKey.AccessKeyId;
132
134
  secretAccessKey = accessKeyResult.AccessKey.SecretAccessKey;
@@ -158,7 +160,7 @@ async function provisionBackupDestination(options) {
158
160
  createdAt: existingState?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
159
161
  };
160
162
  }
161
-
162
163
  //#endregion
163
164
  exports.provisionBackupDestination = provisionBackupDestination;
164
- //# sourceMappingURL=backup-provisioner-BrsHwYG0.cjs.map
165
+
166
+ //# sourceMappingURL=backup-provisioner-BmhIO_Yr.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup-provisioner-BmhIO_Yr.cjs","names":["S3Client","IAMClient","HeadBucketCommand","GetUserCommand","CreateBucketCommand","PutBucketVersioningCommand","CreateUserCommand","PutUserPolicyCommand","CreateAccessKeyCommand"],"sources":["../src/deploy/backup-provisioner.ts"],"sourcesContent":["/**\n * Backup Destination Provisioner\n *\n * Creates AWS resources (S3 bucket, IAM user, access keys) and configures\n * Dokploy backup destinations for database backups.\n */\n\nimport {\n\tCreateAccessKeyCommand,\n\tCreateUserCommand,\n\tGetUserCommand,\n\tIAMClient,\n\ttype IAMClientConfig,\n\tPutUserPolicyCommand,\n} from '@aws-sdk/client-iam';\nimport {\n\ttype BucketLocationConstraint,\n\tCreateBucketCommand,\n\tHeadBucketCommand,\n\tPutBucketVersioningCommand,\n\tS3Client,\n\ttype S3ClientConfig,\n} from '@aws-sdk/client-s3';\nimport type { BackupsConfig } from '../workspace/types.js';\nimport type { DokployApi } from './dokploy-api.js';\nimport type { BackupState } from './state.js';\n\nexport interface ProvisionBackupOptions {\n\t/** Dokploy API client */\n\tapi: DokployApi;\n\t/** Dokploy project ID */\n\tprojectId: string;\n\t/** Workspace name (used for resource naming) */\n\tprojectName: string;\n\t/** Deploy stage (e.g., 'production', 'staging') */\n\tstage: string;\n\t/** Backup configuration */\n\tconfig: BackupsConfig;\n\t/** Existing backup state (if any) */\n\texistingState?: BackupState;\n\t/** Logger for progress output */\n\tlogger: { log: (msg: string) => void };\n\t/** AWS endpoint override (for testing with LocalStack) */\n\tawsEndpoint?: string;\n}\n\n/**\n * Generate a random suffix for unique resource names\n */\nfunction randomSuffix(): string {\n\treturn Math.random().toString(36).substring(2, 8);\n}\n\n/**\n * Sanitize a name for AWS resources (lowercase alphanumeric and hyphens)\n */\nfunction sanitizeName(name: string): string {\n\treturn name.toLowerCase().replace(/[^a-z0-9-]/g, '-');\n}\n\n/**\n * Create AWS clients with optional profile credentials\n */\nasync function createAwsClients(\n\tregion: string,\n\tprofile?: string,\n\tendpoint?: string,\n): Promise<{ s3: S3Client; iam: IAMClient }> {\n\tconst config: S3ClientConfig & IAMClientConfig = { region };\n\n\tif (profile) {\n\t\tconst { fromIni } = await import('@aws-sdk/credential-providers');\n\t\tconfig.credentials = fromIni({ profile });\n\t}\n\n\t// Support custom endpoint for testing (e.g., LocalStack)\n\tif (endpoint) {\n\t\tconfig.endpoint = endpoint;\n\t\t(config as S3ClientConfig).forcePathStyle = true;\n\t\t// Use test credentials when endpoint is specified\n\t\tconfig.credentials = {\n\t\t\taccessKeyId: 'test',\n\t\t\tsecretAccessKey: 'test',\n\t\t};\n\t}\n\n\treturn {\n\t\ts3: new S3Client(config),\n\t\tiam: new IAMClient(config),\n\t};\n}\n\n/**\n * Check if an S3 bucket exists\n */\nasync function bucketExists(\n\ts3: S3Client,\n\tbucketName: string,\n): Promise<boolean> {\n\ttry {\n\t\tawait s3.send(new HeadBucketCommand({ Bucket: bucketName }));\n\t\treturn true;\n\t} catch (error) {\n\t\tif ((error as { name?: string }).name === 'NotFound') {\n\t\t\treturn false;\n\t\t}\n\t\t// 403 means bucket exists but we don't have access\n\t\tif (\n\t\t\t(error as { $metadata?: { httpStatusCode?: number } }).$metadata\n\t\t\t\t?.httpStatusCode === 403\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Check if an IAM user exists\n */\nasync function userExists(iam: IAMClient, userName: string): Promise<boolean> {\n\ttry {\n\t\tawait iam.send(new GetUserCommand({ UserName: userName }));\n\t\treturn true;\n\t} catch (error) {\n\t\tconst errorName = (error as { name?: string }).name;\n\t\t// AWS returns 'NoSuchEntity', LocalStack returns 'NoSuchEntityException'\n\t\tif (errorName === 'NoSuchEntity' || errorName === 'NoSuchEntityException') {\n\t\t\treturn false;\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Provision backup destination for a deployment.\n *\n * Creates AWS resources (S3 bucket, IAM user) and Dokploy destination if needed.\n * Reuses existing resources from state when possible.\n */\nexport async function provisionBackupDestination(\n\toptions: ProvisionBackupOptions,\n): Promise<BackupState> {\n\tconst {\n\t\tapi,\n\t\tprojectName,\n\t\tstage,\n\t\tconfig,\n\t\texistingState,\n\t\tlogger,\n\t\tawsEndpoint,\n\t} = options;\n\n\t// If we have existing state, verify the Dokploy destination still exists\n\tif (existingState?.destinationId) {\n\t\ttry {\n\t\t\tawait api.getDestination(existingState.destinationId);\n\t\t\tlogger.log(' Using existing backup destination');\n\t\t\treturn existingState;\n\t\t} catch {\n\t\t\tlogger.log(' Existing destination not found, recreating...');\n\t\t}\n\t}\n\n\t// Create AWS clients\n\tconst aws = await createAwsClients(\n\t\tconfig.region,\n\t\tconfig.profile,\n\t\tawsEndpoint,\n\t);\n\tconst sanitizedProject = sanitizeName(projectName);\n\n\t// 1. Create or verify S3 bucket\n\tconst bucketName =\n\t\texistingState?.bucketName ??\n\t\t`${sanitizedProject}-${stage}-backups-${randomSuffix()}`;\n\n\tconst bucketAlreadyExists = await bucketExists(aws.s3, bucketName);\n\tif (!bucketAlreadyExists) {\n\t\tlogger.log(` Creating S3 bucket: ${bucketName}`);\n\n\t\t// CreateBucket needs LocationConstraint for non-us-east-1 regions\n\t\tconst createBucketParams: {\n\t\t\tBucket: string;\n\t\t\tCreateBucketConfiguration?: {\n\t\t\t\tLocationConstraint: BucketLocationConstraint;\n\t\t\t};\n\t\t} = {\n\t\t\tBucket: bucketName,\n\t\t};\n\t\tif (config.region !== 'us-east-1') {\n\t\t\tcreateBucketParams.CreateBucketConfiguration = {\n\t\t\t\tLocationConstraint: config.region as BucketLocationConstraint,\n\t\t\t};\n\t\t}\n\n\t\tawait aws.s3.send(new CreateBucketCommand(createBucketParams));\n\n\t\t// Enable versioning for backup integrity\n\t\tawait aws.s3.send(\n\t\t\tnew PutBucketVersioningCommand({\n\t\t\t\tBucket: bucketName,\n\t\t\t\tVersioningConfiguration: { Status: 'Enabled' },\n\t\t\t}),\n\t\t);\n\t} else {\n\t\tlogger.log(` Using existing S3 bucket: ${bucketName}`);\n\t}\n\n\t// 2. Create or verify IAM user\n\tconst iamUserName =\n\t\texistingState?.iamUserName ?? `dokploy-backup-${sanitizedProject}-${stage}`;\n\n\tconst iamUserAlreadyExists = await userExists(aws.iam, iamUserName);\n\tif (!iamUserAlreadyExists) {\n\t\tlogger.log(` Creating IAM user: ${iamUserName}`);\n\t\tawait aws.iam.send(new CreateUserCommand({ UserName: iamUserName }));\n\t} else {\n\t\tlogger.log(` Using existing IAM user: ${iamUserName}`);\n\t}\n\n\t// 3. Attach bucket policy to IAM user\n\tconst policyDocument = {\n\t\tVersion: '2012-10-17',\n\t\tStatement: [\n\t\t\t{\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: [\n\t\t\t\t\t's3:GetObject',\n\t\t\t\t\t's3:PutObject',\n\t\t\t\t\t's3:DeleteObject',\n\t\t\t\t\t's3:ListBucket',\n\t\t\t\t\t's3:GetBucketLocation',\n\t\t\t\t],\n\t\t\t\tResource: [\n\t\t\t\t\t`arn:aws:s3:::${bucketName}`,\n\t\t\t\t\t`arn:aws:s3:::${bucketName}/*`,\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t};\n\n\tlogger.log(' Updating IAM policy');\n\tawait aws.iam.send(\n\t\tnew PutUserPolicyCommand({\n\t\t\tUserName: iamUserName,\n\t\t\tPolicyName: 'DokployBackupAccess',\n\t\t\tPolicyDocument: JSON.stringify(policyDocument),\n\t\t}),\n\t);\n\n\t// 4. Create access key (or reuse existing if state has it and destination needs recreation)\n\tlet accessKeyId: string;\n\tlet secretAccessKey: string;\n\n\tif (existingState?.iamAccessKeyId && existingState?.iamSecretAccessKey) {\n\t\t// Reuse existing credentials\n\t\tlogger.log(' Using existing IAM access key');\n\t\taccessKeyId = existingState.iamAccessKeyId;\n\t\tsecretAccessKey = existingState.iamSecretAccessKey;\n\t} else {\n\t\t// Create new access key\n\t\tlogger.log(' Creating IAM access key');\n\t\tconst accessKeyResult = await aws.iam.send(\n\t\t\tnew CreateAccessKeyCommand({ UserName: iamUserName }),\n\t\t);\n\n\t\tif (!accessKeyResult.AccessKey) {\n\t\t\tthrow new Error('Failed to create IAM access key');\n\t\t}\n\n\t\taccessKeyId = accessKeyResult.AccessKey.AccessKeyId!;\n\t\tsecretAccessKey = accessKeyResult.AccessKey.SecretAccessKey!;\n\t}\n\n\t// 5. Create Dokploy destination\n\tconst destinationName = `${sanitizedProject}-${stage}-s3`;\n\tlogger.log(` Creating Dokploy destination: ${destinationName}`);\n\n\tconst { destination, created } = await api.findOrCreateDestination(\n\t\tdestinationName,\n\t\t{\n\t\t\taccessKey: accessKeyId,\n\t\t\tsecretAccessKey: secretAccessKey,\n\t\t\tbucket: bucketName,\n\t\t\tregion: config.region,\n\t\t},\n\t);\n\n\tif (created) {\n\t\tlogger.log(' ✓ Dokploy destination created');\n\t} else {\n\t\tlogger.log(' ✓ Using existing Dokploy destination');\n\t}\n\n\t// 6. Test connection\n\ttry {\n\t\tawait api.testDestinationConnection(destination.destinationId);\n\t\tlogger.log(' ✓ Destination connection verified');\n\t} catch (error) {\n\t\tlogger.log(\n\t\t\t` ⚠ Warning: Could not verify destination connection: ${error}`,\n\t\t);\n\t}\n\n\treturn {\n\t\tbucketName,\n\t\tbucketArn: `arn:aws:s3:::${bucketName}`,\n\t\tiamUserName,\n\t\tiamAccessKeyId: accessKeyId,\n\t\tiamSecretAccessKey: secretAccessKey,\n\t\tdestinationId: destination.destinationId,\n\t\tregion: config.region,\n\t\tcreatedAt: existingState?.createdAt ?? new Date().toISOString(),\n\t};\n}\n"],"mappings":"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"}
@@ -1,8 +1,13 @@
1
1
  import { CreateBucketCommand, HeadBucketCommand, PutBucketVersioningCommand, S3Client } from "@aws-sdk/client-s3";
2
2
  import { CreateAccessKeyCommand, CreateUserCommand, GetUserCommand, IAMClient, PutUserPolicyCommand } from "@aws-sdk/client-iam";
3
-
4
3
  //#region src/deploy/backup-provisioner.ts
5
4
  /**
5
+ * Backup Destination Provisioner
6
+ *
7
+ * Creates AWS resources (S3 bucket, IAM user, access keys) and configures
8
+ * Dokploy backup destinations for database backups.
9
+ */
10
+ /**
6
11
  * Generate a random suffix for unique resource names
7
12
  */
8
13
  function randomSuffix() {
@@ -80,8 +85,7 @@ async function provisionBackupDestination(options) {
80
85
  const aws = await createAwsClients(config.region, config.profile, awsEndpoint);
81
86
  const sanitizedProject = sanitizeName(projectName);
82
87
  const bucketName = existingState?.bucketName ?? `${sanitizedProject}-${stage}-backups-${randomSuffix()}`;
83
- const bucketAlreadyExists = await bucketExists(aws.s3, bucketName);
84
- if (!bucketAlreadyExists) {
88
+ if (!await bucketExists(aws.s3, bucketName)) {
85
89
  logger.log(` Creating S3 bucket: ${bucketName}`);
86
90
  const createBucketParams = { Bucket: bucketName };
87
91
  if (config.region !== "us-east-1") createBucketParams.CreateBucketConfiguration = { LocationConstraint: config.region };
@@ -92,8 +96,7 @@ async function provisionBackupDestination(options) {
92
96
  }));
93
97
  } else logger.log(` Using existing S3 bucket: ${bucketName}`);
94
98
  const iamUserName = existingState?.iamUserName ?? `dokploy-backup-${sanitizedProject}-${stage}`;
95
- const iamUserAlreadyExists = await userExists(aws.iam, iamUserName);
96
- if (!iamUserAlreadyExists) {
99
+ if (!await userExists(aws.iam, iamUserName)) {
97
100
  logger.log(` Creating IAM user: ${iamUserName}`);
98
101
  await aws.iam.send(new CreateUserCommand({ UserName: iamUserName }));
99
102
  } else logger.log(` Using existing IAM user: ${iamUserName}`);
@@ -157,7 +160,7 @@ async function provisionBackupDestination(options) {
157
160
  createdAt: existingState?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
158
161
  };
159
162
  }
160
-
161
163
  //#endregion
162
164
  export { provisionBackupDestination };
163
- //# sourceMappingURL=backup-provisioner-Bgj5Vi_L.mjs.map
165
+
166
+ //# sourceMappingURL=backup-provisioner-DIVCbfee.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup-provisioner-DIVCbfee.mjs","names":[],"sources":["../src/deploy/backup-provisioner.ts"],"sourcesContent":["/**\n * Backup Destination Provisioner\n *\n * Creates AWS resources (S3 bucket, IAM user, access keys) and configures\n * Dokploy backup destinations for database backups.\n */\n\nimport {\n\tCreateAccessKeyCommand,\n\tCreateUserCommand,\n\tGetUserCommand,\n\tIAMClient,\n\ttype IAMClientConfig,\n\tPutUserPolicyCommand,\n} from '@aws-sdk/client-iam';\nimport {\n\ttype BucketLocationConstraint,\n\tCreateBucketCommand,\n\tHeadBucketCommand,\n\tPutBucketVersioningCommand,\n\tS3Client,\n\ttype S3ClientConfig,\n} from '@aws-sdk/client-s3';\nimport type { BackupsConfig } from '../workspace/types.js';\nimport type { DokployApi } from './dokploy-api.js';\nimport type { BackupState } from './state.js';\n\nexport interface ProvisionBackupOptions {\n\t/** Dokploy API client */\n\tapi: DokployApi;\n\t/** Dokploy project ID */\n\tprojectId: string;\n\t/** Workspace name (used for resource naming) */\n\tprojectName: string;\n\t/** Deploy stage (e.g., 'production', 'staging') */\n\tstage: string;\n\t/** Backup configuration */\n\tconfig: BackupsConfig;\n\t/** Existing backup state (if any) */\n\texistingState?: BackupState;\n\t/** Logger for progress output */\n\tlogger: { log: (msg: string) => void };\n\t/** AWS endpoint override (for testing with LocalStack) */\n\tawsEndpoint?: string;\n}\n\n/**\n * Generate a random suffix for unique resource names\n */\nfunction randomSuffix(): string {\n\treturn Math.random().toString(36).substring(2, 8);\n}\n\n/**\n * Sanitize a name for AWS resources (lowercase alphanumeric and hyphens)\n */\nfunction sanitizeName(name: string): string {\n\treturn name.toLowerCase().replace(/[^a-z0-9-]/g, '-');\n}\n\n/**\n * Create AWS clients with optional profile credentials\n */\nasync function createAwsClients(\n\tregion: string,\n\tprofile?: string,\n\tendpoint?: string,\n): Promise<{ s3: S3Client; iam: IAMClient }> {\n\tconst config: S3ClientConfig & IAMClientConfig = { region };\n\n\tif (profile) {\n\t\tconst { fromIni } = await import('@aws-sdk/credential-providers');\n\t\tconfig.credentials = fromIni({ profile });\n\t}\n\n\t// Support custom endpoint for testing (e.g., LocalStack)\n\tif (endpoint) {\n\t\tconfig.endpoint = endpoint;\n\t\t(config as S3ClientConfig).forcePathStyle = true;\n\t\t// Use test credentials when endpoint is specified\n\t\tconfig.credentials = {\n\t\t\taccessKeyId: 'test',\n\t\t\tsecretAccessKey: 'test',\n\t\t};\n\t}\n\n\treturn {\n\t\ts3: new S3Client(config),\n\t\tiam: new IAMClient(config),\n\t};\n}\n\n/**\n * Check if an S3 bucket exists\n */\nasync function bucketExists(\n\ts3: S3Client,\n\tbucketName: string,\n): Promise<boolean> {\n\ttry {\n\t\tawait s3.send(new HeadBucketCommand({ Bucket: bucketName }));\n\t\treturn true;\n\t} catch (error) {\n\t\tif ((error as { name?: string }).name === 'NotFound') {\n\t\t\treturn false;\n\t\t}\n\t\t// 403 means bucket exists but we don't have access\n\t\tif (\n\t\t\t(error as { $metadata?: { httpStatusCode?: number } }).$metadata\n\t\t\t\t?.httpStatusCode === 403\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Check if an IAM user exists\n */\nasync function userExists(iam: IAMClient, userName: string): Promise<boolean> {\n\ttry {\n\t\tawait iam.send(new GetUserCommand({ UserName: userName }));\n\t\treturn true;\n\t} catch (error) {\n\t\tconst errorName = (error as { name?: string }).name;\n\t\t// AWS returns 'NoSuchEntity', LocalStack returns 'NoSuchEntityException'\n\t\tif (errorName === 'NoSuchEntity' || errorName === 'NoSuchEntityException') {\n\t\t\treturn false;\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Provision backup destination for a deployment.\n *\n * Creates AWS resources (S3 bucket, IAM user) and Dokploy destination if needed.\n * Reuses existing resources from state when possible.\n */\nexport async function provisionBackupDestination(\n\toptions: ProvisionBackupOptions,\n): Promise<BackupState> {\n\tconst {\n\t\tapi,\n\t\tprojectName,\n\t\tstage,\n\t\tconfig,\n\t\texistingState,\n\t\tlogger,\n\t\tawsEndpoint,\n\t} = options;\n\n\t// If we have existing state, verify the Dokploy destination still exists\n\tif (existingState?.destinationId) {\n\t\ttry {\n\t\t\tawait api.getDestination(existingState.destinationId);\n\t\t\tlogger.log(' Using existing backup destination');\n\t\t\treturn existingState;\n\t\t} catch {\n\t\t\tlogger.log(' Existing destination not found, recreating...');\n\t\t}\n\t}\n\n\t// Create AWS clients\n\tconst aws = await createAwsClients(\n\t\tconfig.region,\n\t\tconfig.profile,\n\t\tawsEndpoint,\n\t);\n\tconst sanitizedProject = sanitizeName(projectName);\n\n\t// 1. Create or verify S3 bucket\n\tconst bucketName =\n\t\texistingState?.bucketName ??\n\t\t`${sanitizedProject}-${stage}-backups-${randomSuffix()}`;\n\n\tconst bucketAlreadyExists = await bucketExists(aws.s3, bucketName);\n\tif (!bucketAlreadyExists) {\n\t\tlogger.log(` Creating S3 bucket: ${bucketName}`);\n\n\t\t// CreateBucket needs LocationConstraint for non-us-east-1 regions\n\t\tconst createBucketParams: {\n\t\t\tBucket: string;\n\t\t\tCreateBucketConfiguration?: {\n\t\t\t\tLocationConstraint: BucketLocationConstraint;\n\t\t\t};\n\t\t} = {\n\t\t\tBucket: bucketName,\n\t\t};\n\t\tif (config.region !== 'us-east-1') {\n\t\t\tcreateBucketParams.CreateBucketConfiguration = {\n\t\t\t\tLocationConstraint: config.region as BucketLocationConstraint,\n\t\t\t};\n\t\t}\n\n\t\tawait aws.s3.send(new CreateBucketCommand(createBucketParams));\n\n\t\t// Enable versioning for backup integrity\n\t\tawait aws.s3.send(\n\t\t\tnew PutBucketVersioningCommand({\n\t\t\t\tBucket: bucketName,\n\t\t\t\tVersioningConfiguration: { Status: 'Enabled' },\n\t\t\t}),\n\t\t);\n\t} else {\n\t\tlogger.log(` Using existing S3 bucket: ${bucketName}`);\n\t}\n\n\t// 2. Create or verify IAM user\n\tconst iamUserName =\n\t\texistingState?.iamUserName ?? `dokploy-backup-${sanitizedProject}-${stage}`;\n\n\tconst iamUserAlreadyExists = await userExists(aws.iam, iamUserName);\n\tif (!iamUserAlreadyExists) {\n\t\tlogger.log(` Creating IAM user: ${iamUserName}`);\n\t\tawait aws.iam.send(new CreateUserCommand({ UserName: iamUserName }));\n\t} else {\n\t\tlogger.log(` Using existing IAM user: ${iamUserName}`);\n\t}\n\n\t// 3. Attach bucket policy to IAM user\n\tconst policyDocument = {\n\t\tVersion: '2012-10-17',\n\t\tStatement: [\n\t\t\t{\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: [\n\t\t\t\t\t's3:GetObject',\n\t\t\t\t\t's3:PutObject',\n\t\t\t\t\t's3:DeleteObject',\n\t\t\t\t\t's3:ListBucket',\n\t\t\t\t\t's3:GetBucketLocation',\n\t\t\t\t],\n\t\t\t\tResource: [\n\t\t\t\t\t`arn:aws:s3:::${bucketName}`,\n\t\t\t\t\t`arn:aws:s3:::${bucketName}/*`,\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t};\n\n\tlogger.log(' Updating IAM policy');\n\tawait aws.iam.send(\n\t\tnew PutUserPolicyCommand({\n\t\t\tUserName: iamUserName,\n\t\t\tPolicyName: 'DokployBackupAccess',\n\t\t\tPolicyDocument: JSON.stringify(policyDocument),\n\t\t}),\n\t);\n\n\t// 4. Create access key (or reuse existing if state has it and destination needs recreation)\n\tlet accessKeyId: string;\n\tlet secretAccessKey: string;\n\n\tif (existingState?.iamAccessKeyId && existingState?.iamSecretAccessKey) {\n\t\t// Reuse existing credentials\n\t\tlogger.log(' Using existing IAM access key');\n\t\taccessKeyId = existingState.iamAccessKeyId;\n\t\tsecretAccessKey = existingState.iamSecretAccessKey;\n\t} else {\n\t\t// Create new access key\n\t\tlogger.log(' Creating IAM access key');\n\t\tconst accessKeyResult = await aws.iam.send(\n\t\t\tnew CreateAccessKeyCommand({ UserName: iamUserName }),\n\t\t);\n\n\t\tif (!accessKeyResult.AccessKey) {\n\t\t\tthrow new Error('Failed to create IAM access key');\n\t\t}\n\n\t\taccessKeyId = accessKeyResult.AccessKey.AccessKeyId!;\n\t\tsecretAccessKey = accessKeyResult.AccessKey.SecretAccessKey!;\n\t}\n\n\t// 5. Create Dokploy destination\n\tconst destinationName = `${sanitizedProject}-${stage}-s3`;\n\tlogger.log(` Creating Dokploy destination: ${destinationName}`);\n\n\tconst { destination, created } = await api.findOrCreateDestination(\n\t\tdestinationName,\n\t\t{\n\t\t\taccessKey: accessKeyId,\n\t\t\tsecretAccessKey: secretAccessKey,\n\t\t\tbucket: bucketName,\n\t\t\tregion: config.region,\n\t\t},\n\t);\n\n\tif (created) {\n\t\tlogger.log(' ✓ Dokploy destination created');\n\t} else {\n\t\tlogger.log(' ✓ Using existing Dokploy destination');\n\t}\n\n\t// 6. Test connection\n\ttry {\n\t\tawait api.testDestinationConnection(destination.destinationId);\n\t\tlogger.log(' ✓ Destination connection verified');\n\t} catch (error) {\n\t\tlogger.log(\n\t\t\t` ⚠ Warning: Could not verify destination connection: ${error}`,\n\t\t);\n\t}\n\n\treturn {\n\t\tbucketName,\n\t\tbucketArn: `arn:aws:s3:::${bucketName}`,\n\t\tiamUserName,\n\t\tiamAccessKeyId: accessKeyId,\n\t\tiamSecretAccessKey: secretAccessKey,\n\t\tdestinationId: destination.destinationId,\n\t\tregion: config.region,\n\t\tcreatedAt: existingState?.createdAt ?? new Date().toISOString(),\n\t};\n}\n"],"mappings":"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"}
@@ -1,8 +1,7 @@
1
1
  import { createRequire } from "node:module";
2
- import { dirname, join } from "node:path";
3
2
  import { mkdir, writeFile } from "node:fs/promises";
3
+ import { dirname, join } from "node:path";
4
4
  import { spawnSync } from "node:child_process";
5
-
6
5
  //#region src/build/bundler.ts
7
6
  /**
8
7
  * Banner to inject into ESM bundle for CJS compatibility.
@@ -62,14 +61,8 @@ async function collectRequiredEnvVars(constructs) {
62
61
  * @param options - Bundle configuration options
63
62
  * @returns Bundle result with output path and optional master key
64
63
  */
65
- /** Default env var values for docker compose services */
66
- const DOCKER_SERVICE_ENV_VARS = {
67
- postgres: { DATABASE_URL: "postgresql://postgres:postgres@postgres:5432/app" },
68
- redis: { REDIS_URL: "redis://redis:6379" },
69
- rabbitmq: { RABBITMQ_URL: "amqp://rabbitmq:5672" }
70
- };
71
64
  async function bundleServer(options) {
72
- const { entryPoint, outputDir, minify, sourcemap, external, stage, constructs, dockerServices } = options;
65
+ const { entryPoint, outputDir, minify, sourcemap, external, stage, constructs } = options;
73
66
  await mkdir(outputDir, { recursive: true });
74
67
  const mjsOutput = join(outputDir, "server.mjs");
75
68
  const args = [
@@ -88,23 +81,20 @@ async function bundleServer(options) {
88
81
  for (const ext of external) args.push(`--external:${ext}`);
89
82
  let masterKey;
90
83
  if (stage) {
91
- const { readStageSecrets, toEmbeddableSecrets, validateEnvironmentVariables, initStageSecrets, writeStageSecrets } = await import("./storage-CYlGwWTj.mjs");
92
- const { encryptSecrets, generateDefineOptions } = await import("./encryption-CQUncu7v.mjs");
93
- let secrets = await readStageSecrets(stage);
84
+ const { toEmbeddableSecrets, validateEnvironmentVariables, initStageSecrets } = await import("./storage-BzaUxmqe.mjs").then((n) => n.r);
85
+ const { FileSecretsStore, secretsStoreFor } = await import("./store-DeGATCS-.mjs").then((n) => n.r);
86
+ const { ConfigNotFound, loadWorkspaceSettings } = await import("./config.mjs");
87
+ const { encryptSecrets, generateDefineOptions } = await import("./index.mjs").then((n) => n.i);
88
+ const store = await loadWorkspaceSettings().then((workspace) => secretsStoreFor(workspace, stage), (error) => {
89
+ if (error instanceof ConfigNotFound) return new FileSecretsStore(process.cwd());
90
+ throw error;
91
+ });
92
+ let secrets = await store.read(stage);
94
93
  if (!secrets) {
95
94
  console.log(` Initializing secrets for stage "${stage}"...`);
96
95
  secrets = initStageSecrets(stage);
97
- await writeStageSecrets(secrets);
98
- console.log(` ✓ Created .gkm/secrets/${stage}.json`);
99
- }
100
- if (dockerServices) {
101
- for (const [service, enabled] of Object.entries(dockerServices)) if (enabled && DOCKER_SERVICE_ENV_VARS[service]) for (const [envVar, defaultValue] of Object.entries(DOCKER_SERVICE_ENV_VARS[service])) {
102
- const urlKey = envVar;
103
- if (!secrets.urls[urlKey] && !secrets.custom[envVar]) {
104
- secrets.urls[urlKey] = defaultValue;
105
- console.log(` Auto-populated ${envVar} from docker compose`);
106
- }
107
- }
96
+ await store.write(stage, secrets);
97
+ console.log(` ✓ Created the "${stage}" secrets (${store.name})`);
108
98
  }
109
99
  if (constructs && constructs.length > 0) {
110
100
  console.log(" Analyzing environment variable requirements...");
@@ -118,7 +108,7 @@ async function bundleServer(options) {
118
108
  ...validation.missing.map((v) => ` ❌ ${v}`),
119
109
  "",
120
110
  "To fix this, either:",
121
- ` 1. Add the missing variables to .gkm/secrets/${stage}.json using:`,
111
+ ` 1. Add the missing variables to the "${stage}" secrets using:`,
122
112
  ` gkm secrets:set <KEY> <VALUE> --stage ${stage}`,
123
113
  "",
124
114
  ` 2. Or import from a JSON file:`,
@@ -132,8 +122,7 @@ async function bundleServer(options) {
132
122
  console.log(` ✓ All ${requiredVars.length} required environment variables found`);
133
123
  }
134
124
  }
135
- const embeddable = toEmbeddableSecrets(secrets);
136
- const encrypted = encryptSecrets(embeddable);
125
+ const encrypted = encryptSecrets(toEmbeddableSecrets(secrets));
137
126
  masterKey = encrypted.masterKey;
138
127
  const defines = generateDefineOptions(encrypted);
139
128
  for (const [key, value] of Object.entries(defines)) args.push(`--define:${key}=${JSON.stringify(value)}`);
@@ -148,8 +137,8 @@ async function bundleServer(options) {
148
137
  });
149
138
  if (result.error) throw result.error;
150
139
  if (result.status !== 0) throw new Error(`esbuild exited with code ${result.status}`);
151
- const { readFile: readFile$1 } = await import("node:fs/promises");
152
- const content = await readFile$1(mjsOutput, "utf-8");
140
+ const { readFile } = await import("node:fs/promises");
141
+ const content = await readFile(mjsOutput, "utf-8");
153
142
  if (!content.startsWith("#!")) await writeFile(mjsOutput, `#!/usr/bin/env node\n${content}`);
154
143
  } catch (error) {
155
144
  throw new Error(`Failed to bundle server: ${error instanceof Error ? error.message : "Unknown error"}`);
@@ -159,7 +148,7 @@ async function bundleServer(options) {
159
148
  masterKey
160
149
  };
161
150
  }
162
-
163
151
  //#endregion
164
152
  export { bundleServer };
165
- //# sourceMappingURL=bundler-Be8Zg4Wd.mjs.map
153
+
154
+ //# sourceMappingURL=bundler--fyybf1N.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundler--fyybf1N.mjs","names":[],"sources":["../src/build/bundler.ts"],"sourcesContent":["import { spawnSync } from 'node:child_process';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { createRequire } from 'node:module';\nimport { dirname, join } from 'node:path';\nimport type { Construct } from '@geekmidas/constructs';\n\n/**\n * Banner to inject into ESM bundle for CJS compatibility.\n * Creates a `require` function using Node's createRequire for packages\n * that internally use CommonJS require() for Node builtins.\n */\nconst ESM_CJS_COMPAT_BANNER =\n\t'import { createRequire } from \"module\"; const require = createRequire(import.meta.url);';\n\nexport interface BundleOptions {\n\t/** Entry point file (e.g., .gkm/server/server.ts) */\n\tentryPoint: string;\n\t/** Output directory for bundled files */\n\toutputDir: string;\n\t/** Minify the output (default: true) */\n\tminify: boolean;\n\t/** Generate sourcemaps (default: false) */\n\tsourcemap: boolean;\n\t/** Packages to exclude from bundling */\n\texternal: string[];\n\t/** Stage for secrets injection (optional) */\n\tstage?: string;\n\t/** Constructs to validate environment variables for */\n\tconstructs?: Construct[];\n}\n\nexport interface BundleResult {\n\t/** Path to the bundled output */\n\toutputPath: string;\n\t/** Ephemeral master key for deployment (only if stage was provided) */\n\tmasterKey?: string;\n}\n\n/**\n * The esbuild binary, resolved from this package rather than from PATH.\n *\n * It used to be `npx esbuild`, which is two assumptions: that esbuild is\n * installed somewhere npx can find, and that the *app's* directory is where to\n * look. Neither holds — the CLI did not depend on esbuild at all, so a deploy\n * run from an app that had not installed it failed at the bundle step with\n * `sh: esbuild: command not found`, after provisioning had already happened.\n *\n * The same fix `bin/gkm.mjs` makes for tsx: resolve from this package's own\n * `node_modules`, so the tool the CLI shells out to is the one it depends on.\n */\nfunction esbuildBinary(): string {\n\tconst require = createRequire(import.meta.url);\n\n\ttry {\n\t\t// The package root, then its bin — `require.resolve('esbuild')` gives the\n\t\t// JS entry point, which is a library rather than the CLI.\n\t\tconst pkg = require.resolve('esbuild/package.json');\n\t\treturn join(dirname(pkg), 'bin', 'esbuild');\n\t} catch {\n\t\t// Better a PATH lookup than a hard failure: a project that installed\n\t\t// esbuild itself still works.\n\t\treturn 'esbuild';\n\t}\n}\n\n/**\n * Collect the environment variables a build cannot proceed without.\n *\n * Uses the SnifferEnvironmentParser to detect which env vars each service\n * reads, and keeps only the ones it cannot do without. `markOptional` is what\n * separates them: the sniffer already knows which reads went through\n * `.optional()` or `.default()`, and asking for that distinction is the\n * difference between a build that stops and one that proceeds correctly.\n *\n * Without it, a key that is *absent by design* failed the build. A surface\n * publishes `AUTH_COOKIE_DOMAIN` only when there is a domain to widen a cookie\n * to — one host has nothing to share it with, so the derivation correctly\n * yields nothing, and Better Auth reads it as optional for exactly that\n * reason. Treating every sniffed read as required made the honest answer\n * indistinguishable from a missing secret.\n *\n * @param constructs - Array of constructs to analyze\n * @returns Deduplicated, sorted names of the required variables\n */\nasync function collectRequiredEnvVars(\n\tconstructs: Construct[],\n): Promise<string[]> {\n\t// A key read optionally by one construct and required by another stays\n\t// required: the strictest read is the one that fails at runtime, and taking\n\t// the union of the required reads is what says so.\n\tconst required = new Set<string>();\n\n\tfor (const construct of constructs) {\n\t\tfor (const name of await construct.getEnvironment({ markOptional: true })) {\n\t\t\tif (!name.endsWith('?')) required.add(name);\n\t\t}\n\t}\n\n\treturn Array.from(required).sort();\n}\n\n/**\n * Bundle the server application using esbuild.\n * Creates a fully standalone bundle with all dependencies included.\n *\n * @param options - Bundle configuration options\n * @returns Bundle result with output path and optional master key\n */\n\nexport async function bundleServer(\n\toptions: BundleOptions,\n): Promise<BundleResult> {\n\tconst {\n\t\tentryPoint,\n\t\toutputDir,\n\t\tminify,\n\t\tsourcemap,\n\t\texternal,\n\t\tstage,\n\t\tconstructs,\n\t} = options;\n\n\t// Ensure output directory exists\n\tawait mkdir(outputDir, { recursive: true });\n\n\tconst mjsOutput = join(outputDir, 'server.mjs');\n\n\t// Build command-line arguments for esbuild\n\tconst args = [\n\t\tesbuildBinary(),\n\t\tentryPoint,\n\t\t'--bundle',\n\t\t'--platform=node',\n\t\t'--target=node22',\n\t\t'--format=esm',\n\t\t`--outfile=${mjsOutput}`,\n\t\t'--packages=bundle', // Bundle all dependencies for standalone output\n\t\t`--banner:js=${ESM_CJS_COMPAT_BANNER}`, // CJS compatibility for packages like pino\n\t];\n\n\tif (minify) {\n\t\targs.push('--minify');\n\t}\n\n\tif (sourcemap) {\n\t\targs.push('--sourcemap');\n\t}\n\n\t// Add external packages (user-specified)\n\tfor (const ext of external) {\n\t\targs.push(`--external:${ext}`);\n\t}\n\n\t// Handle secrets injection if stage is provided\n\tlet masterKey: string | undefined;\n\n\tif (stage) {\n\t\tconst {\n\t\t\ttoEmbeddableSecrets,\n\t\t\tvalidateEnvironmentVariables,\n\t\t\tinitStageSecrets,\n\t\t} = await import('../secrets/storage');\n\t\tconst { FileSecretsStore, secretsStoreFor } = await import(\n\t\t\t'../secrets/store'\n\t\t);\n\t\tconst { ConfigNotFound, loadWorkspaceSettings } = await import('../config');\n\t\tconst { encryptSecrets, generateDefineOptions } = await import(\n\t\t\t'../secrets/encryption'\n\t\t);\n\n\t\t// The stage's own store — SSM for a deployed stage kept there. Outside\n\t\t// any workspace nothing names one, so it is the file.\n\t\tconst store = await loadWorkspaceSettings().then(\n\t\t\t(workspace) => secretsStoreFor(workspace, stage),\n\t\t\t(error) => {\n\t\t\t\tif (error instanceof ConfigNotFound) {\n\t\t\t\t\treturn new FileSecretsStore(process.cwd());\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t},\n\t\t);\n\t\tlet secrets = await store.read(stage);\n\n\t\tif (!secrets) {\n\t\t\t// Auto-initialize secrets for the stage\n\t\t\tconsole.log(` Initializing secrets for stage \"${stage}\"...`);\n\t\t\tsecrets = initStageSecrets(stage);\n\t\t\tawait store.write(stage, secrets);\n\t\t\tconsole.log(` ✓ Created the \"${stage}\" secrets (${store.name})`);\n\t\t}\n\n\t\t// Validate environment variables if constructs are provided\n\t\tif (constructs && constructs.length > 0) {\n\t\t\tconsole.log(' Analyzing environment variable requirements...');\n\t\t\tconst requiredVars = await collectRequiredEnvVars(constructs);\n\n\t\t\tif (requiredVars.length > 0) {\n\t\t\t\tconst validation = validateEnvironmentVariables(requiredVars, secrets);\n\n\t\t\t\tif (!validation.valid) {\n\t\t\t\t\tconst errorMessage = [\n\t\t\t\t\t\t`Missing environment variables for stage \"${stage}\":`,\n\t\t\t\t\t\t'',\n\t\t\t\t\t\t...validation.missing.map((v) => ` ❌ ${v}`),\n\t\t\t\t\t\t'',\n\t\t\t\t\t\t'To fix this, either:',\n\t\t\t\t\t\t` 1. Add the missing variables to the \"${stage}\" secrets using:`,\n\t\t\t\t\t\t` gkm secrets:set <KEY> <VALUE> --stage ${stage}`,\n\t\t\t\t\t\t'',\n\t\t\t\t\t\t` 2. Or import from a JSON file:`,\n\t\t\t\t\t\t` gkm secrets:import secrets.json --stage ${stage}`,\n\t\t\t\t\t\t'',\n\t\t\t\t\t\t'Required variables:',\n\t\t\t\t\t\t...validation.required.map((v) =>\n\t\t\t\t\t\t\tvalidation.missing.includes(v) ? ` ❌ ${v}` : ` ✓ ${v}`,\n\t\t\t\t\t\t),\n\t\t\t\t\t].join('\\n');\n\n\t\t\t\t\tthrow new Error(errorMessage);\n\t\t\t\t}\n\n\t\t\t\tconsole.log(\n\t\t\t\t\t` ✓ All ${requiredVars.length} required environment variables found`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Convert to embeddable format and encrypt\n\t\tconst embeddable = toEmbeddableSecrets(secrets);\n\t\tconst encrypted = encryptSecrets(embeddable);\n\t\tmasterKey = encrypted.masterKey;\n\n\t\t// Add define options for build-time injection using esbuild's --define:KEY=VALUE format\n\t\tconst defines = generateDefineOptions(encrypted);\n\t\tfor (const [key, value] of Object.entries(defines)) {\n\t\t\targs.push(`--define:${key}=${JSON.stringify(value)}`);\n\t\t}\n\n\t\tconsole.log(` Secrets encrypted for stage \"${stage}\"`);\n\t}\n\n\ttry {\n\t\t// Run esbuild with command-line arguments\n\t\tconst [cmd, ...cmdArgs] = args as [string, ...string[]];\n\t\tconst result = spawnSync(cmd, cmdArgs, {\n\t\t\tcwd: process.cwd(),\n\t\t\tstdio: 'inherit',\n\t\t\tshell: process.platform === 'win32', // Only use shell on Windows for npx resolution\n\t\t});\n\n\t\tif (result.error) {\n\t\t\tthrow result.error;\n\t\t}\n\t\tif (result.status !== 0) {\n\t\t\tthrow new Error(`esbuild exited with code ${result.status}`);\n\t\t}\n\n\t\t// Add shebang to the bundled file\n\t\tconst { readFile } = await import('node:fs/promises');\n\t\tconst content = await readFile(mjsOutput, 'utf-8');\n\t\tif (!content.startsWith('#!')) {\n\t\t\tawait writeFile(mjsOutput, `#!/usr/bin/env node\\n${content}`);\n\t\t}\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Failed to bundle server: ${error instanceof Error ? error.message : 'Unknown error'}`,\n\t\t);\n\t}\n\n\treturn {\n\t\toutputPath: mjsOutput,\n\t\tmasterKey,\n\t};\n}\n"],"mappings":"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"}