@geekmidas/cli 10.0.0-alpha.5 → 10.0.0-alpha.51

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 (267) hide show
  1. package/README.md +196 -257
  2. package/bin/adjacent-tsconfig.mjs +97 -0
  3. package/bin/gkm.mjs +16 -2
  4. package/bin/owning-tsconfig-jsx-register.mjs +18 -0
  5. package/bin/owning-tsconfig-jsx.mjs +108 -0
  6. package/dist/{HostingerProvider-h3EbEp-Z.cjs → HostingerProvider-BZFGYYVA.cjs} +21 -22
  7. package/dist/HostingerProvider-BZFGYYVA.cjs.map +1 -0
  8. package/dist/{HostingerProvider-Cft7lEYr.mjs → HostingerProvider-GF6NAZAK.mjs} +20 -21
  9. package/dist/HostingerProvider-GF6NAZAK.mjs.map +1 -0
  10. package/dist/{LocalStateProvider-Db9SEjyH.cjs → LocalStateProvider-7HR6SS2t.cjs} +11 -6
  11. package/dist/LocalStateProvider-7HR6SS2t.cjs.map +1 -0
  12. package/dist/{LocalStateProvider-Bn6tS-oL.mjs → LocalStateProvider-D7QyJoLM.mjs} +10 -4
  13. package/dist/LocalStateProvider-D7QyJoLM.mjs.map +1 -0
  14. package/dist/{openapi-CA0yJUTN.cjs → OpenApiTsGenerator-BN0PIZxd.cjs} +157 -372
  15. package/dist/OpenApiTsGenerator-BN0PIZxd.cjs.map +1 -0
  16. package/dist/{openapi-kkQbsRVj.mjs → OpenApiTsGenerator-BzIGy8KE.mjs} +128 -324
  17. package/dist/OpenApiTsGenerator-BzIGy8KE.mjs.map +1 -0
  18. package/dist/{Route53Provider-RdpUDJQk.mjs → Route53Provider-Cvj40A8I.mjs} +12 -12
  19. package/dist/Route53Provider-Cvj40A8I.mjs.map +1 -0
  20. package/dist/{Route53Provider-di2fMM5N.cjs → Route53Provider-DVILBsvV.cjs} +20 -21
  21. package/dist/Route53Provider-DVILBsvV.cjs.map +1 -0
  22. package/dist/{SSMStateProvider-BRt2py-A.mjs → SSMStateProvider-B0Hu1MKZ.mjs} +13 -4
  23. package/dist/SSMStateProvider-B0Hu1MKZ.mjs.map +1 -0
  24. package/dist/{SSMStateProvider-DyX8s9HI.cjs → SSMStateProvider-VrQ-yeqd.cjs} +17 -9
  25. package/dist/SSMStateProvider-VrQ-yeqd.cjs.map +1 -0
  26. package/dist/aws-DkHdTV-p.cjs +61 -0
  27. package/dist/aws-DkHdTV-p.cjs.map +1 -0
  28. package/dist/aws-DojzQnLd.mjs +61 -0
  29. package/dist/aws-DojzQnLd.mjs.map +1 -0
  30. package/dist/backends-B9V2XD2M.cjs +82 -0
  31. package/dist/backends-B9V2XD2M.cjs.map +1 -0
  32. package/dist/backends-CSuLl0YP.mjs +59 -0
  33. package/dist/backends-CSuLl0YP.mjs.map +1 -0
  34. package/dist/{backup-provisioner-BrsHwYG0.cjs → backup-provisioner-BmhIO_Yr.cjs} +21 -19
  35. package/dist/backup-provisioner-BmhIO_Yr.cjs.map +1 -0
  36. package/dist/{backup-provisioner-Bgj5Vi_L.mjs → backup-provisioner-DIVCbfee.mjs} +10 -7
  37. package/dist/backup-provisioner-DIVCbfee.mjs.map +1 -0
  38. package/dist/{bundler-Be8Zg4Wd.mjs → bundler--fyybf1N.mjs} +19 -30
  39. package/dist/bundler--fyybf1N.mjs.map +1 -0
  40. package/dist/{bundler-BY1OmV1e.cjs → bundler-By6_sXiL.cjs} +20 -32
  41. package/dist/bundler-By6_sXiL.cjs.map +1 -0
  42. package/dist/{config-C21ocYNd.cjs → config-Cvr5fCbn.cjs} +210 -210
  43. package/dist/config-Cvr5fCbn.cjs.map +1 -0
  44. package/dist/{config-WRdsQYZK.mjs → config-SOkDH-n1.mjs} +143 -154
  45. package/dist/config-SOkDH-n1.mjs.map +1 -0
  46. package/dist/config.cjs +9 -6
  47. package/dist/config.d.cts +62 -33
  48. package/dist/config.d.cts.map +1 -1
  49. package/dist/config.d.mts +62 -33
  50. package/dist/config.d.mts.map +1 -1
  51. package/dist/config.mjs +3 -5
  52. package/dist/deploy/sniffer-envkit-patch.cjs +18 -10
  53. package/dist/deploy/sniffer-envkit-patch.cjs.map +1 -1
  54. package/dist/deploy/sniffer-envkit-patch.d.cts +0 -2
  55. package/dist/deploy/sniffer-envkit-patch.d.cts.map +1 -1
  56. package/dist/deploy/sniffer-envkit-patch.d.mts +0 -2
  57. package/dist/deploy/sniffer-envkit-patch.d.mts.map +1 -1
  58. package/dist/deploy/sniffer-envkit-patch.mjs +10 -2
  59. package/dist/deploy/sniffer-envkit-patch.mjs.map +1 -1
  60. package/dist/deploy/sniffer-hooks.cjs +13 -7
  61. package/dist/deploy/sniffer-hooks.cjs.map +1 -1
  62. package/dist/deploy/sniffer-hooks.d.cts +1 -2
  63. package/dist/deploy/sniffer-hooks.d.cts.map +1 -1
  64. package/dist/deploy/sniffer-hooks.d.mts +1 -2
  65. package/dist/deploy/sniffer-hooks.d.mts.map +1 -1
  66. package/dist/deploy/sniffer-hooks.mjs +9 -2
  67. package/dist/deploy/sniffer-hooks.mjs.map +1 -1
  68. package/dist/deploy/sniffer-loader.cjs +16 -7
  69. package/dist/deploy/sniffer-loader.cjs.map +1 -1
  70. package/dist/deploy/sniffer-loader.d.cts +1 -1
  71. package/dist/deploy/sniffer-loader.d.mts +1 -1
  72. package/dist/deploy/sniffer-loader.mjs +13 -2
  73. package/dist/deploy/sniffer-loader.mjs.map +1 -1
  74. package/dist/deploy/sniffer-routes-worker.cjs +11 -11
  75. package/dist/deploy/sniffer-routes-worker.cjs.map +1 -1
  76. package/dist/deploy/sniffer-routes-worker.d.cts +1 -1
  77. package/dist/deploy/sniffer-routes-worker.d.mts +1 -1
  78. package/dist/deploy/sniffer-routes-worker.mjs +3 -3
  79. package/dist/deploy/sniffer-routes-worker.mjs.map +1 -1
  80. package/dist/deploy/sniffer-worker.cjs +15 -6
  81. package/dist/deploy/sniffer-worker.cjs.map +1 -1
  82. package/dist/deploy/sniffer-worker.d.cts +1 -3
  83. package/dist/deploy/sniffer-worker.d.cts.map +1 -1
  84. package/dist/deploy/sniffer-worker.d.mts +1 -3
  85. package/dist/deploy/sniffer-worker.d.mts.map +1 -1
  86. package/dist/deploy/sniffer-worker.mjs +15 -4
  87. package/dist/deploy/sniffer-worker.mjs.map +1 -1
  88. package/dist/{discover-CAlFgYec.mjs → discover-Cf2jAcgF.mjs} +56 -63
  89. package/dist/discover-Cf2jAcgF.mjs.map +1 -0
  90. package/dist/{discover-DTmFeloL.cjs → discover-DXNviAh-.cjs} +86 -110
  91. package/dist/discover-DXNviAh-.cjs.map +1 -0
  92. package/dist/{emit-CiZS_5Ub.mjs → emit-C1e8qD08.mjs} +13 -26
  93. package/dist/emit-C1e8qD08.mjs.map +1 -0
  94. package/dist/{emit-CbcRolsa.cjs → emit-Dmc1nvdS.cjs} +31 -57
  95. package/dist/emit-Dmc1nvdS.cjs.map +1 -0
  96. package/dist/encryption-Bt8SHRI6.cjs +91 -0
  97. package/dist/encryption-Bt8SHRI6.cjs.map +1 -0
  98. package/dist/encryption-DRCXp-U4.mjs +90 -0
  99. package/dist/encryption-DRCXp-U4.mjs.map +1 -0
  100. package/dist/github-BGx4U61b.cjs +213 -0
  101. package/dist/github-BGx4U61b.cjs.map +1 -0
  102. package/dist/github-U_frvZgD.mjs +213 -0
  103. package/dist/github-U_frvZgD.mjs.map +1 -0
  104. package/dist/{index-CHB4tSzX.d.cts → index-DYh5TrsZ.d.mts} +4 -23
  105. package/dist/index-DYh5TrsZ.d.mts.map +1 -0
  106. package/dist/{index-BThUzEIp.d.mts → index-fU3qHy7c.d.cts} +4 -23
  107. package/dist/index-fU3qHy7c.d.cts.map +1 -0
  108. package/dist/index.cjs +9561 -7232
  109. package/dist/index.cjs.map +1 -1
  110. package/dist/index.d.cts +1 -1
  111. package/dist/index.d.mts +1 -1
  112. package/dist/index.mjs +9479 -7177
  113. package/dist/index.mjs.map +1 -1
  114. package/dist/migrate-dx4I0epp.cjs +743 -0
  115. package/dist/migrate-dx4I0epp.cjs.map +1 -0
  116. package/dist/migrate-soUQtku3.mjs +736 -0
  117. package/dist/migrate-soUQtku3.mjs.map +1 -0
  118. package/dist/openapi.cjs +198 -8
  119. package/dist/openapi.cjs.map +1 -0
  120. package/dist/openapi.d.cts +53 -15
  121. package/dist/openapi.d.cts.map +1 -1
  122. package/dist/openapi.d.mts +53 -15
  123. package/dist/openapi.d.mts.map +1 -1
  124. package/dist/openapi.mjs +193 -5
  125. package/dist/openapi.mjs.map +1 -0
  126. package/dist/ready-WfT-eWlm.mjs +861 -0
  127. package/dist/ready-WfT-eWlm.mjs.map +1 -0
  128. package/dist/ready-lr2Fm-ep.cjs +932 -0
  129. package/dist/ready-lr2Fm-ep.cjs.map +1 -0
  130. package/dist/reconcile/public.cjs +3 -5
  131. package/dist/reconcile/public.d.cts +30 -33
  132. package/dist/reconcile/public.d.cts.map +1 -1
  133. package/dist/reconcile/public.d.mts +30 -33
  134. package/dist/reconcile/public.d.mts.map +1 -1
  135. package/dist/reconcile/public.mjs +3 -4
  136. package/dist/{reconcile-CFs8C_1a.mjs → reconcile--I7V4Klj.mjs} +4 -7
  137. package/dist/reconcile--I7V4Klj.mjs.map +1 -0
  138. package/dist/{reconcile-D0pimNiy.cjs → reconcile-CuCQBa5-.cjs} +5 -8
  139. package/dist/reconcile-CuCQBa5-.cjs.map +1 -0
  140. package/dist/rolldown-runtime-BhDjJH2R.mjs +15 -0
  141. package/dist/{chunk-CUT6urMc.cjs → rolldown-runtime-C0BPl7ul.cjs} +23 -10
  142. package/dist/storage-BzaUxmqe.mjs +124 -0
  143. package/dist/storage-BzaUxmqe.mjs.map +1 -0
  144. package/dist/storage-CSGoC3bK.cjs +152 -0
  145. package/dist/storage-CSGoC3bK.cjs.map +1 -0
  146. package/dist/store-DQgxT_WR.cjs +211 -0
  147. package/dist/store-DQgxT_WR.cjs.map +1 -0
  148. package/dist/store-DeGATCS-.mjs +182 -0
  149. package/dist/store-DeGATCS-.mjs.map +1 -0
  150. package/dist/types-CT0ymlBk.d.mts +6230 -0
  151. package/dist/types-CT0ymlBk.d.mts.map +1 -0
  152. package/dist/types-DYDT4FfE.d.cts +6231 -0
  153. package/dist/types-DYDT4FfE.d.cts.map +1 -0
  154. package/dist/vitest.cjs +65 -0
  155. package/dist/vitest.cjs.map +1 -0
  156. package/dist/vitest.d.cts +29 -0
  157. package/dist/vitest.d.cts.map +1 -0
  158. package/dist/vitest.d.mts +29 -0
  159. package/dist/vitest.d.mts.map +1 -0
  160. package/dist/vitest.mjs +65 -0
  161. package/dist/vitest.mjs.map +1 -0
  162. package/dist/workspace/index.cjs +3 -4
  163. package/dist/workspace/index.d.cts +3 -3
  164. package/dist/workspace/index.d.mts +3 -3
  165. package/dist/workspace/index.mjs +2 -3
  166. package/dist/{workspace-BcucjwvA.cjs → workspace-BcCiLzTO.cjs} +1040 -1120
  167. package/dist/workspace-BcCiLzTO.cjs.map +1 -0
  168. package/dist/{credentials-BBIRJVp4.mjs → workspace-Dp2zK_Nk.mjs} +2212 -1732
  169. package/dist/workspace-Dp2zK_Nk.mjs.map +1 -0
  170. package/dist/{credentials-G1JwBoXH.cjs → workspace-GBzR_hID.cjs} +2348 -1849
  171. package/dist/workspace-GBzR_hID.cjs.map +1 -0
  172. package/dist/{workspace-BFMrAKqI.mjs → workspace-bOsks1uk.mjs} +871 -974
  173. package/dist/workspace-bOsks1uk.mjs.map +1 -0
  174. package/package.json +29 -20
  175. package/dist/CachedStateProvider-BLo7zv7S.cjs +0 -3
  176. package/dist/CachedStateProvider-BxoAm-B2.mjs +0 -3
  177. package/dist/CachedStateProvider-CqPdW7CB.cjs +0 -60
  178. package/dist/CachedStateProvider-CqPdW7CB.cjs.map +0 -1
  179. package/dist/CachedStateProvider-DTniehMP.mjs +0 -54
  180. package/dist/CachedStateProvider-DTniehMP.mjs.map +0 -1
  181. package/dist/HostingerProvider-Cft7lEYr.mjs.map +0 -1
  182. package/dist/HostingerProvider-h3EbEp-Z.cjs.map +0 -1
  183. package/dist/LocalStateProvider-Bn6tS-oL.mjs.map +0 -1
  184. package/dist/LocalStateProvider-Db9SEjyH.cjs.map +0 -1
  185. package/dist/Route53Provider-RdpUDJQk.mjs.map +0 -1
  186. package/dist/Route53Provider-di2fMM5N.cjs.map +0 -1
  187. package/dist/SSMStateProvider-BRt2py-A.mjs.map +0 -1
  188. package/dist/SSMStateProvider-DyX8s9HI.cjs.map +0 -1
  189. package/dist/backup-provisioner-Bgj5Vi_L.mjs.map +0 -1
  190. package/dist/backup-provisioner-BrsHwYG0.cjs.map +0 -1
  191. package/dist/bundler-BY1OmV1e.cjs.map +0 -1
  192. package/dist/bundler-Be8Zg4Wd.mjs.map +0 -1
  193. package/dist/chunk-Duj1WY3L.mjs +0 -7
  194. package/dist/config-C21ocYNd.cjs.map +0 -1
  195. package/dist/config-WRdsQYZK.mjs.map +0 -1
  196. package/dist/credentials-B5zRHdAl.mjs +0 -141
  197. package/dist/credentials-B5zRHdAl.mjs.map +0 -1
  198. package/dist/credentials-BBIRJVp4.mjs.map +0 -1
  199. package/dist/credentials-BapzjyV5.cjs +0 -195
  200. package/dist/credentials-BapzjyV5.cjs.map +0 -1
  201. package/dist/credentials-G1JwBoXH.cjs.map +0 -1
  202. package/dist/discover-CAlFgYec.mjs.map +0 -1
  203. package/dist/discover-DTmFeloL.cjs.map +0 -1
  204. package/dist/dokploy-api-BS2xnJLZ.mjs +0 -3
  205. package/dist/dokploy-api-BsVH0y_s.cjs +0 -718
  206. package/dist/dokploy-api-BsVH0y_s.cjs.map +0 -1
  207. package/dist/dokploy-api-BuxDRo3l.cjs +0 -3
  208. package/dist/dokploy-api-aTFPz7_4.mjs +0 -706
  209. package/dist/dokploy-api-aTFPz7_4.mjs.map +0 -1
  210. package/dist/emit-CbcRolsa.cjs.map +0 -1
  211. package/dist/emit-CiZS_5Ub.mjs.map +0 -1
  212. package/dist/encryption-C6jaU0hE.cjs +0 -54
  213. package/dist/encryption-C6jaU0hE.cjs.map +0 -1
  214. package/dist/encryption-CHzzeR8a.cjs +0 -4
  215. package/dist/encryption-CQUncu7v.mjs +0 -3
  216. package/dist/encryption-D-abyhZx.mjs +0 -42
  217. package/dist/encryption-D-abyhZx.mjs.map +0 -1
  218. package/dist/fullstack-secrets-C-GGohke.cjs +0 -111
  219. package/dist/fullstack-secrets-C-GGohke.cjs.map +0 -1
  220. package/dist/fullstack-secrets-D1o9-G5l.mjs +0 -87
  221. package/dist/fullstack-secrets-D1o9-G5l.mjs.map +0 -1
  222. package/dist/generator-CNzBw7g5.cjs +0 -289
  223. package/dist/generator-CNzBw7g5.cjs.map +0 -1
  224. package/dist/generator-VE2JrFlY.mjs +0 -253
  225. package/dist/generator-VE2JrFlY.mjs.map +0 -1
  226. package/dist/index-BThUzEIp.d.mts.map +0 -1
  227. package/dist/index-CHB4tSzX.d.cts.map +0 -1
  228. package/dist/openapi-CA0yJUTN.cjs.map +0 -1
  229. package/dist/openapi-kkQbsRVj.mjs.map +0 -1
  230. package/dist/openapi-react-query-Byltvm_7.mjs +0 -167
  231. package/dist/openapi-react-query-Byltvm_7.mjs.map +0 -1
  232. package/dist/openapi-react-query-CmJ1xDbn.cjs +0 -173
  233. package/dist/openapi-react-query-CmJ1xDbn.cjs.map +0 -1
  234. package/dist/openapi-react-query.cjs +0 -4
  235. package/dist/openapi-react-query.d.cts +0 -11
  236. package/dist/openapi-react-query.d.cts.map +0 -1
  237. package/dist/openapi-react-query.d.mts +0 -11
  238. package/dist/openapi-react-query.d.mts.map +0 -1
  239. package/dist/openapi-react-query.mjs +0 -4
  240. package/dist/reconcile-CFs8C_1a.mjs.map +0 -1
  241. package/dist/reconcile-D0pimNiy.cjs.map +0 -1
  242. package/dist/setup-B5_oHHUj.cjs +0 -11
  243. package/dist/setup-Cm2UnAdh.cjs +0 -473
  244. package/dist/setup-Cm2UnAdh.cjs.map +0 -1
  245. package/dist/setup-DXvRLTZN.mjs +0 -443
  246. package/dist/setup-DXvRLTZN.mjs.map +0 -1
  247. package/dist/setup-ZijS4qeC.mjs +0 -11
  248. package/dist/storage-BJmzYxt8.cjs +0 -363
  249. package/dist/storage-BJmzYxt8.cjs.map +0 -1
  250. package/dist/storage-BM9hGpgv.cjs +0 -7
  251. package/dist/storage-Bi24WfoW.mjs +0 -297
  252. package/dist/storage-Bi24WfoW.mjs.map +0 -1
  253. package/dist/storage-CYlGwWTj.mjs +0 -3
  254. package/dist/sync-CmEuNZsB.mjs +0 -76
  255. package/dist/sync-CmEuNZsB.mjs.map +0 -1
  256. package/dist/sync-D306yOWu.cjs +0 -4
  257. package/dist/sync-SizaChmC.cjs +0 -93
  258. package/dist/sync-SizaChmC.cjs.map +0 -1
  259. package/dist/sync-hOqAWHR6.mjs +0 -3
  260. package/dist/types-C-arnQ1r.d.mts +0 -2184
  261. package/dist/types-C-arnQ1r.d.mts.map +0 -1
  262. package/dist/types-abJQuk7m.d.cts +0 -2184
  263. package/dist/types-abJQuk7m.d.cts.map +0 -1
  264. package/dist/workspace-BFMrAKqI.mjs.map +0 -1
  265. package/dist/workspace-BcucjwvA.cjs.map +0 -1
  266. package/dist/workspace-DdJss8I6.cjs +0 -9
  267. package/dist/workspace-k4l3rsFh.mjs +0 -8
@@ -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"}
@@ -0,0 +1,82 @@
1
+ const require_ready = require("./ready-lr2Fm-ep.cjs");
2
+ //#region src/workspace/backends.ts
3
+ /**
4
+ * Which backend each declared kind resolves to — decided by where the project
5
+ * deploys, and by nothing else.
6
+ *
7
+ * These used to be read out of a `services:` block, where one key answered two
8
+ * questions: *does this exist* and *what backs it*. The first is the manifest's
9
+ * — a declared `Cache` is why there is a cache — and the second is the deploy
10
+ * target's, because the right cache for a Lambda is not the right cache for a
11
+ * box already running Postgres. With both answered elsewhere there was nothing
12
+ * left for the block to say, so it is gone and these read the target.
13
+ *
14
+ * Mail has no answer here at all: every provider speaks SMTP, so the provider
15
+ * is whichever one the stage's mail URL points at.
16
+ */
17
+ /**
18
+ * Which family of defaults a workspace's deploy target belongs to.
19
+ *
20
+ * One question, asked once: **does this target run containers the project
21
+ * controls?** Dokploy and a bare server do, so a bucket or a cache can live
22
+ * beside the app; AWS, Vercel and Cloudflare do not, so the default has to be
23
+ * something managed. That is the whole of the distinction, and it is why
24
+ * `MainProvider` is the right axis rather than the deploy target's own name —
25
+ * three targets share one answer.
26
+ *
27
+ * An AWS deploy names `deploy.default: 'sst'`, because it goes through SST.
28
+ */
29
+ function providerOf(workspace) {
30
+ const target = workspace.deploy?.default;
31
+ return target === "dokploy" || target === "server" ? "server" : "aws";
32
+ }
33
+ /**
34
+ * The cache backend a target implies.
35
+ *
36
+ * The same answer locally and deployed: a local `gkm dev` for an AWS app still
37
+ * uses the Upstash protocol, because a backend that differed between the two
38
+ * would be worse than a slower one.
39
+ */
40
+ function cacheBackendFor(on) {
41
+ return require_ready.DEFAULT_CACHE[on];
42
+ }
43
+ /** Where a declared bucket lives on a target. */
44
+ function storageBackendFor(on) {
45
+ return require_ready.DEFAULT_STORAGE[on];
46
+ }
47
+ /**
48
+ * Which broker carries declared queues and topics on a target.
49
+ *
50
+ * Only consulted when something declares one; a project with no `Topic` or
51
+ * `Queue` has no events at all, whatever the target.
52
+ */
53
+ function eventsBackendFor(on) {
54
+ return require_ready.DEFAULT_EVENTS[on];
55
+ }
56
+ //#endregion
57
+ Object.defineProperty(exports, "cacheBackendFor", {
58
+ enumerable: true,
59
+ get: function() {
60
+ return cacheBackendFor;
61
+ }
62
+ });
63
+ Object.defineProperty(exports, "eventsBackendFor", {
64
+ enumerable: true,
65
+ get: function() {
66
+ return eventsBackendFor;
67
+ }
68
+ });
69
+ Object.defineProperty(exports, "providerOf", {
70
+ enumerable: true,
71
+ get: function() {
72
+ return providerOf;
73
+ }
74
+ });
75
+ Object.defineProperty(exports, "storageBackendFor", {
76
+ enumerable: true,
77
+ get: function() {
78
+ return storageBackendFor;
79
+ }
80
+ });
81
+
82
+ //# sourceMappingURL=backends-B9V2XD2M.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backends-B9V2XD2M.cjs","names":["DEFAULT_CACHE","DEFAULT_STORAGE","DEFAULT_EVENTS"],"sources":["../src/workspace/backends.ts"],"sourcesContent":["/**\n * Which backend each declared kind resolves to — decided by where the project\n * deploys, and by nothing else.\n *\n * These used to be read out of a `services:` block, where one key answered two\n * questions: *does this exist* and *what backs it*. The first is the manifest's\n * — a declared `Cache` is why there is a cache — and the second is the deploy\n * target's, because the right cache for a Lambda is not the right cache for a\n * box already running Postgres. With both answered elsewhere there was nothing\n * left for the block to say, so it is gone and these read the target.\n *\n * Mail has no answer here at all: every provider speaks SMTP, so the provider\n * is whichever one the stage's mail URL points at.\n */\n\nimport {\n\ttype CacheBackend,\n\tDEFAULT_CACHE,\n\tDEFAULT_EVENTS,\n\tDEFAULT_STORAGE,\n\ttype EventsBackend,\n\ttype MainProvider,\n\ttype StorageBackend,\n} from '../types.js';\n\n/**\n * Which family of defaults a workspace's deploy target belongs to.\n *\n * One question, asked once: **does this target run containers the project\n * controls?** Dokploy and a bare server do, so a bucket or a cache can live\n * beside the app; AWS, Vercel and Cloudflare do not, so the default has to be\n * something managed. That is the whole of the distinction, and it is why\n * `MainProvider` is the right axis rather than the deploy target's own name —\n * three targets share one answer.\n *\n * An AWS deploy names `deploy.default: 'sst'`, because it goes through SST.\n */\nexport function providerOf(workspace: {\n\tdeploy?: { default?: string } | undefined;\n}): MainProvider {\n\tconst target = workspace.deploy?.default;\n\n\treturn target === 'dokploy' || target === 'server' ? 'server' : 'aws';\n}\n\n/**\n * The cache backend a target implies.\n *\n * The same answer locally and deployed: a local `gkm dev` for an AWS app still\n * uses the Upstash protocol, because a backend that differed between the two\n * would be worse than a slower one.\n */\nexport function cacheBackendFor(on: MainProvider): CacheBackend {\n\treturn DEFAULT_CACHE[on];\n}\n\n/** Where a declared bucket lives on a target. */\nexport function storageBackendFor(on: MainProvider): StorageBackend {\n\treturn DEFAULT_STORAGE[on];\n}\n\n/**\n * Which broker carries declared queues and topics on a target.\n *\n * Only consulted when something declares one; a project with no `Topic` or\n * `Queue` has no events at all, whatever the target.\n */\nexport function eventsBackendFor(on: MainProvider): EventsBackend {\n\treturn DEFAULT_EVENTS[on];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCA,SAAgB,WAAW,WAEV;CAChB,MAAM,SAAS,UAAU,QAAQ;CAEjC,OAAO,WAAW,aAAa,WAAW,WAAW,WAAW;AACjE;;;;;;;;AASA,SAAgB,gBAAgB,IAAgC;CAC/D,OAAOA,cAAAA,cAAc;AACtB;;AAGA,SAAgB,kBAAkB,IAAkC;CACnE,OAAOC,cAAAA,gBAAgB;AACxB;;;;;;;AAQA,SAAgB,iBAAiB,IAAiC;CACjE,OAAOC,cAAAA,eAAe;AACvB"}
@@ -0,0 +1,59 @@
1
+ import { d as DEFAULT_EVENTS, f as DEFAULT_STORAGE, u as DEFAULT_CACHE } from "./ready-WfT-eWlm.mjs";
2
+ //#region src/workspace/backends.ts
3
+ /**
4
+ * Which backend each declared kind resolves to — decided by where the project
5
+ * deploys, and by nothing else.
6
+ *
7
+ * These used to be read out of a `services:` block, where one key answered two
8
+ * questions: *does this exist* and *what backs it*. The first is the manifest's
9
+ * — a declared `Cache` is why there is a cache — and the second is the deploy
10
+ * target's, because the right cache for a Lambda is not the right cache for a
11
+ * box already running Postgres. With both answered elsewhere there was nothing
12
+ * left for the block to say, so it is gone and these read the target.
13
+ *
14
+ * Mail has no answer here at all: every provider speaks SMTP, so the provider
15
+ * is whichever one the stage's mail URL points at.
16
+ */
17
+ /**
18
+ * Which family of defaults a workspace's deploy target belongs to.
19
+ *
20
+ * One question, asked once: **does this target run containers the project
21
+ * controls?** Dokploy and a bare server do, so a bucket or a cache can live
22
+ * beside the app; AWS, Vercel and Cloudflare do not, so the default has to be
23
+ * something managed. That is the whole of the distinction, and it is why
24
+ * `MainProvider` is the right axis rather than the deploy target's own name —
25
+ * three targets share one answer.
26
+ *
27
+ * An AWS deploy names `deploy.default: 'sst'`, because it goes through SST.
28
+ */
29
+ function providerOf(workspace) {
30
+ const target = workspace.deploy?.default;
31
+ return target === "dokploy" || target === "server" ? "server" : "aws";
32
+ }
33
+ /**
34
+ * The cache backend a target implies.
35
+ *
36
+ * The same answer locally and deployed: a local `gkm dev` for an AWS app still
37
+ * uses the Upstash protocol, because a backend that differed between the two
38
+ * would be worse than a slower one.
39
+ */
40
+ function cacheBackendFor(on) {
41
+ return DEFAULT_CACHE[on];
42
+ }
43
+ /** Where a declared bucket lives on a target. */
44
+ function storageBackendFor(on) {
45
+ return DEFAULT_STORAGE[on];
46
+ }
47
+ /**
48
+ * Which broker carries declared queues and topics on a target.
49
+ *
50
+ * Only consulted when something declares one; a project with no `Topic` or
51
+ * `Queue` has no events at all, whatever the target.
52
+ */
53
+ function eventsBackendFor(on) {
54
+ return DEFAULT_EVENTS[on];
55
+ }
56
+ //#endregion
57
+ export { storageBackendFor as i, eventsBackendFor as n, providerOf as r, cacheBackendFor as t };
58
+
59
+ //# sourceMappingURL=backends-CSuLl0YP.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backends-CSuLl0YP.mjs","names":[],"sources":["../src/workspace/backends.ts"],"sourcesContent":["/**\n * Which backend each declared kind resolves to — decided by where the project\n * deploys, and by nothing else.\n *\n * These used to be read out of a `services:` block, where one key answered two\n * questions: *does this exist* and *what backs it*. The first is the manifest's\n * — a declared `Cache` is why there is a cache — and the second is the deploy\n * target's, because the right cache for a Lambda is not the right cache for a\n * box already running Postgres. With both answered elsewhere there was nothing\n * left for the block to say, so it is gone and these read the target.\n *\n * Mail has no answer here at all: every provider speaks SMTP, so the provider\n * is whichever one the stage's mail URL points at.\n */\n\nimport {\n\ttype CacheBackend,\n\tDEFAULT_CACHE,\n\tDEFAULT_EVENTS,\n\tDEFAULT_STORAGE,\n\ttype EventsBackend,\n\ttype MainProvider,\n\ttype StorageBackend,\n} from '../types.js';\n\n/**\n * Which family of defaults a workspace's deploy target belongs to.\n *\n * One question, asked once: **does this target run containers the project\n * controls?** Dokploy and a bare server do, so a bucket or a cache can live\n * beside the app; AWS, Vercel and Cloudflare do not, so the default has to be\n * something managed. That is the whole of the distinction, and it is why\n * `MainProvider` is the right axis rather than the deploy target's own name —\n * three targets share one answer.\n *\n * An AWS deploy names `deploy.default: 'sst'`, because it goes through SST.\n */\nexport function providerOf(workspace: {\n\tdeploy?: { default?: string } | undefined;\n}): MainProvider {\n\tconst target = workspace.deploy?.default;\n\n\treturn target === 'dokploy' || target === 'server' ? 'server' : 'aws';\n}\n\n/**\n * The cache backend a target implies.\n *\n * The same answer locally and deployed: a local `gkm dev` for an AWS app still\n * uses the Upstash protocol, because a backend that differed between the two\n * would be worse than a slower one.\n */\nexport function cacheBackendFor(on: MainProvider): CacheBackend {\n\treturn DEFAULT_CACHE[on];\n}\n\n/** Where a declared bucket lives on a target. */\nexport function storageBackendFor(on: MainProvider): StorageBackend {\n\treturn DEFAULT_STORAGE[on];\n}\n\n/**\n * Which broker carries declared queues and topics on a target.\n *\n * Only consulted when something declares one; a project with no `Topic` or\n * `Queue` has no events at all, whatever the target.\n */\nexport function eventsBackendFor(on: MainProvider): EventsBackend {\n\treturn DEFAULT_EVENTS[on];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCA,SAAgB,WAAW,WAEV;CAChB,MAAM,SAAS,UAAU,QAAQ;CAEjC,OAAO,WAAW,aAAa,WAAW,WAAW,WAAW;AACjE;;;;;;;;AASA,SAAgB,gBAAgB,IAAgC;CAC/D,OAAO,cAAc;AACtB;;AAGA,SAAgB,kBAAkB,IAAkC;CACnE,OAAO,gBAAgB;AACxB;;;;;;;AAQA,SAAgB,iBAAiB,IAAiC;CACjE,OAAO,eAAe;AACvB"}
@@ -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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"}