@geekmidas/cli 10.0.0-alpha.96 → 10.0.0-alpha.98

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 (200) hide show
  1. package/dist/{GoDaddyProvider-BsUKjGcg.mjs → GoDaddyProvider-C2fK-zeQ.mjs} +2 -2
  2. package/dist/{GoDaddyProvider-BsUKjGcg.mjs.map → GoDaddyProvider-C2fK-zeQ.mjs.map} +1 -1
  3. package/dist/{GoDaddyProvider-DyHXHDHe.cjs → GoDaddyProvider-SLlyDwD1.cjs} +2 -2
  4. package/dist/{GoDaddyProvider-DyHXHDHe.cjs.map → GoDaddyProvider-SLlyDwD1.cjs.map} +1 -1
  5. package/dist/{LegacyStateStore-BnMcONE0.cjs → LegacyStateStore-CAvxM8U5.cjs} +3 -3
  6. package/dist/{LegacyStateStore-BnMcONE0.cjs.map → LegacyStateStore-CAvxM8U5.cjs.map} +1 -1
  7. package/dist/{LegacyStateStore-B6SOqRFi.mjs → LegacyStateStore-D9QDWCst.mjs} +3 -3
  8. package/dist/{LegacyStateStore-B6SOqRFi.mjs.map → LegacyStateStore-D9QDWCst.mjs.map} +1 -1
  9. package/dist/{LocalStateStore-6CrsZzqI.mjs → LocalStateStore-DPQSqkSb.mjs} +2 -2
  10. package/dist/{LocalStateStore-6CrsZzqI.mjs.map → LocalStateStore-DPQSqkSb.mjs.map} +1 -1
  11. package/dist/{LocalStateStore-pd4bbdJJ.cjs → LocalStateStore-QcrdjCl6.cjs} +2 -2
  12. package/dist/{LocalStateStore-pd4bbdJJ.cjs.map → LocalStateStore-QcrdjCl6.cjs.map} +1 -1
  13. package/dist/{S3StateStore-CoGk6ZKU.cjs → S3StateStore-C16tVikU.cjs} +73 -11
  14. package/dist/S3StateStore-C16tVikU.cjs.map +1 -0
  15. package/dist/{S3StateStore-DP_7Q4yb.mjs → S3StateStore-jorgKaee.mjs} +73 -11
  16. package/dist/S3StateStore-jorgKaee.mjs.map +1 -0
  17. package/dist/{SSMStateStore-Bo6_KDYY.mjs → SSMStateStore-Dvjt_INw.mjs} +58 -7
  18. package/dist/SSMStateStore-Dvjt_INw.mjs.map +1 -0
  19. package/dist/{SSMStateStore-D9s4_Do3.cjs → SSMStateStore-mPAEr2P2.cjs} +58 -7
  20. package/dist/SSMStateStore-mPAEr2P2.cjs.map +1 -0
  21. package/dist/{add-BQS2QZHp.mjs → add-CZIIsTcv.mjs} +4 -4
  22. package/dist/{add-BQS2QZHp.mjs.map → add-CZIIsTcv.mjs.map} +1 -1
  23. package/dist/{add-CjURJ-vI.cjs → add-SRk65Udu.cjs} +4 -4
  24. package/dist/{add-CjURJ-vI.cjs.map → add-SRk65Udu.cjs.map} +1 -1
  25. package/dist/{aws-Dz2bYxK1.mjs → aws-8QQ8n-t6.mjs} +2 -2
  26. package/dist/{aws-Dz2bYxK1.mjs.map → aws-8QQ8n-t6.mjs.map} +1 -1
  27. package/dist/{aws-BTReGjpY.cjs → aws-DY9NqQIT.cjs} +2 -2
  28. package/dist/{aws-BTReGjpY.cjs.map → aws-DY9NqQIT.cjs.map} +1 -1
  29. package/dist/{awsStore-BWa1KNY-.mjs → awsStore-Bx5nCFnS.mjs} +8 -6
  30. package/dist/awsStore-Bx5nCFnS.mjs.map +1 -0
  31. package/dist/{awsStore-B5We7bMg.cjs → awsStore-u3ehhMoY.cjs} +8 -6
  32. package/dist/awsStore-u3ehhMoY.cjs.map +1 -0
  33. package/dist/{bundler-DmLJQ7AY.cjs → bundler-B0Pj5FXT.cjs} +5 -5
  34. package/dist/{bundler-DmLJQ7AY.cjs.map → bundler-B0Pj5FXT.cjs.map} +1 -1
  35. package/dist/{bundler-BMM3UsGm.mjs → bundler-DxJ-cIEt.mjs} +5 -5
  36. package/dist/{bundler-BMM3UsGm.mjs.map → bundler-DxJ-cIEt.mjs.map} +1 -1
  37. package/dist/{cli-C2c-jOVK.cjs → cli-CRDJWWmZ.cjs} +2 -2
  38. package/dist/{cli-C2c-jOVK.cjs.map → cli-CRDJWWmZ.cjs.map} +1 -1
  39. package/dist/{cli-Ce6NgaQF.mjs → cli-CV85bY3-.mjs} +2 -2
  40. package/dist/{cli-Ce6NgaQF.mjs.map → cli-CV85bY3-.mjs.map} +1 -1
  41. package/dist/{compose-BJDPFBAN.mjs → compose-CQQRRJC3.mjs} +7 -7
  42. package/dist/{compose-BJDPFBAN.mjs.map → compose-CQQRRJC3.mjs.map} +1 -1
  43. package/dist/{compose-ETFKqkTu.cjs → compose-DMY4ouEk.cjs} +7 -7
  44. package/dist/{compose-ETFKqkTu.cjs.map → compose-DMY4ouEk.cjs.map} +1 -1
  45. package/dist/{config-C8NPCn1l.d.mts → config-CS76CzXM.d.cts} +4 -4
  46. package/dist/{config-4yirvxS8.d.cts.map → config-CS76CzXM.d.cts.map} +1 -1
  47. package/dist/{config-4yirvxS8.d.cts → config-pBzLfNef.d.mts} +4 -4
  48. package/dist/{config-C8NPCn1l.d.mts.map → config-pBzLfNef.d.mts.map} +1 -1
  49. package/dist/config.cjs +1 -1
  50. package/dist/config.d.cts +3 -3
  51. package/dist/config.d.mts +3 -3
  52. package/dist/config.mjs +1 -1
  53. package/dist/deploy/public.cjs +7 -7
  54. package/dist/deploy/public.d.cts +5 -5
  55. package/dist/deploy/public.d.cts.map +1 -1
  56. package/dist/deploy/public.d.mts +5 -5
  57. package/dist/deploy/public.d.mts.map +1 -1
  58. package/dist/deploy/public.mjs +7 -7
  59. package/dist/{deploy-BdN0ym2i.cjs → deploy-277jek46.cjs} +19 -8
  60. package/dist/deploy-277jek46.cjs.map +1 -0
  61. package/dist/{deploy-DktQl7ti.mjs → deploy-Dw8g3sLY.mjs} +19 -8
  62. package/dist/{deploy-DktQl7ti.mjs.map → deploy-Dw8g3sLY.mjs.map} +1 -1
  63. package/dist/{discover-D-MdXZgm.d.mts → discover-CU1LjCOf.d.cts} +2 -2
  64. package/dist/{discover-BtFofyQB.d.cts.map → discover-CU1LjCOf.d.cts.map} +1 -1
  65. package/dist/{discover-BtFofyQB.d.cts → discover-DJCCgimv.d.mts} +2 -2
  66. package/dist/{discover-D-MdXZgm.d.mts.map → discover-DJCCgimv.d.mts.map} +1 -1
  67. package/dist/{dns-ClI7-d5g.cjs → dns-4RAiVFa7.cjs} +5 -5
  68. package/dist/{dns-ClI7-d5g.cjs.map → dns-4RAiVFa7.cjs.map} +1 -1
  69. package/dist/{dns-BjHECDO7.mjs → dns-CU22K_i0.mjs} +5 -5
  70. package/dist/{dns-BjHECDO7.mjs.map → dns-CU22K_i0.mjs.map} +1 -1
  71. package/dist/{dns-C-X6PLkV.cjs → dns-Dhjbnjgl.cjs} +3 -3
  72. package/dist/{dns-C-X6PLkV.cjs.map → dns-Dhjbnjgl.cjs.map} +1 -1
  73. package/dist/{dns-C2EeR6n2.mjs → dns-LqWzPb8l.mjs} +3 -3
  74. package/dist/{dns-C2EeR6n2.mjs.map → dns-LqWzPb8l.mjs.map} +1 -1
  75. package/dist/{dokploy-CxtkS5hl.mjs → dokploy-D_uCbZQ7.mjs} +2 -2
  76. package/dist/{dokploy-CxtkS5hl.mjs.map → dokploy-D_uCbZQ7.mjs.map} +1 -1
  77. package/dist/{dokploy-CD_Bc4EV.cjs → dokploy-Dw4oCXqR.cjs} +2 -2
  78. package/dist/{dokploy-CD_Bc4EV.cjs.map → dokploy-Dw4oCXqR.cjs.map} +1 -1
  79. package/dist/{encryption-BgQd4f2S.mjs → encryption-C0uKYE9v.mjs} +3 -3
  80. package/dist/{encryption-BgQd4f2S.mjs.map → encryption-C0uKYE9v.mjs.map} +1 -1
  81. package/dist/{encryption-CqVfz-m7.cjs → encryption-CtVPHWWq.cjs} +3 -3
  82. package/dist/{encryption-CqVfz-m7.cjs.map → encryption-CtVPHWWq.cjs.map} +1 -1
  83. package/dist/{engine-YlXzlTUl.mjs → engine-BE5PdE8S.mjs} +4 -4
  84. package/dist/{engine-YlXzlTUl.mjs.map → engine-BE5PdE8S.mjs.map} +1 -1
  85. package/dist/{engine-OVVKtPmW.cjs → engine-HI8aLw1M.cjs} +4 -4
  86. package/dist/{engine-OVVKtPmW.cjs.map → engine-HI8aLw1M.cjs.map} +1 -1
  87. package/dist/{github-CsMloeSL.cjs → github-BCdfZ9zO.cjs} +63 -26
  88. package/dist/github-BCdfZ9zO.cjs.map +1 -0
  89. package/dist/{github-DuuvqI3Z.mjs → github-CT3X720_.mjs} +63 -26
  90. package/dist/github-CT3X720_.mjs.map +1 -0
  91. package/dist/{index-5eL8rM6H.d.mts → index-DKtB01YC.d.cts} +2 -2
  92. package/dist/{index-5eL8rM6H.d.mts.map → index-DKtB01YC.d.cts.map} +1 -1
  93. package/dist/{index-Dz-gyjm_.d.cts → index-ZAUrXH6Q.d.mts} +2 -2
  94. package/dist/{index-Dz-gyjm_.d.cts.map → index-ZAUrXH6Q.d.mts.map} +1 -1
  95. package/dist/index.cjs +81 -62
  96. package/dist/index.cjs.map +1 -1
  97. package/dist/index.mjs +81 -62
  98. package/dist/index.mjs.map +1 -1
  99. package/dist/{migrate-CB8B6W9y.mjs → migrate-CCZUgxMK.mjs} +3 -3
  100. package/dist/{migrate-CB8B6W9y.mjs.map → migrate-CCZUgxMK.mjs.map} +1 -1
  101. package/dist/{migrate-BAmJeVq1.cjs → migrate-Cs7kQxD5.cjs} +3 -3
  102. package/dist/{migrate-BAmJeVq1.cjs.map → migrate-Cs7kQxD5.cjs.map} +1 -1
  103. package/dist/migrate-DBGyoBvZ.cjs +181 -0
  104. package/dist/migrate-DBGyoBvZ.cjs.map +1 -0
  105. package/dist/migrate-DH-lOK3j.mjs +181 -0
  106. package/dist/migrate-DH-lOK3j.mjs.map +1 -0
  107. package/dist/openapi.cjs +1 -1
  108. package/dist/openapi.d.cts +1 -1
  109. package/dist/openapi.d.mts +1 -1
  110. package/dist/openapi.mjs +1 -1
  111. package/dist/projectBucket-BjeDcAYy.cjs +206 -0
  112. package/dist/projectBucket-BjeDcAYy.cjs.map +1 -0
  113. package/dist/projectBucket-DrkEht-j.mjs +177 -0
  114. package/dist/projectBucket-DrkEht-j.mjs.map +1 -0
  115. package/dist/reconcile/public.d.cts +2 -2
  116. package/dist/reconcile/public.d.mts +2 -2
  117. package/dist/{resolve-BDatNOoG.cjs → resolve-CScrqgdC.cjs} +3 -3
  118. package/dist/{resolve-BDatNOoG.cjs.map → resolve-CScrqgdC.cjs.map} +1 -1
  119. package/dist/{resolve-CUne0qOm.mjs → resolve-OfLaxaxY.mjs} +3 -3
  120. package/dist/{resolve-CUne0qOm.mjs.map → resolve-OfLaxaxY.mjs.map} +1 -1
  121. package/dist/{rollback-CuG3DkTY.cjs → rollback-D7p4wJNO.cjs} +7 -6
  122. package/dist/{rollback-CuG3DkTY.cjs.map → rollback-D7p4wJNO.cjs.map} +1 -1
  123. package/dist/{rollback-CCdEEEcF.mjs → rollback-DQBSN-i7.mjs} +7 -6
  124. package/dist/{rollback-CCdEEEcF.mjs.map → rollback-DQBSN-i7.mjs.map} +1 -1
  125. package/dist/{runtime-Vr6mleo2.d.mts → runtime-BlHOVx8L.d.cts} +2 -2
  126. package/dist/{runtime-DUQdM-mW.d.cts.map → runtime-BlHOVx8L.d.cts.map} +1 -1
  127. package/dist/{runtime-DUQdM-mW.d.cts → runtime-DMq9C3KB.d.mts} +2 -2
  128. package/dist/{runtime-Vr6mleo2.d.mts.map → runtime-DMq9C3KB.d.mts.map} +1 -1
  129. package/dist/s3-CUKzBBae.mjs +215 -0
  130. package/dist/s3-CUKzBBae.mjs.map +1 -0
  131. package/dist/s3-QQi4sNOQ.cjs +215 -0
  132. package/dist/s3-QQi4sNOQ.cjs.map +1 -0
  133. package/dist/{sandbox-DGOsMPMf.d.mts → sandbox-ArJ_hEKk.d.cts} +2 -2
  134. package/dist/{sandbox-BbJmh2rI.d.cts.map → sandbox-ArJ_hEKk.d.cts.map} +1 -1
  135. package/dist/{sandbox-BbJmh2rI.d.cts → sandbox-Cm4X6HRh.d.mts} +2 -2
  136. package/dist/{sandbox-DGOsMPMf.d.mts.map → sandbox-Cm4X6HRh.d.mts.map} +1 -1
  137. package/dist/{secrets-ZikTIxEm.mjs → secrets-AA_tQUl-.mjs} +3 -3
  138. package/dist/{secrets-ZikTIxEm.mjs.map → secrets-AA_tQUl-.mjs.map} +1 -1
  139. package/dist/{secrets-C5qROlbp.cjs → secrets-B_biKsKx.cjs} +3 -3
  140. package/dist/{secrets-C5qROlbp.cjs.map → secrets-B_biKsKx.cjs.map} +1 -1
  141. package/dist/{secretsManager-C6wm-9uZ.mjs → secretsManager-CQORubp9.mjs} +2 -2
  142. package/dist/{secretsManager-C6wm-9uZ.mjs.map → secretsManager-CQORubp9.mjs.map} +1 -1
  143. package/dist/{secretsManager-Cwwgs81g.cjs → secretsManager-RAGcEDX1.cjs} +2 -2
  144. package/dist/{secretsManager-Cwwgs81g.cjs.map → secretsManager-RAGcEDX1.cjs.map} +1 -1
  145. package/dist/{seeds-CbJSne1A.mjs → seeds-BvxWUGKJ.mjs} +32 -6
  146. package/dist/seeds-BvxWUGKJ.mjs.map +1 -0
  147. package/dist/{seeds-BjXW-Krf.cjs → seeds-Df4BI-T3.cjs} +32 -6
  148. package/dist/seeds-Df4BI-T3.cjs.map +1 -0
  149. package/dist/{sniffer-DQGBYY8i.mjs → sniffer-BqjXvcaK.mjs} +77 -11
  150. package/dist/sniffer-BqjXvcaK.mjs.map +1 -0
  151. package/dist/{sniffer-NPD1dyab.cjs → sniffer-DZeQJKty.cjs} +100 -10
  152. package/dist/sniffer-DZeQJKty.cjs.map +1 -0
  153. package/dist/{stack-BFq3K-_z.mjs → stack-A6MAFZ6u.mjs} +3 -3
  154. package/dist/{stack-BFq3K-_z.mjs.map → stack-A6MAFZ6u.mjs.map} +1 -1
  155. package/dist/{stack-DOVvEd1T.cjs → stack-BpkZEEfB.cjs} +3 -3
  156. package/dist/{stack-DOVvEd1T.cjs.map → stack-BpkZEEfB.cjs.map} +1 -1
  157. package/dist/{stages-BEn2ArJZ.mjs → stages-6SEoRA4x.mjs} +6 -5
  158. package/dist/stages-6SEoRA4x.mjs.map +1 -0
  159. package/dist/{stages-Cw6urZyJ.cjs → stages-DH5z89eT.cjs} +6 -5
  160. package/dist/stages-DH5z89eT.cjs.map +1 -0
  161. package/dist/target/public.cjs +1 -1
  162. package/dist/target/public.d.cts +2 -2
  163. package/dist/target/public.d.mts +2 -2
  164. package/dist/target/public.mjs +1 -1
  165. package/dist/{types-dipJRmKV.d.cts → types-DipUP9zp.d.cts} +82 -11
  166. package/dist/types-DipUP9zp.d.cts.map +1 -0
  167. package/dist/{types-bkqVscdN.d.mts → types-Drpl9HX9.d.mts} +82 -11
  168. package/dist/types-Drpl9HX9.d.mts.map +1 -0
  169. package/dist/workspace/index.cjs +1 -1
  170. package/dist/workspace/index.d.cts +2 -2
  171. package/dist/workspace/index.d.mts +2 -2
  172. package/dist/workspace/index.mjs +1 -1
  173. package/dist/{workspace-CqFUjObk.mjs → workspace-BBeT6Pil.mjs} +63 -7
  174. package/dist/workspace-BBeT6Pil.mjs.map +1 -0
  175. package/dist/{workspace-DDkm4y6D.cjs → workspace-BJ78Bgvm.cjs} +74 -6
  176. package/dist/workspace-BJ78Bgvm.cjs.map +1 -0
  177. package/package.json +16 -15
  178. package/dist/S3StateStore-CoGk6ZKU.cjs.map +0 -1
  179. package/dist/S3StateStore-DP_7Q4yb.mjs.map +0 -1
  180. package/dist/SSMStateStore-Bo6_KDYY.mjs.map +0 -1
  181. package/dist/SSMStateStore-D9s4_Do3.cjs.map +0 -1
  182. package/dist/awsStore-B5We7bMg.cjs.map +0 -1
  183. package/dist/awsStore-BWa1KNY-.mjs.map +0 -1
  184. package/dist/deploy-BdN0ym2i.cjs.map +0 -1
  185. package/dist/github-CsMloeSL.cjs.map +0 -1
  186. package/dist/github-DuuvqI3Z.mjs.map +0 -1
  187. package/dist/migrate-BdkoNdIu.cjs +0 -94
  188. package/dist/migrate-BdkoNdIu.cjs.map +0 -1
  189. package/dist/migrate-CsPFHgTs.mjs +0 -94
  190. package/dist/migrate-CsPFHgTs.mjs.map +0 -1
  191. package/dist/seeds-BjXW-Krf.cjs.map +0 -1
  192. package/dist/seeds-CbJSne1A.mjs.map +0 -1
  193. package/dist/sniffer-DQGBYY8i.mjs.map +0 -1
  194. package/dist/sniffer-NPD1dyab.cjs.map +0 -1
  195. package/dist/stages-BEn2ArJZ.mjs.map +0 -1
  196. package/dist/stages-Cw6urZyJ.cjs.map +0 -1
  197. package/dist/types-bkqVscdN.d.mts.map +0 -1
  198. package/dist/types-dipJRmKV.d.cts.map +0 -1
  199. package/dist/workspace-CqFUjObk.mjs.map +0 -1
  200. package/dist/workspace-DDkm4y6D.cjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate-DH-lOK3j.mjs","names":[],"sources":["../src/secrets/migrate.ts"],"sourcesContent":["/**\n * `gkm secrets:migrate` — copy a deployed stage's secrets from the store\n * `secrets.store` names to another one, whole: service passwords, URLs and\n * custom keys alike, so nothing is regenerated and running services keep\n * their credentials.\n *\n * It copies, reads the copy back and compares it, and nothing more. The source\n * is never deleted, and gkm.config.ts is not edited: pointing `secrets.store`\n * at the new store is the switch, and the old copy is deleted by hand — with\n * the command this prints — once deploys read from the new one. Run again, a\n * target already holding the same secrets is verified and left alone.\n */\n\nimport { isDeepStrictEqual } from 'node:util';\n\nimport { loadWorkspaceSettings } from '../config.js';\nimport { GkmError } from '../errors';\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport { secretsParameterName } from './aws.js';\nimport { secretsManagerSecretName } from './secretsManager.js';\nimport {\n\tSECRETS_STORE_PROVIDERS,\n\ttype SecretsStore,\n\ttype SecretsStoreConfig,\n\ttype SecretsStoreOptions,\n\ttype SecretsStoreProvider,\n\ts3SecretsLocation,\n\tsecretsStoreFor,\n\tstoreFromConfig,\n\tUnknownSecretsStoreProvider,\n} from './store.js';\nimport type { StageSecrets } from './types.js';\n\nconst logger = console;\n\nexport interface SecretsMigrateOptions {\n\tstage: string;\n\t/** The store to copy to: `'file'`, `'s3'`, `'ssm'` or `'secrets-manager'`. */\n\tto: string;\n\t/**\n\t * The target's AWS region; defaults to the configured store's — for `s3`,\n\t * to the S3 state's first.\n\t */\n\tregion?: string;\n\t/** Overwrite a stage the target already holds different secrets for. */\n\tforce?: boolean;\n\t/** The AWS profile for the stage's account, for both stores. */\n\tprofile?: string;\n}\n\n/** An AWS store to copy to, with no region given and none configured. */\nexport class MigrateTargetNeedsRegion extends GkmError {\n\tconstructor(readonly to: string) {\n\t\tsuper(\n\t\t\t`Copying to ${to} needs a region, and secrets.store has none to reuse. ` +\n\t\t\t\t`Pass it: gkm secrets:migrate --to ${to} --region <region> --stage <stage>.`,\n\t\t);\n\t\tthis.name = 'MigrateTargetNeedsRegion';\n\t}\n}\n\n/** The target is the store the stage is already read from. */\nexport class MigrateTargetIsSource extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly to: string,\n\t) {\n\t\tsuper(\n\t\t\t`Stage \"${stage}\" is already kept in ${to}, so there is nothing to copy. ` +\n\t\t\t\t'Name a different store with --to, or a different region with --region.',\n\t\t);\n\t\tthis.name = 'MigrateTargetIsSource';\n\t}\n}\n\n/** The configured store holds nothing for the stage. */\nexport class NoSecretsToMigrate extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly store: string,\n\t) {\n\t\tsuper(\n\t\t\t`The ${store} store holds no secrets for stage \"${stage}\", so there is nothing to copy. ` +\n\t\t\t\t`Check secrets.store in gkm.config.ts still names the store the stage is in, ` +\n\t\t\t\t`or create the stage there with gkm secrets:init --stage ${stage}.`,\n\t\t);\n\t\tthis.name = 'NoSecretsToMigrate';\n\t}\n}\n\n/**\n * The target already holds different secrets for the stage, and `--force` was\n * not given.\n */\nexport class MigrateTargetHoldsStage extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly to: string,\n\t) {\n\t\tsuper(\n\t\t\t`The ${to} store already holds different secrets for stage \"${stage}\". ` +\n\t\t\t\t'Pass --force to replace them with the ones being copied.',\n\t\t);\n\t\tthis.name = 'MigrateTargetHoldsStage';\n\t}\n}\n\n/** The copy read back from the target is not what was copied. */\nexport class MigratedSecretsDiffer extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly to: string,\n\t\t/** The keys whose values differ, or that only one side has. */\n\t\treadonly keys: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`The secrets for stage \"${stage}\" read back from ${to} differ from the ones copied` +\n\t\t\t\t(keys.length ? ` (${keys.join(', ')})` : '') +\n\t\t\t\t'. The source is untouched and secrets.store still names it; run the migration again, ' +\n\t\t\t\t'or with --force if something else is writing to the target.',\n\t\t);\n\t\tthis.name = 'MigratedSecretsDiffer';\n\t}\n}\n\n/** The region the configured store is in, where it is an AWS one. */\nfunction configuredRegion(\n\tconfigured: SecretsStoreConfig,\n\tstate: unknown,\n): string | undefined {\n\tif (typeof configured !== 'object') return undefined;\n\tif (configured.provider === 's3') {\n\t\treturn s3SecretsLocation(configured, state).region;\n\t}\n\t// A custom store has no region to reuse.\n\treturn 'region' in configured ? configured.region : undefined;\n}\n\n/** The store a `--to` name and region describe. */\nfunction targetConfig(\n\tto: string,\n\tregion: string | undefined,\n\tconfigured: SecretsStoreConfig,\n\tstate: unknown,\n): SecretsStoreConfig {\n\tif (!(SECRETS_STORE_PROVIDERS as readonly string[]).includes(to)) {\n\t\tthrow new UnknownSecretsStoreProvider(to);\n\t}\n\tconst provider = to as SecretsStoreProvider;\n\tif (provider === 'file') return 'file';\n\n\tconst fallback = configuredRegion(configured, state);\n\n\tif (provider === 's3') {\n\t\t// The project bucket beside an S3 state is in the state's region.\n\t\tconst inherits =\n\t\t\ttypeof state === 'object' &&\n\t\t\tstate !== null &&\n\t\t\t(state as { provider?: unknown }).provider === 's3';\n\t\tconst resolved = region ?? (inherits ? undefined : fallback);\n\t\tif (!resolved && !inherits) throw new MigrateTargetNeedsRegion(provider);\n\t\treturn { provider, ...(resolved ? { region: resolved } : {}) };\n\t}\n\n\tconst resolved = region ?? fallback;\n\tif (!resolved) throw new MigrateTargetNeedsRegion(provider);\n\treturn { provider, region: resolved };\n}\n\n/** Whether two configs name the same place. */\nfunction samePlace(\n\ta: SecretsStoreConfig,\n\tb: SecretsStoreConfig,\n\tstate: unknown,\n): boolean {\n\tif (typeof a === 'string' || typeof b === 'string') return a === b;\n\tif (a.provider !== b.provider) return false;\n\tif (a.provider === 's3' && b.provider === 's3') {\n\t\treturn isDeepStrictEqual(\n\t\t\ts3SecretsLocation(a, state),\n\t\t\ts3SecretsLocation(b, state),\n\t\t);\n\t}\n\treturn 'region' in a && 'region' in b && a.region === b.region;\n}\n\n/** The `secrets.store` value to write in gkm.config.ts. */\nfunction configLine(target: SecretsStoreConfig): string {\n\tif (typeof target === 'string') return `'${target}'`;\n\tconst region =\n\t\t'region' in target && target.region ? `, region: '${target.region}'` : '';\n\treturn `{ provider: '${String(target.provider)}'${region} }`;\n}\n\n/** The keys whose values differ between two documents, as `section.KEY`. */\nexport function differingKeys(\n\ta: StageSecrets,\n\tb: StageSecrets | null,\n): string[] {\n\tconst keys: string[] = [];\n\tfor (const section of ['custom', 'urls', 'services'] as const) {\n\t\tconst left = a[section] as Record<string, unknown>;\n\t\tconst right = (b?.[section] ?? {}) as Record<string, unknown>;\n\t\tfor (const key of new Set([...Object.keys(left), ...Object.keys(right)])) {\n\t\t\tif (!isDeepStrictEqual(left[key], right[key])) {\n\t\t\t\tkeys.push(`${section}.${key}`);\n\t\t\t}\n\t\t}\n\t}\n\treturn keys.sort();\n}\n\n/**\n * The command that deletes the stage's copy in the source store, for the\n * person to run once deploys read from the target — gkm never runs it.\n */\nexport function deleteSourceCommand(\n\tworkspace: { name: string },\n\tconfigured: SecretsStoreConfig,\n\tsource: SecretsStore,\n\tstage: string,\n\tprofile: string | undefined,\n): string | null {\n\tconst as = profile ? ` --profile ${profile}` : '';\n\tif (configured === 'file') return `rm .gkm/secrets/${stage}.json`;\n\tswitch (configured.provider) {\n\t\tcase 'ssm':\n\t\t\treturn `aws ssm delete-parameter --name ${secretsParameterName(workspace.name, stage)} --region ${configured.region}${as}`;\n\t\tcase 'secrets-manager':\n\t\t\treturn `aws secretsmanager delete-secret --secret-id ${secretsManagerSecretName(workspace.name, stage)} --region ${configured.region}${as}`;\n\t\tcase 's3': {\n\t\t\tconst location = (\n\t\t\t\tsource as { location?(stage: string): string }\n\t\t\t).location?.(stage);\n\t\t\tif (!location) return null;\n\t\t\t// Every version, as the bucket keeps them: only a delete marker is added.\n\t\t\treturn `aws s3 rm ${location}${as}`;\n\t\t}\n\t\tdefault:\n\t\t\treturn null;\n\t}\n}\n\nexport async function secretsMigrateCommand(\n\toptions: SecretsMigrateOptions,\n): Promise<void> {\n\tconst { stage, to } = options;\n\tconst workspace = await loadWorkspaceSettings();\n\tassertDeployedStage(workspace.stages, stage);\n\n\tconst configured = workspace.secrets.store ?? 'file';\n\tconst target = targetConfig(to, options.region, configured, workspace.state);\n\tif (samePlace(configured, target, workspace.state)) {\n\t\tthrow new MigrateTargetIsSource(stage, to);\n\t}\n\n\tconst storeOptions: SecretsStoreOptions = options.profile\n\t\t? { profile: options.profile }\n\t\t: {};\n\tconst source = await secretsStoreFor(workspace, stage, storeOptions);\n\tconst destination = await storeFromConfig(workspace, target, storeOptions);\n\n\tconst secrets = await source.read(stage);\n\tif (!secrets) throw new NoSecretsToMigrate(stage, source.name);\n\n\tconst held = await destination.read(stage);\n\tconst already = held !== null && isDeepStrictEqual(held, secrets);\n\tif (held && !already && !options.force) {\n\t\tthrow new MigrateTargetHoldsStage(stage, destination.name);\n\t}\n\tif (!already) await destination.write(stage, secrets);\n\n\t// Read back through a store of its own, so nothing cached answers.\n\tconst copied = await (\n\t\tawait storeFromConfig(workspace, target, storeOptions)\n\t).read(stage);\n\tif (!isDeepStrictEqual(copied, secrets)) {\n\t\tthrow new MigratedSecretsDiffer(\n\t\t\tstage,\n\t\t\tdestination.name,\n\t\t\tdifferingKeys(secrets, copied),\n\t\t);\n\t}\n\n\tconst count =\n\t\tObject.keys(secrets.custom).length +\n\t\tObject.keys(secrets.urls).length +\n\t\tObject.keys(secrets.services).length;\n\tlogger.log(\n\t\talready\n\t\t\t? `\\n✓ ${destination.name} already holds the same secrets for stage \"${stage}\" as ${source.name}; nothing copied`\n\t\t\t: `\\n✓ Copied the secrets for stage \"${stage}\" from ${source.name} to ${destination.name}`,\n\t);\n\tlogger.log(\n\t\t` Verified: ${count} keys read back from ${destination.name}, equal.`,\n\t);\n\tlogger.log(\n\t\t`\\n Next: set secrets.store to ${configLine(target)} in gkm.config.ts, so commands and deploys read from ${destination.name}.`,\n\t);\n\tconst remove = deleteSourceCommand(\n\t\tworkspace,\n\t\tconfigured,\n\t\tsource,\n\t\tstage,\n\t\toptions.profile,\n\t);\n\tlogger.log(\n\t\t` The copy in ${source.name} is left as it was. Once deploys read from ${destination.name}, delete it` +\n\t\t\t(remove ? ` with:\\n\\n ${remove}\\n` : '.'),\n\t);\n}\n"],"mappings":"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"}
package/dist/openapi.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env -S npx tsx
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const require_derive = require("./derive-DAbo49sm.cjs");
4
- const require_workspace = require("./workspace-DDkm4y6D.cjs");
4
+ const require_workspace = require("./workspace-BJ78Bgvm.cjs");
5
5
  const require_config = require("./config.cjs");
6
6
  const require_EndpointGenerator = require("./EndpointGenerator-De03t1hd.cjs");
7
7
  const require_OpenApiTsGenerator = require("./OpenApiTsGenerator-CS_RtWOz.cjs");
@@ -1,4 +1,4 @@
1
- import { a as Routes, i as OpenApiConfig } from "./types-dipJRmKV.cjs";
1
+ import { a as Routes, i as OpenApiConfig } from "./types-DipUP9zp.cjs";
2
2
  import { Endpoint } from "@geekmidas/constructs/endpoints";
3
3
  //#region src/generators/clientTelemetry.d.ts
4
4
  type ClientTelemetryDefault = boolean | {
@@ -1,4 +1,4 @@
1
- import { a as Routes, i as OpenApiConfig } from "./types-bkqVscdN.mjs";
1
+ import { a as Routes, i as OpenApiConfig } from "./types-Drpl9HX9.mjs";
2
2
  import { Endpoint } from "@geekmidas/constructs/endpoints";
3
3
  //#region src/generators/clientTelemetry.d.ts
4
4
  type ClientTelemetryDefault = boolean | {
package/dist/openapi.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env -S npx tsx
2
2
  import { t as appKey } from "./derive-B4vDpzgP.mjs";
3
- import { o as appConstructGlobs } from "./workspace-CqFUjObk.mjs";
3
+ import { o as appConstructGlobs } from "./workspace-BBeT6Pil.mjs";
4
4
  import { loadWorkspaceConfig } from "./config.mjs";
5
5
  import { a as telemetryArgs, r as parseTelemetryFlag, t as EndpointGenerator } from "./EndpointGenerator-Bor2tZCm.mjs";
6
6
  import { t as OpenApiTsGenerator } from "./OpenApiTsGenerator-DWAF-7Fu.mjs";
@@ -0,0 +1,206 @@
1
+ const require_errors = require("./errors-DMC4Umsb.cjs");
2
+ require("./seeds-Df4BI-T3.cjs");
3
+ let node_crypto = require("node:crypto");
4
+ //#region src/providers/projectBucket.ts
5
+ /**
6
+ * The project bucket: one S3 bucket per project and AWS account, created and
7
+ * owned by gkm, that holds what gkm keeps for every stage — the deploy state
8
+ * under `<prefix>/<project>/<stage>/`, and anything else gkm writes per stage
9
+ * beside it.
10
+ *
11
+ * Nothing can record its name — the deploy state lives in it — so the name is
12
+ * worked out the same way every time: `gkm-<scope>-<account id>`, the scope
13
+ * being the one the `s3` objects provider names its buckets with. The account
14
+ * id keeps it from clashing with another account's project of the same name
15
+ * without a random suffix; a clash that happens anyway (HeadBucket 403) is
16
+ * refused, never adopted.
17
+ *
18
+ * Created with versioning, SSE-S3, every public access blocked, ownership
19
+ * enforced for the bucket owner, a lifecycle that expires old versions, and
20
+ * the tag `gkm:project=<identity key>`.
21
+ */
22
+ /** What the project part may take: 63 less `gkm-`, `-` and a 12-digit id. */
23
+ const SCOPE_MAX = 46;
24
+ /** The tag the bucket is created with, as the `s3` provider tags its own. */
25
+ const PROJECT_BUCKET_TAG = "gkm:project";
26
+ /** The bucket's name is held by another account, or cannot be reached. */
27
+ var ProjectBucketTaken = class extends require_errors.GkmError {
28
+ bucket;
29
+ status;
30
+ constructor(bucket, status) {
31
+ super(`The project bucket '${bucket}' ${status === 301 ? "is in another region than the one configured" : "exists and this account cannot reach it — another account holds the name"} (HTTP ${status}). Name a bucket you own instead — in gkm.config.ts: state: { provider: 's3', bucket: '<your bucket>', region: '<its region>' } — or run with the credentials of the account the name is for.`);
32
+ this.bucket = bucket;
33
+ this.status = status;
34
+ this.name = "ProjectBucketTaken";
35
+ }
36
+ };
37
+ /** The running credentials may not create the project bucket. */
38
+ var ProjectBucketNotCreatable = class extends require_errors.GkmError {
39
+ bucket;
40
+ reason;
41
+ constructor(bucket, reason) {
42
+ super(`The project bucket '${bucket}' does not exist yet, and these credentials cannot create it (${reason}). Run the first deploy with credentials allowed s3:CreateBucket and the bucket put-config actions on it — \`gkm deploy:github\` grants them to the CI role — or name an existing bucket: state: { provider: 's3', bucket, region }.`);
43
+ this.bucket = bucket;
44
+ this.reason = reason;
45
+ this.name = "ProjectBucketNotCreatable";
46
+ }
47
+ };
48
+ /** {@link projectBucket}, from a deploy identity's scope. */
49
+ function projectBucketName(scope, accountId) {
50
+ let part = scope.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
51
+ if (part.length > SCOPE_MAX) {
52
+ const hash = (0, node_crypto.createHash)("sha256").update(scope).digest("hex").slice(0, 6);
53
+ part = `${part.slice(0, 39).replace(/-+$/, "")}-${hash}`;
54
+ }
55
+ return `gkm-${part}-${accountId}`;
56
+ }
57
+ /** The account the credentials are in — what the bucket is named by. */
58
+ async function callerAccountId(sts) {
59
+ const { GetCallerIdentityCommand } = await import("@aws-sdk/client-sts");
60
+ const { Account } = await sts.send(new GetCallerIdentityCommand({}));
61
+ return Account ?? "";
62
+ }
63
+ /**
64
+ * The bucket's whole lifecycle configuration. A put replaces every rule the
65
+ * bucket has, so this is the one place the set is built: the bucket's own
66
+ * noncurrent-version expiry, and any per-prefix expiry passed in.
67
+ */
68
+ function projectBucketLifecycle(expiries = []) {
69
+ return [{
70
+ ID: "gkm-noncurrent-versions",
71
+ Status: "Enabled",
72
+ Filter: { Prefix: "" },
73
+ NoncurrentVersionExpiration: { NoncurrentDays: 90 },
74
+ Expiration: { ExpiredObjectDeleteMarker: true }
75
+ }, ...expiries.map((expiry) => ({
76
+ ID: expiry.id,
77
+ Status: "Enabled",
78
+ Filter: { Prefix: expiry.prefix },
79
+ Expiration: { Days: expiry.days }
80
+ }))];
81
+ }
82
+ /**
83
+ * Whether `bucket` is this account's (`ours`) or does not exist (`none`).
84
+ * Mapped by status: a HEAD has no body, so SDK v3 names a 301, 400 or 403
85
+ * `Unknown`.
86
+ *
87
+ * @throws {ProjectBucketTaken} on a 403 or 301
88
+ */
89
+ async function findProjectBucket(s3, bucket) {
90
+ const { HeadBucketCommand } = await import("@aws-sdk/client-s3");
91
+ try {
92
+ await s3.send(new HeadBucketCommand({ Bucket: bucket }));
93
+ return "ours";
94
+ } catch (error) {
95
+ const status = error.$metadata?.httpStatusCode;
96
+ if (status === 404) return "none";
97
+ if (status === 403 || status === 301) throw new ProjectBucketTaken(bucket, status);
98
+ throw error;
99
+ }
100
+ }
101
+ /**
102
+ * The project bucket, created and configured if it does not exist yet.
103
+ * Returns whether this call created it. One that exists is used as it is.
104
+ *
105
+ * @throws {ProjectBucketTaken} when another account holds the name
106
+ * @throws {ProjectBucketNotCreatable} when the credentials may not create it
107
+ */
108
+ async function ensureProjectBucket(s3, options) {
109
+ const { bucket: Bucket, region } = options;
110
+ if (await findProjectBucket(s3, Bucket) === "ours") return { created: false };
111
+ const S3 = await import("@aws-sdk/client-s3");
112
+ try {
113
+ await s3.send(new S3.CreateBucketCommand({
114
+ Bucket,
115
+ ObjectOwnership: "BucketOwnerEnforced",
116
+ ...region === "us-east-1" ? {} : { CreateBucketConfiguration: { LocationConstraint: region } }
117
+ }));
118
+ } catch (error) {
119
+ const failure = error;
120
+ const code = failure.Code ?? failure.name;
121
+ if (code !== "BucketAlreadyOwnedByYou") {
122
+ if (code === "BucketAlreadyExists") throw new ProjectBucketTaken(Bucket, 409);
123
+ if (code === "AccessDenied" || failure.$metadata?.httpStatusCode === 403) throw new ProjectBucketNotCreatable(Bucket, failure.message ?? "AccessDenied");
124
+ throw error;
125
+ }
126
+ }
127
+ await s3.send(new S3.PutPublicAccessBlockCommand({
128
+ Bucket,
129
+ PublicAccessBlockConfiguration: {
130
+ BlockPublicAcls: true,
131
+ IgnorePublicAcls: true,
132
+ BlockPublicPolicy: true,
133
+ RestrictPublicBuckets: true
134
+ }
135
+ }));
136
+ await s3.send(new S3.PutBucketOwnershipControlsCommand({
137
+ Bucket,
138
+ OwnershipControls: { Rules: [{ ObjectOwnership: "BucketOwnerEnforced" }] }
139
+ }));
140
+ await s3.send(new S3.PutBucketEncryptionCommand({
141
+ Bucket,
142
+ ServerSideEncryptionConfiguration: { Rules: [{ ApplyServerSideEncryptionByDefault: { SSEAlgorithm: "AES256" } }] }
143
+ }));
144
+ await s3.send(new S3.PutBucketVersioningCommand({
145
+ Bucket,
146
+ VersioningConfiguration: { Status: "Enabled" }
147
+ }));
148
+ await s3.send(new S3.PutBucketLifecycleConfigurationCommand({
149
+ Bucket,
150
+ LifecycleConfiguration: { Rules: projectBucketLifecycle(options.expiries) }
151
+ }));
152
+ await s3.send(new S3.PutBucketTaggingCommand({
153
+ Bucket,
154
+ Tagging: { TagSet: [{
155
+ Key: PROJECT_BUCKET_TAG,
156
+ Value: options.identity.key
157
+ }] }
158
+ }));
159
+ return { created: true };
160
+ }
161
+ /**
162
+ * The IAM actions creating and configuring the project bucket takes — what a
163
+ * role that runs the first deploy needs on it, beside object access.
164
+ */
165
+ const PROJECT_BUCKET_CREATE_ACTIONS = [
166
+ "s3:CreateBucket",
167
+ "s3:PutBucketVersioning",
168
+ "s3:PutEncryptionConfiguration",
169
+ "s3:PutBucketPublicAccessBlock",
170
+ "s3:PutBucketOwnershipControls",
171
+ "s3:PutLifecycleConfiguration",
172
+ "s3:PutBucketTagging"
173
+ ];
174
+ //#endregion
175
+ Object.defineProperty(exports, "PROJECT_BUCKET_CREATE_ACTIONS", {
176
+ enumerable: true,
177
+ get: function() {
178
+ return PROJECT_BUCKET_CREATE_ACTIONS;
179
+ }
180
+ });
181
+ Object.defineProperty(exports, "callerAccountId", {
182
+ enumerable: true,
183
+ get: function() {
184
+ return callerAccountId;
185
+ }
186
+ });
187
+ Object.defineProperty(exports, "ensureProjectBucket", {
188
+ enumerable: true,
189
+ get: function() {
190
+ return ensureProjectBucket;
191
+ }
192
+ });
193
+ Object.defineProperty(exports, "findProjectBucket", {
194
+ enumerable: true,
195
+ get: function() {
196
+ return findProjectBucket;
197
+ }
198
+ });
199
+ Object.defineProperty(exports, "projectBucketName", {
200
+ enumerable: true,
201
+ get: function() {
202
+ return projectBucketName;
203
+ }
204
+ });
205
+
206
+ //# sourceMappingURL=projectBucket-BjeDcAYy.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projectBucket-BjeDcAYy.cjs","names":["GkmError","createHash"],"sources":["../src/providers/projectBucket.ts"],"sourcesContent":["/**\n * The project bucket: one S3 bucket per project and AWS account, created and\n * owned by gkm, that holds what gkm keeps for every stage — the deploy state\n * under `<prefix>/<project>/<stage>/`, and anything else gkm writes per stage\n * beside it.\n *\n * Nothing can record its name — the deploy state lives in it — so the name is\n * worked out the same way every time: `gkm-<scope>-<account id>`, the scope\n * being the one the `s3` objects provider names its buckets with. The account\n * id keeps it from clashing with another account's project of the same name\n * without a random suffix; a clash that happens anyway (HeadBucket 403) is\n * refused, never adopted.\n *\n * Created with versioning, SSE-S3, every public access blocked, ownership\n * enforced for the bucket owner, a lifecycle that expires old versions, and\n * the tag `gkm:project=<identity key>`.\n */\n\nimport { createHash } from 'node:crypto';\nimport type {\n\tBucketLocationConstraint,\n\tLifecycleRule,\n\tS3Client,\n} from '@aws-sdk/client-s3';\nimport type { STSClient } from '@aws-sdk/client-sts';\nimport { type DeployIdentity, deployIdentity } from '../deploy/identity.js';\nimport { GkmError } from '../errors';\n\n/** S3's limit on a bucket name. */\nconst BUCKET_NAME_MAX = 63;\n/** What the project part may take: 63 less `gkm-`, `-` and a 12-digit id. */\nconst SCOPE_MAX = BUCKET_NAME_MAX - 'gkm-'.length - 1 - 12;\n\n/** The tag the bucket is created with, as the `s3` provider tags its own. */\nexport const PROJECT_BUCKET_TAG = 'gkm:project';\n\n/** How long a replaced or deleted object's old version is kept. */\nexport const NONCURRENT_VERSION_DAYS = 90;\n\n/** The bucket's name is held by another account, or cannot be reached. */\nexport class ProjectBucketTaken extends GkmError {\n\tconstructor(\n\t\treadonly bucket: string,\n\t\t/** HeadBucket's status: 403 another account's, 301 another region's. */\n\t\treadonly status: number,\n\t) {\n\t\tconst why =\n\t\t\tstatus === 301\n\t\t\t\t? 'is in another region than the one configured'\n\t\t\t\t: 'exists and this account cannot reach it — another account holds the name';\n\t\tsuper(\n\t\t\t`The project bucket '${bucket}' ${why} (HTTP ${status}). Name a bucket ` +\n\t\t\t\t\"you own instead — in gkm.config.ts: state: { provider: 's3', \" +\n\t\t\t\t\"bucket: '<your bucket>', region: '<its region>' } — or run with the \" +\n\t\t\t\t'credentials of the account the name is for.',\n\t\t);\n\t\tthis.name = 'ProjectBucketTaken';\n\t}\n}\n\n/** The running credentials may not create the project bucket. */\nexport class ProjectBucketNotCreatable extends GkmError {\n\tconstructor(\n\t\treadonly bucket: string,\n\t\treadonly reason: string,\n\t) {\n\t\tsuper(\n\t\t\t`The project bucket '${bucket}' does not exist yet, and these ` +\n\t\t\t\t`credentials cannot create it (${reason}). Run the first deploy with ` +\n\t\t\t\t'credentials allowed s3:CreateBucket and the bucket put-config ' +\n\t\t\t\t'actions on it — `gkm deploy:github` grants them to the CI role — ' +\n\t\t\t\t\"or name an existing bucket: state: { provider: 's3', bucket, region }.\",\n\t\t);\n\t\tthis.name = 'ProjectBucketNotCreatable';\n\t}\n}\n\n/**\n * The project bucket's name: `gkm-<scope>-<account id>`, lowercase and\n * DNS-safe. A scope too long to fit keeps its start and ends in a hash of\n * the whole, so two long scopes never share a name.\n */\nexport function projectBucket(\n\tworkspace: { name: string; deploy?: { namespace?: string } },\n\toptions: { accountId: string },\n): string {\n\tconst { scope } = deployIdentity(workspace, '');\n\treturn projectBucketName(scope, options.accountId);\n}\n\n/** {@link projectBucket}, from a deploy identity's scope. */\nexport function projectBucketName(scope: string, accountId: string): string {\n\tlet part = scope\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9]+/g, '-')\n\t\t.replace(/^-+|-+$/g, '');\n\tif (part.length > SCOPE_MAX) {\n\t\tconst hash = createHash('sha256').update(scope).digest('hex').slice(0, 6);\n\t\tpart = `${part.slice(0, SCOPE_MAX - 7).replace(/-+$/, '')}-${hash}`;\n\t}\n\treturn `gkm-${part}-${accountId}`;\n}\n\n/** The account the credentials are in — what the bucket is named by. */\nexport async function callerAccountId(sts: STSClient): Promise<string> {\n\tconst { GetCallerIdentityCommand } = await import('@aws-sdk/client-sts');\n\tconst { Account } = await sts.send(new GetCallerIdentityCommand({}));\n\treturn Account ?? '';\n}\n\n/** An expiry for one stage's prefix, added beside the bucket's own rule. */\nexport interface PrefixExpiry {\n\t/** Rule id, unique in the bucket — e.g. `backups-production`. */\n\tid: string;\n\t/** Key prefix it applies to — e.g. `gkm/acme/production/backups/`. */\n\tprefix: string;\n\t/** Days after which a current object expires. */\n\tdays: number;\n}\n\n/**\n * The bucket's whole lifecycle configuration. A put replaces every rule the\n * bucket has, so this is the one place the set is built: the bucket's own\n * noncurrent-version expiry, and any per-prefix expiry passed in.\n */\nexport function projectBucketLifecycle(\n\texpiries: readonly PrefixExpiry[] = [],\n): LifecycleRule[] {\n\treturn [\n\t\t{\n\t\t\tID: 'gkm-noncurrent-versions',\n\t\t\tStatus: 'Enabled',\n\t\t\tFilter: { Prefix: '' },\n\t\t\tNoncurrentVersionExpiration: { NoncurrentDays: NONCURRENT_VERSION_DAYS },\n\t\t\t// A lock is deleted every run; its markers go once nothing is under them.\n\t\t\tExpiration: { ExpiredObjectDeleteMarker: true },\n\t\t},\n\t\t...expiries.map(\n\t\t\t(expiry): LifecycleRule => ({\n\t\t\t\tID: expiry.id,\n\t\t\t\tStatus: 'Enabled',\n\t\t\t\tFilter: { Prefix: expiry.prefix },\n\t\t\t\tExpiration: { Days: expiry.days },\n\t\t\t}),\n\t\t),\n\t];\n}\n\ninterface S3Failure {\n\tname?: string;\n\tCode?: string;\n\tmessage?: string;\n\t$metadata?: { httpStatusCode?: number };\n}\n\n/**\n * Whether `bucket` is this account's (`ours`) or does not exist (`none`).\n * Mapped by status: a HEAD has no body, so SDK v3 names a 301, 400 or 403\n * `Unknown`.\n *\n * @throws {ProjectBucketTaken} on a 403 or 301\n */\nexport async function findProjectBucket(\n\ts3: S3Client,\n\tbucket: string,\n): Promise<'ours' | 'none'> {\n\tconst { HeadBucketCommand } = await import('@aws-sdk/client-s3');\n\ttry {\n\t\tawait s3.send(new HeadBucketCommand({ Bucket: bucket }));\n\t\treturn 'ours';\n\t} catch (error) {\n\t\tconst status = (error as S3Failure).$metadata?.httpStatusCode;\n\t\tif (status === 404) return 'none';\n\t\tif (status === 403 || status === 301) {\n\t\t\tthrow new ProjectBucketTaken(bucket, status);\n\t\t}\n\t\tthrow error;\n\t}\n}\n\nexport interface EnsureProjectBucketOptions {\n\tbucket: string;\n\tregion: string;\n\t/** The deploy identity the bucket is tagged with. */\n\tidentity: Pick<DeployIdentity, 'key'>;\n\t/** Per-prefix expiries beside the bucket's own rule. */\n\texpiries?: readonly PrefixExpiry[];\n}\n\n/**\n * The project bucket, created and configured if it does not exist yet.\n * Returns whether this call created it. One that exists is used as it is.\n *\n * @throws {ProjectBucketTaken} when another account holds the name\n * @throws {ProjectBucketNotCreatable} when the credentials may not create it\n */\nexport async function ensureProjectBucket(\n\ts3: S3Client,\n\toptions: EnsureProjectBucketOptions,\n): Promise<{ created: boolean }> {\n\tconst { bucket: Bucket, region } = options;\n\tif ((await findProjectBucket(s3, Bucket)) === 'ours') {\n\t\treturn { created: false };\n\t}\n\n\tconst S3 = await import('@aws-sdk/client-s3');\n\ttry {\n\t\tawait s3.send(\n\t\t\tnew S3.CreateBucketCommand({\n\t\t\t\tBucket,\n\t\t\t\tObjectOwnership: 'BucketOwnerEnforced',\n\t\t\t\t// us-east-1 is the one region named by leaving it out.\n\t\t\t\t...(region === 'us-east-1'\n\t\t\t\t\t? {}\n\t\t\t\t\t: {\n\t\t\t\t\t\t\tCreateBucketConfiguration: {\n\t\t\t\t\t\t\t\tLocationConstraint: region as BucketLocationConstraint,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}),\n\t\t\t}),\n\t\t);\n\t} catch (error) {\n\t\tconst failure = error as S3Failure;\n\t\tconst code = failure.Code ?? failure.name;\n\t\t// Another run created it a moment ago: configure it all the same.\n\t\tif (code !== 'BucketAlreadyOwnedByYou') {\n\t\t\tif (code === 'BucketAlreadyExists') {\n\t\t\t\tthrow new ProjectBucketTaken(Bucket, 409);\n\t\t\t}\n\t\t\tif (\n\t\t\t\tcode === 'AccessDenied' ||\n\t\t\t\tfailure.$metadata?.httpStatusCode === 403\n\t\t\t) {\n\t\t\t\tthrow new ProjectBucketNotCreatable(\n\t\t\t\t\tBucket,\n\t\t\t\t\tfailure.message ?? 'AccessDenied',\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tawait s3.send(\n\t\tnew S3.PutPublicAccessBlockCommand({\n\t\t\tBucket,\n\t\t\tPublicAccessBlockConfiguration: {\n\t\t\t\tBlockPublicAcls: true,\n\t\t\t\tIgnorePublicAcls: true,\n\t\t\t\tBlockPublicPolicy: true,\n\t\t\t\tRestrictPublicBuckets: true,\n\t\t\t},\n\t\t}),\n\t);\n\tawait s3.send(\n\t\tnew S3.PutBucketOwnershipControlsCommand({\n\t\t\tBucket,\n\t\t\tOwnershipControls: {\n\t\t\t\tRules: [{ ObjectOwnership: 'BucketOwnerEnforced' }],\n\t\t\t},\n\t\t}),\n\t);\n\tawait s3.send(\n\t\tnew S3.PutBucketEncryptionCommand({\n\t\t\tBucket,\n\t\t\tServerSideEncryptionConfiguration: {\n\t\t\t\tRules: [\n\t\t\t\t\t{ ApplyServerSideEncryptionByDefault: { SSEAlgorithm: 'AES256' } },\n\t\t\t\t],\n\t\t\t},\n\t\t}),\n\t);\n\tawait s3.send(\n\t\tnew S3.PutBucketVersioningCommand({\n\t\t\tBucket,\n\t\t\tVersioningConfiguration: { Status: 'Enabled' },\n\t\t}),\n\t);\n\tawait s3.send(\n\t\tnew S3.PutBucketLifecycleConfigurationCommand({\n\t\t\tBucket,\n\t\t\tLifecycleConfiguration: {\n\t\t\t\tRules: projectBucketLifecycle(options.expiries),\n\t\t\t},\n\t\t}),\n\t);\n\tawait s3.send(\n\t\tnew S3.PutBucketTaggingCommand({\n\t\t\tBucket,\n\t\t\tTagging: {\n\t\t\t\tTagSet: [{ Key: PROJECT_BUCKET_TAG, Value: options.identity.key }],\n\t\t\t},\n\t\t}),\n\t);\n\treturn { created: true };\n}\n\n/**\n * The IAM actions creating and configuring the project bucket takes — what a\n * role that runs the first deploy needs on it, beside object access.\n */\nexport const PROJECT_BUCKET_CREATE_ACTIONS = [\n\t's3:CreateBucket',\n\t's3:PutBucketVersioning',\n\t's3:PutEncryptionConfiguration',\n\t's3:PutBucketPublicAccessBlock',\n\t's3:PutBucketOwnershipControls',\n\t's3:PutLifecycleConfiguration',\n\t's3:PutBucketTagging',\n] as const;\n"],"mappings":"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"}
@@ -0,0 +1,177 @@
1
+ import { t as GkmError } from "./errors-D8Gwp5kU.mjs";
2
+ import "./seeds-BvxWUGKJ.mjs";
3
+ import { createHash } from "node:crypto";
4
+ //#region src/providers/projectBucket.ts
5
+ /**
6
+ * The project bucket: one S3 bucket per project and AWS account, created and
7
+ * owned by gkm, that holds what gkm keeps for every stage — the deploy state
8
+ * under `<prefix>/<project>/<stage>/`, and anything else gkm writes per stage
9
+ * beside it.
10
+ *
11
+ * Nothing can record its name — the deploy state lives in it — so the name is
12
+ * worked out the same way every time: `gkm-<scope>-<account id>`, the scope
13
+ * being the one the `s3` objects provider names its buckets with. The account
14
+ * id keeps it from clashing with another account's project of the same name
15
+ * without a random suffix; a clash that happens anyway (HeadBucket 403) is
16
+ * refused, never adopted.
17
+ *
18
+ * Created with versioning, SSE-S3, every public access blocked, ownership
19
+ * enforced for the bucket owner, a lifecycle that expires old versions, and
20
+ * the tag `gkm:project=<identity key>`.
21
+ */
22
+ /** What the project part may take: 63 less `gkm-`, `-` and a 12-digit id. */
23
+ const SCOPE_MAX = 46;
24
+ /** The tag the bucket is created with, as the `s3` provider tags its own. */
25
+ const PROJECT_BUCKET_TAG = "gkm:project";
26
+ /** The bucket's name is held by another account, or cannot be reached. */
27
+ var ProjectBucketTaken = class extends GkmError {
28
+ bucket;
29
+ status;
30
+ constructor(bucket, status) {
31
+ super(`The project bucket '${bucket}' ${status === 301 ? "is in another region than the one configured" : "exists and this account cannot reach it — another account holds the name"} (HTTP ${status}). Name a bucket you own instead — in gkm.config.ts: state: { provider: 's3', bucket: '<your bucket>', region: '<its region>' } — or run with the credentials of the account the name is for.`);
32
+ this.bucket = bucket;
33
+ this.status = status;
34
+ this.name = "ProjectBucketTaken";
35
+ }
36
+ };
37
+ /** The running credentials may not create the project bucket. */
38
+ var ProjectBucketNotCreatable = class extends GkmError {
39
+ bucket;
40
+ reason;
41
+ constructor(bucket, reason) {
42
+ super(`The project bucket '${bucket}' does not exist yet, and these credentials cannot create it (${reason}). Run the first deploy with credentials allowed s3:CreateBucket and the bucket put-config actions on it — \`gkm deploy:github\` grants them to the CI role — or name an existing bucket: state: { provider: 's3', bucket, region }.`);
43
+ this.bucket = bucket;
44
+ this.reason = reason;
45
+ this.name = "ProjectBucketNotCreatable";
46
+ }
47
+ };
48
+ /** {@link projectBucket}, from a deploy identity's scope. */
49
+ function projectBucketName(scope, accountId) {
50
+ let part = scope.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
51
+ if (part.length > SCOPE_MAX) {
52
+ const hash = createHash("sha256").update(scope).digest("hex").slice(0, 6);
53
+ part = `${part.slice(0, 39).replace(/-+$/, "")}-${hash}`;
54
+ }
55
+ return `gkm-${part}-${accountId}`;
56
+ }
57
+ /** The account the credentials are in — what the bucket is named by. */
58
+ async function callerAccountId(sts) {
59
+ const { GetCallerIdentityCommand } = await import("@aws-sdk/client-sts");
60
+ const { Account } = await sts.send(new GetCallerIdentityCommand({}));
61
+ return Account ?? "";
62
+ }
63
+ /**
64
+ * The bucket's whole lifecycle configuration. A put replaces every rule the
65
+ * bucket has, so this is the one place the set is built: the bucket's own
66
+ * noncurrent-version expiry, and any per-prefix expiry passed in.
67
+ */
68
+ function projectBucketLifecycle(expiries = []) {
69
+ return [{
70
+ ID: "gkm-noncurrent-versions",
71
+ Status: "Enabled",
72
+ Filter: { Prefix: "" },
73
+ NoncurrentVersionExpiration: { NoncurrentDays: 90 },
74
+ Expiration: { ExpiredObjectDeleteMarker: true }
75
+ }, ...expiries.map((expiry) => ({
76
+ ID: expiry.id,
77
+ Status: "Enabled",
78
+ Filter: { Prefix: expiry.prefix },
79
+ Expiration: { Days: expiry.days }
80
+ }))];
81
+ }
82
+ /**
83
+ * Whether `bucket` is this account's (`ours`) or does not exist (`none`).
84
+ * Mapped by status: a HEAD has no body, so SDK v3 names a 301, 400 or 403
85
+ * `Unknown`.
86
+ *
87
+ * @throws {ProjectBucketTaken} on a 403 or 301
88
+ */
89
+ async function findProjectBucket(s3, bucket) {
90
+ const { HeadBucketCommand } = await import("@aws-sdk/client-s3");
91
+ try {
92
+ await s3.send(new HeadBucketCommand({ Bucket: bucket }));
93
+ return "ours";
94
+ } catch (error) {
95
+ const status = error.$metadata?.httpStatusCode;
96
+ if (status === 404) return "none";
97
+ if (status === 403 || status === 301) throw new ProjectBucketTaken(bucket, status);
98
+ throw error;
99
+ }
100
+ }
101
+ /**
102
+ * The project bucket, created and configured if it does not exist yet.
103
+ * Returns whether this call created it. One that exists is used as it is.
104
+ *
105
+ * @throws {ProjectBucketTaken} when another account holds the name
106
+ * @throws {ProjectBucketNotCreatable} when the credentials may not create it
107
+ */
108
+ async function ensureProjectBucket(s3, options) {
109
+ const { bucket: Bucket, region } = options;
110
+ if (await findProjectBucket(s3, Bucket) === "ours") return { created: false };
111
+ const S3 = await import("@aws-sdk/client-s3");
112
+ try {
113
+ await s3.send(new S3.CreateBucketCommand({
114
+ Bucket,
115
+ ObjectOwnership: "BucketOwnerEnforced",
116
+ ...region === "us-east-1" ? {} : { CreateBucketConfiguration: { LocationConstraint: region } }
117
+ }));
118
+ } catch (error) {
119
+ const failure = error;
120
+ const code = failure.Code ?? failure.name;
121
+ if (code !== "BucketAlreadyOwnedByYou") {
122
+ if (code === "BucketAlreadyExists") throw new ProjectBucketTaken(Bucket, 409);
123
+ if (code === "AccessDenied" || failure.$metadata?.httpStatusCode === 403) throw new ProjectBucketNotCreatable(Bucket, failure.message ?? "AccessDenied");
124
+ throw error;
125
+ }
126
+ }
127
+ await s3.send(new S3.PutPublicAccessBlockCommand({
128
+ Bucket,
129
+ PublicAccessBlockConfiguration: {
130
+ BlockPublicAcls: true,
131
+ IgnorePublicAcls: true,
132
+ BlockPublicPolicy: true,
133
+ RestrictPublicBuckets: true
134
+ }
135
+ }));
136
+ await s3.send(new S3.PutBucketOwnershipControlsCommand({
137
+ Bucket,
138
+ OwnershipControls: { Rules: [{ ObjectOwnership: "BucketOwnerEnforced" }] }
139
+ }));
140
+ await s3.send(new S3.PutBucketEncryptionCommand({
141
+ Bucket,
142
+ ServerSideEncryptionConfiguration: { Rules: [{ ApplyServerSideEncryptionByDefault: { SSEAlgorithm: "AES256" } }] }
143
+ }));
144
+ await s3.send(new S3.PutBucketVersioningCommand({
145
+ Bucket,
146
+ VersioningConfiguration: { Status: "Enabled" }
147
+ }));
148
+ await s3.send(new S3.PutBucketLifecycleConfigurationCommand({
149
+ Bucket,
150
+ LifecycleConfiguration: { Rules: projectBucketLifecycle(options.expiries) }
151
+ }));
152
+ await s3.send(new S3.PutBucketTaggingCommand({
153
+ Bucket,
154
+ Tagging: { TagSet: [{
155
+ Key: PROJECT_BUCKET_TAG,
156
+ Value: options.identity.key
157
+ }] }
158
+ }));
159
+ return { created: true };
160
+ }
161
+ /**
162
+ * The IAM actions creating and configuring the project bucket takes — what a
163
+ * role that runs the first deploy needs on it, beside object access.
164
+ */
165
+ const PROJECT_BUCKET_CREATE_ACTIONS = [
166
+ "s3:CreateBucket",
167
+ "s3:PutBucketVersioning",
168
+ "s3:PutEncryptionConfiguration",
169
+ "s3:PutBucketPublicAccessBlock",
170
+ "s3:PutBucketOwnershipControls",
171
+ "s3:PutLifecycleConfiguration",
172
+ "s3:PutBucketTagging"
173
+ ];
174
+ //#endregion
175
+ export { projectBucketName as a, findProjectBucket as i, callerAccountId as n, ensureProjectBucket as r, PROJECT_BUCKET_CREATE_ACTIONS as t };
176
+
177
+ //# sourceMappingURL=projectBucket-DrkEht-j.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projectBucket-DrkEht-j.mjs","names":[],"sources":["../src/providers/projectBucket.ts"],"sourcesContent":["/**\n * The project bucket: one S3 bucket per project and AWS account, created and\n * owned by gkm, that holds what gkm keeps for every stage — the deploy state\n * under `<prefix>/<project>/<stage>/`, and anything else gkm writes per stage\n * beside it.\n *\n * Nothing can record its name — the deploy state lives in it — so the name is\n * worked out the same way every time: `gkm-<scope>-<account id>`, the scope\n * being the one the `s3` objects provider names its buckets with. The account\n * id keeps it from clashing with another account's project of the same name\n * without a random suffix; a clash that happens anyway (HeadBucket 403) is\n * refused, never adopted.\n *\n * Created with versioning, SSE-S3, every public access blocked, ownership\n * enforced for the bucket owner, a lifecycle that expires old versions, and\n * the tag `gkm:project=<identity key>`.\n */\n\nimport { createHash } from 'node:crypto';\nimport type {\n\tBucketLocationConstraint,\n\tLifecycleRule,\n\tS3Client,\n} from '@aws-sdk/client-s3';\nimport type { STSClient } from '@aws-sdk/client-sts';\nimport { type DeployIdentity, deployIdentity } from '../deploy/identity.js';\nimport { GkmError } from '../errors';\n\n/** S3's limit on a bucket name. */\nconst BUCKET_NAME_MAX = 63;\n/** What the project part may take: 63 less `gkm-`, `-` and a 12-digit id. */\nconst SCOPE_MAX = BUCKET_NAME_MAX - 'gkm-'.length - 1 - 12;\n\n/** The tag the bucket is created with, as the `s3` provider tags its own. */\nexport const PROJECT_BUCKET_TAG = 'gkm:project';\n\n/** How long a replaced or deleted object's old version is kept. */\nexport const NONCURRENT_VERSION_DAYS = 90;\n\n/** The bucket's name is held by another account, or cannot be reached. */\nexport class ProjectBucketTaken extends GkmError {\n\tconstructor(\n\t\treadonly bucket: string,\n\t\t/** HeadBucket's status: 403 another account's, 301 another region's. */\n\t\treadonly status: number,\n\t) {\n\t\tconst why =\n\t\t\tstatus === 301\n\t\t\t\t? 'is in another region than the one configured'\n\t\t\t\t: 'exists and this account cannot reach it — another account holds the name';\n\t\tsuper(\n\t\t\t`The project bucket '${bucket}' ${why} (HTTP ${status}). Name a bucket ` +\n\t\t\t\t\"you own instead — in gkm.config.ts: state: { provider: 's3', \" +\n\t\t\t\t\"bucket: '<your bucket>', region: '<its region>' } — or run with the \" +\n\t\t\t\t'credentials of the account the name is for.',\n\t\t);\n\t\tthis.name = 'ProjectBucketTaken';\n\t}\n}\n\n/** The running credentials may not create the project bucket. */\nexport class ProjectBucketNotCreatable extends GkmError {\n\tconstructor(\n\t\treadonly bucket: string,\n\t\treadonly reason: string,\n\t) {\n\t\tsuper(\n\t\t\t`The project bucket '${bucket}' does not exist yet, and these ` +\n\t\t\t\t`credentials cannot create it (${reason}). Run the first deploy with ` +\n\t\t\t\t'credentials allowed s3:CreateBucket and the bucket put-config ' +\n\t\t\t\t'actions on it — `gkm deploy:github` grants them to the CI role — ' +\n\t\t\t\t\"or name an existing bucket: state: { provider: 's3', bucket, region }.\",\n\t\t);\n\t\tthis.name = 'ProjectBucketNotCreatable';\n\t}\n}\n\n/**\n * The project bucket's name: `gkm-<scope>-<account id>`, lowercase and\n * DNS-safe. A scope too long to fit keeps its start and ends in a hash of\n * the whole, so two long scopes never share a name.\n */\nexport function projectBucket(\n\tworkspace: { name: string; deploy?: { namespace?: string } },\n\toptions: { accountId: string },\n): string {\n\tconst { scope } = deployIdentity(workspace, '');\n\treturn projectBucketName(scope, options.accountId);\n}\n\n/** {@link projectBucket}, from a deploy identity's scope. */\nexport function projectBucketName(scope: string, accountId: string): string {\n\tlet part = scope\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9]+/g, '-')\n\t\t.replace(/^-+|-+$/g, '');\n\tif (part.length > SCOPE_MAX) {\n\t\tconst hash = createHash('sha256').update(scope).digest('hex').slice(0, 6);\n\t\tpart = `${part.slice(0, SCOPE_MAX - 7).replace(/-+$/, '')}-${hash}`;\n\t}\n\treturn `gkm-${part}-${accountId}`;\n}\n\n/** The account the credentials are in — what the bucket is named by. */\nexport async function callerAccountId(sts: STSClient): Promise<string> {\n\tconst { GetCallerIdentityCommand } = await import('@aws-sdk/client-sts');\n\tconst { Account } = await sts.send(new GetCallerIdentityCommand({}));\n\treturn Account ?? '';\n}\n\n/** An expiry for one stage's prefix, added beside the bucket's own rule. */\nexport interface PrefixExpiry {\n\t/** Rule id, unique in the bucket — e.g. `backups-production`. */\n\tid: string;\n\t/** Key prefix it applies to — e.g. `gkm/acme/production/backups/`. */\n\tprefix: string;\n\t/** Days after which a current object expires. */\n\tdays: number;\n}\n\n/**\n * The bucket's whole lifecycle configuration. A put replaces every rule the\n * bucket has, so this is the one place the set is built: the bucket's own\n * noncurrent-version expiry, and any per-prefix expiry passed in.\n */\nexport function projectBucketLifecycle(\n\texpiries: readonly PrefixExpiry[] = [],\n): LifecycleRule[] {\n\treturn [\n\t\t{\n\t\t\tID: 'gkm-noncurrent-versions',\n\t\t\tStatus: 'Enabled',\n\t\t\tFilter: { Prefix: '' },\n\t\t\tNoncurrentVersionExpiration: { NoncurrentDays: NONCURRENT_VERSION_DAYS },\n\t\t\t// A lock is deleted every run; its markers go once nothing is under them.\n\t\t\tExpiration: { ExpiredObjectDeleteMarker: true },\n\t\t},\n\t\t...expiries.map(\n\t\t\t(expiry): LifecycleRule => ({\n\t\t\t\tID: expiry.id,\n\t\t\t\tStatus: 'Enabled',\n\t\t\t\tFilter: { Prefix: expiry.prefix },\n\t\t\t\tExpiration: { Days: expiry.days },\n\t\t\t}),\n\t\t),\n\t];\n}\n\ninterface S3Failure {\n\tname?: string;\n\tCode?: string;\n\tmessage?: string;\n\t$metadata?: { httpStatusCode?: number };\n}\n\n/**\n * Whether `bucket` is this account's (`ours`) or does not exist (`none`).\n * Mapped by status: a HEAD has no body, so SDK v3 names a 301, 400 or 403\n * `Unknown`.\n *\n * @throws {ProjectBucketTaken} on a 403 or 301\n */\nexport async function findProjectBucket(\n\ts3: S3Client,\n\tbucket: string,\n): Promise<'ours' | 'none'> {\n\tconst { HeadBucketCommand } = await import('@aws-sdk/client-s3');\n\ttry {\n\t\tawait s3.send(new HeadBucketCommand({ Bucket: bucket }));\n\t\treturn 'ours';\n\t} catch (error) {\n\t\tconst status = (error as S3Failure).$metadata?.httpStatusCode;\n\t\tif (status === 404) return 'none';\n\t\tif (status === 403 || status === 301) {\n\t\t\tthrow new ProjectBucketTaken(bucket, status);\n\t\t}\n\t\tthrow error;\n\t}\n}\n\nexport interface EnsureProjectBucketOptions {\n\tbucket: string;\n\tregion: string;\n\t/** The deploy identity the bucket is tagged with. */\n\tidentity: Pick<DeployIdentity, 'key'>;\n\t/** Per-prefix expiries beside the bucket's own rule. */\n\texpiries?: readonly PrefixExpiry[];\n}\n\n/**\n * The project bucket, created and configured if it does not exist yet.\n * Returns whether this call created it. One that exists is used as it is.\n *\n * @throws {ProjectBucketTaken} when another account holds the name\n * @throws {ProjectBucketNotCreatable} when the credentials may not create it\n */\nexport async function ensureProjectBucket(\n\ts3: S3Client,\n\toptions: EnsureProjectBucketOptions,\n): Promise<{ created: boolean }> {\n\tconst { bucket: Bucket, region } = options;\n\tif ((await findProjectBucket(s3, Bucket)) === 'ours') {\n\t\treturn { created: false };\n\t}\n\n\tconst S3 = await import('@aws-sdk/client-s3');\n\ttry {\n\t\tawait s3.send(\n\t\t\tnew S3.CreateBucketCommand({\n\t\t\t\tBucket,\n\t\t\t\tObjectOwnership: 'BucketOwnerEnforced',\n\t\t\t\t// us-east-1 is the one region named by leaving it out.\n\t\t\t\t...(region === 'us-east-1'\n\t\t\t\t\t? {}\n\t\t\t\t\t: {\n\t\t\t\t\t\t\tCreateBucketConfiguration: {\n\t\t\t\t\t\t\t\tLocationConstraint: region as BucketLocationConstraint,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}),\n\t\t\t}),\n\t\t);\n\t} catch (error) {\n\t\tconst failure = error as S3Failure;\n\t\tconst code = failure.Code ?? failure.name;\n\t\t// Another run created it a moment ago: configure it all the same.\n\t\tif (code !== 'BucketAlreadyOwnedByYou') {\n\t\t\tif (code === 'BucketAlreadyExists') {\n\t\t\t\tthrow new ProjectBucketTaken(Bucket, 409);\n\t\t\t}\n\t\t\tif (\n\t\t\t\tcode === 'AccessDenied' ||\n\t\t\t\tfailure.$metadata?.httpStatusCode === 403\n\t\t\t) {\n\t\t\t\tthrow new ProjectBucketNotCreatable(\n\t\t\t\t\tBucket,\n\t\t\t\t\tfailure.message ?? 'AccessDenied',\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tawait s3.send(\n\t\tnew S3.PutPublicAccessBlockCommand({\n\t\t\tBucket,\n\t\t\tPublicAccessBlockConfiguration: {\n\t\t\t\tBlockPublicAcls: true,\n\t\t\t\tIgnorePublicAcls: true,\n\t\t\t\tBlockPublicPolicy: true,\n\t\t\t\tRestrictPublicBuckets: true,\n\t\t\t},\n\t\t}),\n\t);\n\tawait s3.send(\n\t\tnew S3.PutBucketOwnershipControlsCommand({\n\t\t\tBucket,\n\t\t\tOwnershipControls: {\n\t\t\t\tRules: [{ ObjectOwnership: 'BucketOwnerEnforced' }],\n\t\t\t},\n\t\t}),\n\t);\n\tawait s3.send(\n\t\tnew S3.PutBucketEncryptionCommand({\n\t\t\tBucket,\n\t\t\tServerSideEncryptionConfiguration: {\n\t\t\t\tRules: [\n\t\t\t\t\t{ ApplyServerSideEncryptionByDefault: { SSEAlgorithm: 'AES256' } },\n\t\t\t\t],\n\t\t\t},\n\t\t}),\n\t);\n\tawait s3.send(\n\t\tnew S3.PutBucketVersioningCommand({\n\t\t\tBucket,\n\t\t\tVersioningConfiguration: { Status: 'Enabled' },\n\t\t}),\n\t);\n\tawait s3.send(\n\t\tnew S3.PutBucketLifecycleConfigurationCommand({\n\t\t\tBucket,\n\t\t\tLifecycleConfiguration: {\n\t\t\t\tRules: projectBucketLifecycle(options.expiries),\n\t\t\t},\n\t\t}),\n\t);\n\tawait s3.send(\n\t\tnew S3.PutBucketTaggingCommand({\n\t\t\tBucket,\n\t\t\tTagging: {\n\t\t\t\tTagSet: [{ Key: PROJECT_BUCKET_TAG, Value: options.identity.key }],\n\t\t\t},\n\t\t}),\n\t);\n\treturn { created: true };\n}\n\n/**\n * The IAM actions creating and configuring the project bucket takes — what a\n * role that runs the first deploy needs on it, beside object access.\n */\nexport const PROJECT_BUCKET_CREATE_ACTIONS = [\n\t's3:CreateBucket',\n\t's3:PutBucketVersioning',\n\t's3:PutEncryptionConfiguration',\n\t's3:PutBucketPublicAccessBlock',\n\t's3:PutBucketOwnershipControls',\n\t's3:PutLifecycleConfiguration',\n\t's3:PutBucketTagging',\n] as const;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA+BA,MAAM,YAAY;;AAGlB,MAAa,qBAAqB;;AAMlC,IAAa,qBAAb,cAAwC,SAAS;CAEtC;CAEA;CAHV,YACC,QAEA,QACC;EAKD,MACC,uBAAuB,OAAO,IAJ9B,WAAW,MACR,iDACA,2EAEmC,SAAS,OAAO,8LAIvD;EAbS,KAAA,SAAA;EAEA,KAAA,SAAA;EAYT,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,4BAAb,cAA+C,SAAS;CAE7C;CACA;CAFV,YACC,QACA,QACC;EACD,MACC,uBAAuB,OAAO,gEACI,OAAO,qOAI1C;EATS,KAAA,SAAA;EACA,KAAA,SAAA;EAST,KAAK,OAAO;CACb;AACD;;AAgBA,SAAgB,kBAAkB,OAAe,WAA2B;CAC3E,IAAI,OAAO,MACT,YAAY,CAAC,CACb,QAAQ,eAAe,GAAG,CAAC,CAC3B,QAAQ,YAAY,EAAE;CACxB,IAAI,KAAK,SAAS,WAAW;EAC5B,MAAM,OAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC;EACxE,OAAO,GAAG,KAAK,MAAM,GAAG,EAAa,CAAC,CAAC,QAAQ,OAAO,EAAE,EAAE,GAAG;CAC9D;CACA,OAAO,OAAO,KAAK,GAAG;AACvB;;AAGA,eAAsB,gBAAgB,KAAiC;CACtE,MAAM,EAAE,6BAA6B,MAAM,OAAO;CAClD,MAAM,EAAE,YAAY,MAAM,IAAI,KAAK,IAAI,yBAAyB,CAAC,CAAC,CAAC;CACnE,OAAO,WAAW;AACnB;;;;;;AAiBA,SAAgB,uBACf,WAAoC,CAAC,GACnB;CAClB,OAAO,CACN;EACC,IAAI;EACJ,QAAQ;EACR,QAAQ,EAAE,QAAQ,GAAG;EACrB,6BAA6B,EAAE,gBAAA,GAAwC;EAEvE,YAAY,EAAE,2BAA2B,KAAK;CAC/C,GACA,GAAG,SAAS,KACV,YAA2B;EAC3B,IAAI,OAAO;EACX,QAAQ;EACR,QAAQ,EAAE,QAAQ,OAAO,OAAO;EAChC,YAAY,EAAE,MAAM,OAAO,KAAK;CACjC,EACD,CACD;AACD;;;;;;;;AAgBA,eAAsB,kBACrB,IACA,QAC2B;CAC3B,MAAM,EAAE,sBAAsB,MAAM,OAAO;CAC3C,IAAI;EACH,MAAM,GAAG,KAAK,IAAI,kBAAkB,EAAE,QAAQ,OAAO,CAAC,CAAC;EACvD,OAAO;CACR,SAAS,OAAO;EACf,MAAM,SAAU,MAAoB,WAAW;EAC/C,IAAI,WAAW,KAAK,OAAO;EAC3B,IAAI,WAAW,OAAO,WAAW,KAChC,MAAM,IAAI,mBAAmB,QAAQ,MAAM;EAE5C,MAAM;CACP;AACD;;;;;;;;AAkBA,eAAsB,oBACrB,IACA,SACgC;CAChC,MAAM,EAAE,QAAQ,QAAQ,WAAW;CACnC,IAAK,MAAM,kBAAkB,IAAI,MAAM,MAAO,QAC7C,OAAO,EAAE,SAAS,MAAM;CAGzB,MAAM,KAAK,MAAM,OAAO;CACxB,IAAI;EACH,MAAM,GAAG,KACR,IAAI,GAAG,oBAAoB;GAC1B;GACA,iBAAiB;GAEjB,GAAI,WAAW,cACZ,CAAC,IACD,EACA,2BAA2B,EAC1B,oBAAoB,OACrB,EACD;EACH,CAAC,CACF;CACD,SAAS,OAAO;EACf,MAAM,UAAU;EAChB,MAAM,OAAO,QAAQ,QAAQ,QAAQ;EAErC,IAAI,SAAS,2BAA2B;GACvC,IAAI,SAAS,uBACZ,MAAM,IAAI,mBAAmB,QAAQ,GAAG;GAEzC,IACC,SAAS,kBACT,QAAQ,WAAW,mBAAmB,KAEtC,MAAM,IAAI,0BACT,QACA,QAAQ,WAAW,cACpB;GAED,MAAM;EACP;CACD;CAEA,MAAM,GAAG,KACR,IAAI,GAAG,4BAA4B;EAClC;EACA,gCAAgC;GAC/B,iBAAiB;GACjB,kBAAkB;GAClB,mBAAmB;GACnB,uBAAuB;EACxB;CACD,CAAC,CACF;CACA,MAAM,GAAG,KACR,IAAI,GAAG,kCAAkC;EACxC;EACA,mBAAmB,EAClB,OAAO,CAAC,EAAE,iBAAiB,sBAAsB,CAAC,EACnD;CACD,CAAC,CACF;CACA,MAAM,GAAG,KACR,IAAI,GAAG,2BAA2B;EACjC;EACA,mCAAmC,EAClC,OAAO,CACN,EAAE,oCAAoC,EAAE,cAAc,SAAS,EAAE,CAClE,EACD;CACD,CAAC,CACF;CACA,MAAM,GAAG,KACR,IAAI,GAAG,2BAA2B;EACjC;EACA,yBAAyB,EAAE,QAAQ,UAAU;CAC9C,CAAC,CACF;CACA,MAAM,GAAG,KACR,IAAI,GAAG,uCAAuC;EAC7C;EACA,wBAAwB,EACvB,OAAO,uBAAuB,QAAQ,QAAQ,EAC/C;CACD,CAAC,CACF;CACA,MAAM,GAAG,KACR,IAAI,GAAG,wBAAwB;EAC9B;EACA,SAAS,EACR,QAAQ,CAAC;GAAE,KAAK;GAAoB,OAAO,QAAQ,SAAS;EAAI,CAAC,EAClE;CACD,CAAC,CACF;CACA,OAAO,EAAE,SAAS,KAAK;AACxB;;;;;AAMA,MAAa,gCAAgC;CAC5C;CACA;CACA;CACA;CACA;CACA;CACA;AACD"}
@@ -1,5 +1,5 @@
1
- import { n as EmailBackend, t as CacheBackend } from "../types-dipJRmKV.cjs";
2
- import { a as isDeclarable, i as discover, r as DiscoverOptions } from "../discover-BtFofyQB.cjs";
1
+ import { n as EmailBackend, t as CacheBackend } from "../types-DipUP9zp.cjs";
2
+ import { a as isDeclarable, i as discover, r as DiscoverOptions } from "../discover-CU1LjCOf.cjs";
3
3
  import { ConstructManifest, RouteInfo } from "@geekmidas/manifest";
4
4
  //#region src/reconcile/emit.d.ts
5
5
  /**
@@ -1,5 +1,5 @@
1
- import { n as EmailBackend, t as CacheBackend } from "../types-bkqVscdN.mjs";
2
- import { a as isDeclarable, i as discover, r as DiscoverOptions } from "../discover-D-MdXZgm.mjs";
1
+ import { n as EmailBackend, t as CacheBackend } from "../types-Drpl9HX9.mjs";
2
+ import { a as isDeclarable, i as discover, r as DiscoverOptions } from "../discover-DJCCgimv.mjs";
3
3
  import { ConstructManifest, RouteInfo } from "@geekmidas/manifest";
4
4
  //#region src/reconcile/emit.d.ts
5
5
  /**
@@ -200,8 +200,8 @@ var InvalidTargetOptions = class extends require_errors.GkmError {
200
200
  };
201
201
  /** The built-ins `gkm deploy` deploys through, loaded only when used. */
202
202
  const BUILTIN_LOADERS = {
203
- dokploy: async () => (await Promise.resolve().then(() => require("./dokploy-CD_Bc4EV.cjs"))).dokployTarget,
204
- compose: async () => (await Promise.resolve().then(() => require("./compose-ETFKqkTu.cjs")).then((n) => n.compose_exports)).composeTarget(),
203
+ dokploy: async () => (await Promise.resolve().then(() => require("./dokploy-Dw4oCXqR.cjs"))).dokployTarget,
204
+ compose: async () => (await Promise.resolve().then(() => require("./compose-DMY4ouEk.cjs")).then((n) => n.compose_exports)).composeTarget(),
205
205
  sst: async () => (await Promise.resolve().then(() => require("./sst-DGuHDxVU.cjs")).then((n) => n.sst_exports)).sstTarget
206
206
  };
207
207
  /** The target `name` means, in this workspace, for this host. */
@@ -313,4 +313,4 @@ Object.defineProperty(exports, "runtimeOf", {
313
313
  }
314
314
  });
315
315
 
316
- //# sourceMappingURL=resolve-BDatNOoG.cjs.map
316
+ //# sourceMappingURL=resolve-CScrqgdC.cjs.map