@geekmidas/cli 10.0.0-alpha.126 → 10.0.0-alpha.127

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 (207) hide show
  1. package/dist/{EndpointGenerator-errMeldx.mjs → EndpointGenerator-B99OGzbO.mjs} +2 -2
  2. package/dist/{EndpointGenerator-errMeldx.mjs.map → EndpointGenerator-B99OGzbO.mjs.map} +1 -1
  3. package/dist/{EndpointGenerator-BGxf9jWE.cjs → EndpointGenerator-De0lknKD.cjs} +2 -2
  4. package/dist/{EndpointGenerator-BGxf9jWE.cjs.map → EndpointGenerator-De0lknKD.cjs.map} +1 -1
  5. package/dist/{GoDaddyProvider-CvRVmN4W.mjs → GoDaddyProvider-BG-UdElN.mjs} +2 -2
  6. package/dist/{GoDaddyProvider-CvRVmN4W.mjs.map → GoDaddyProvider-BG-UdElN.mjs.map} +1 -1
  7. package/dist/{GoDaddyProvider-DFfpzCOP.cjs → GoDaddyProvider-DnmLq7eo.cjs} +2 -2
  8. package/dist/{GoDaddyProvider-DFfpzCOP.cjs.map → GoDaddyProvider-DnmLq7eo.cjs.map} +1 -1
  9. package/dist/{LegacyStateStore-BFrCHrFQ.cjs → LegacyStateStore-CeBkK9K2.cjs} +3 -3
  10. package/dist/{LegacyStateStore-BFrCHrFQ.cjs.map → LegacyStateStore-CeBkK9K2.cjs.map} +1 -1
  11. package/dist/{LegacyStateStore-CgfENrXF.mjs → LegacyStateStore-DHG_4jcn.mjs} +3 -3
  12. package/dist/{LegacyStateStore-CgfENrXF.mjs.map → LegacyStateStore-DHG_4jcn.mjs.map} +1 -1
  13. package/dist/{LocalStateStore-DxoNpiBg.mjs → LocalStateStore-EenTHwr4.mjs} +2 -2
  14. package/dist/{LocalStateStore-DxoNpiBg.mjs.map → LocalStateStore-EenTHwr4.mjs.map} +1 -1
  15. package/dist/{LocalStateStore-BU5z_IOE.cjs → LocalStateStore-HRnBQFe3.cjs} +2 -2
  16. package/dist/{LocalStateStore-BU5z_IOE.cjs.map → LocalStateStore-HRnBQFe3.cjs.map} +1 -1
  17. package/dist/{OpenApiTsGenerator-cQgtxWQo.cjs → OpenApiTsGenerator-DMLzN-LU.cjs} +2 -2
  18. package/dist/{OpenApiTsGenerator-cQgtxWQo.cjs.map → OpenApiTsGenerator-DMLzN-LU.cjs.map} +1 -1
  19. package/dist/{OpenApiTsGenerator-W_HRXH1X.mjs → OpenApiTsGenerator-DNUCgkkx.mjs} +2 -2
  20. package/dist/{OpenApiTsGenerator-W_HRXH1X.mjs.map → OpenApiTsGenerator-DNUCgkkx.mjs.map} +1 -1
  21. package/dist/{S3StateStore-DiekMLpn.cjs → S3StateStore-DyLljVN1.cjs} +2 -2
  22. package/dist/{S3StateStore-DiekMLpn.cjs.map → S3StateStore-DyLljVN1.cjs.map} +1 -1
  23. package/dist/{S3StateStore-CGMSDilh.mjs → S3StateStore-vbkyBnRB.mjs} +2 -2
  24. package/dist/{S3StateStore-CGMSDilh.mjs.map → S3StateStore-vbkyBnRB.mjs.map} +1 -1
  25. package/dist/{SSMStateStore-BDCOFdKq.cjs → SSMStateStore-BBKlmfaU.cjs} +2 -2
  26. package/dist/{SSMStateStore-BDCOFdKq.cjs.map → SSMStateStore-BBKlmfaU.cjs.map} +1 -1
  27. package/dist/{SSMStateStore-Cq2H9MlF.mjs → SSMStateStore-uk2QLUgz.mjs} +2 -2
  28. package/dist/{SSMStateStore-Cq2H9MlF.mjs.map → SSMStateStore-uk2QLUgz.mjs.map} +1 -1
  29. package/dist/{SubscriberGenerator-BALddfuW.mjs → SubscriberGenerator-0ruEmYXj.mjs} +3 -3
  30. package/dist/{SubscriberGenerator-BALddfuW.mjs.map → SubscriberGenerator-0ruEmYXj.mjs.map} +1 -1
  31. package/dist/{SubscriberGenerator-LgyTxZ8r.cjs → SubscriberGenerator-CPjbWEOI.cjs} +3 -3
  32. package/dist/{SubscriberGenerator-LgyTxZ8r.cjs.map → SubscriberGenerator-CPjbWEOI.cjs.map} +1 -1
  33. package/dist/{add-BsiBYkAW.cjs → add-CH7-9AHI.cjs} +7 -7
  34. package/dist/add-CH7-9AHI.cjs.map +1 -0
  35. package/dist/{add-CamA9Uqv.mjs → add-CgyyN6aF.mjs} +7 -7
  36. package/dist/add-CgyyN6aF.mjs.map +1 -0
  37. package/dist/{bundler-CijtdIZ2.cjs → bundler-BtAkF9wb.cjs} +5 -5
  38. package/dist/{bundler-CijtdIZ2.cjs.map → bundler-BtAkF9wb.cjs.map} +1 -1
  39. package/dist/{bundler-2V2PR_E-.mjs → bundler-DqHozK8W.mjs} +5 -5
  40. package/dist/{bundler-2V2PR_E-.mjs.map → bundler-DqHozK8W.mjs.map} +1 -1
  41. package/dist/{cli-7gc1QG8X.cjs → cli-D08bMg7m.cjs} +3 -3
  42. package/dist/{cli-7gc1QG8X.cjs.map → cli-D08bMg7m.cjs.map} +1 -1
  43. package/dist/{cli-PiyYqHGx.mjs → cli-D_BQkb4B.mjs} +3 -3
  44. package/dist/{cli-PiyYqHGx.mjs.map → cli-D_BQkb4B.mjs.map} +1 -1
  45. package/dist/{compose-8e2xJ61E.cjs → compose-BK9s_2Qe.cjs} +68 -40
  46. package/dist/compose-BK9s_2Qe.cjs.map +1 -0
  47. package/dist/{compose-CvHIjMDd.mjs → compose-CIzCcsoz.mjs} +68 -40
  48. package/dist/compose-CIzCcsoz.mjs.map +1 -0
  49. package/dist/{config-_y1FGBWR.d.mts → config-B8fXmuK0.d.cts} +3 -3
  50. package/dist/{config-CdytFgSR.d.cts.map → config-B8fXmuK0.d.cts.map} +1 -1
  51. package/dist/{config-CdytFgSR.d.cts → config-CRRxZMk7.d.mts} +3 -3
  52. package/dist/{config-_y1FGBWR.d.mts.map → config-CRRxZMk7.d.mts.map} +1 -1
  53. package/dist/config.cjs +2 -2
  54. package/dist/config.d.cts +3 -3
  55. package/dist/config.d.mts +3 -3
  56. package/dist/config.mjs +2 -2
  57. package/dist/{credentialSchemas-DFxvGEwa.cjs → credentialSchemas-CM0rxWmS.cjs} +2 -2
  58. package/dist/{credentialSchemas-DFxvGEwa.cjs.map → credentialSchemas-CM0rxWmS.cjs.map} +1 -1
  59. package/dist/{credentialSchemas-DaMpt-Dx.mjs → credentialSchemas-DPOGqOrU.mjs} +2 -2
  60. package/dist/{credentialSchemas-DaMpt-Dx.mjs.map → credentialSchemas-DPOGqOrU.mjs.map} +1 -1
  61. package/dist/deploy/public.cjs +8 -8
  62. package/dist/deploy/public.d.cts +3 -3
  63. package/dist/deploy/public.d.mts +3 -3
  64. package/dist/deploy/public.mjs +8 -8
  65. package/dist/{deploy-wSlIRPn5.cjs → deploy-A_fzSa59.cjs} +10 -10
  66. package/dist/deploy-A_fzSa59.cjs.map +1 -0
  67. package/dist/{deploy-CIPwTJ47.mjs → deploy-BVf6c_h8.mjs} +10 -10
  68. package/dist/deploy-BVf6c_h8.mjs.map +1 -0
  69. package/dist/{derive-DAbo49sm.cjs → derive-BtGidITm.cjs} +7 -7
  70. package/dist/derive-BtGidITm.cjs.map +1 -0
  71. package/dist/{derive-B4vDpzgP.mjs → derive-Dew0QoGe.mjs} +7 -7
  72. package/dist/derive-Dew0QoGe.mjs.map +1 -0
  73. package/dist/{discover-DL6h-J97.cjs → discover-DEOspD9M.cjs} +12 -1
  74. package/dist/discover-DEOspD9M.cjs.map +1 -0
  75. package/dist/{discover-CzGM5P3p.mjs → discover-DOhYSKY_.mjs} +12 -1
  76. package/dist/discover-DOhYSKY_.mjs.map +1 -0
  77. package/dist/{dokploy-BKbUrz05.cjs → dokploy-CshlFRwr.cjs} +2 -2
  78. package/dist/{dokploy-BKbUrz05.cjs.map → dokploy-CshlFRwr.cjs.map} +1 -1
  79. package/dist/{dokploy-BZf1AHd0.mjs → dokploy-DOndleTd.mjs} +2 -2
  80. package/dist/{dokploy-BZf1AHd0.mjs.map → dokploy-DOndleTd.mjs.map} +1 -1
  81. package/dist/{eas-DzDMGzfc.cjs → eas-ApfPYR4-.cjs} +3 -3
  82. package/dist/{eas-DzDMGzfc.cjs.map → eas-ApfPYR4-.cjs.map} +1 -1
  83. package/dist/{eas-CxX2RCE_.mjs → eas-Dlr1Lw56.mjs} +3 -3
  84. package/dist/{eas-CxX2RCE_.mjs.map → eas-Dlr1Lw56.mjs.map} +1 -1
  85. package/dist/{encryption-BFSrt89F.mjs → encryption-D0tWRvy_.mjs} +2 -2
  86. package/dist/{encryption-BFSrt89F.mjs.map → encryption-D0tWRvy_.mjs.map} +1 -1
  87. package/dist/{encryption-RNcBnG4Z.cjs → encryption-SGWvnBxV.cjs} +2 -2
  88. package/dist/{encryption-RNcBnG4Z.cjs.map → encryption-SGWvnBxV.cjs.map} +1 -1
  89. package/dist/{engine-Dqe9KQCm.cjs → engine-CYu4wavP.cjs} +5 -5
  90. package/dist/{engine-Dqe9KQCm.cjs.map → engine-CYu4wavP.cjs.map} +1 -1
  91. package/dist/{engine-_0pjyCd_.mjs → engine-DKz1NYDX.mjs} +5 -5
  92. package/dist/{engine-_0pjyCd_.mjs.map → engine-DKz1NYDX.mjs.map} +1 -1
  93. package/dist/{github-Csev5EFf.cjs → github-CxWaWQtW.cjs} +5 -5
  94. package/dist/{github-Csev5EFf.cjs.map → github-CxWaWQtW.cjs.map} +1 -1
  95. package/dist/{github-Dck0nLW9.mjs → github-VQaj8gLE.mjs} +5 -5
  96. package/dist/{github-Dck0nLW9.mjs.map → github-VQaj8gLE.mjs.map} +1 -1
  97. package/dist/{googlePlay-DBtDAGBk.cjs → googlePlay-4KVtZ89D.cjs} +3 -3
  98. package/dist/{googlePlay-DBtDAGBk.cjs.map → googlePlay-4KVtZ89D.cjs.map} +1 -1
  99. package/dist/{googlePlay-DTuvDHi5.mjs → googlePlay-Nqc4EAiC.mjs} +3 -3
  100. package/dist/{googlePlay-DTuvDHi5.mjs.map → googlePlay-Nqc4EAiC.mjs.map} +1 -1
  101. package/dist/{index-DrkV7N5w.d.mts → index-Cu8eIwUW.d.mts} +2 -2
  102. package/dist/{index-DrkV7N5w.d.mts.map → index-Cu8eIwUW.d.mts.map} +1 -1
  103. package/dist/{index-yaXZ9m-J.d.cts → index-Dx93n3Ur.d.cts} +2 -2
  104. package/dist/{index-yaXZ9m-J.d.cts.map → index-Dx93n3Ur.d.cts.map} +1 -1
  105. package/dist/index.cjs +101 -73
  106. package/dist/index.cjs.map +1 -1
  107. package/dist/index.mjs +101 -73
  108. package/dist/index.mjs.map +1 -1
  109. package/dist/{migrate-3ZEoqx5q.cjs → migrate-BQPRclv2.cjs} +2 -2
  110. package/dist/{migrate-3ZEoqx5q.cjs.map → migrate-BQPRclv2.cjs.map} +1 -1
  111. package/dist/{migrate-BABIuzQj.mjs → migrate-C7exqXTD.mjs} +2 -2
  112. package/dist/{migrate-BABIuzQj.mjs.map → migrate-C7exqXTD.mjs.map} +1 -1
  113. package/dist/{migrate-DZeNpuX2.mjs → migrate-DQPEUiFC.mjs} +3 -3
  114. package/dist/{migrate-DZeNpuX2.mjs.map → migrate-DQPEUiFC.mjs.map} +1 -1
  115. package/dist/{migrate-pGiUqvBi.cjs → migrate-DTv6LHux.cjs} +3 -3
  116. package/dist/{migrate-pGiUqvBi.cjs.map → migrate-DTv6LHux.cjs.map} +1 -1
  117. package/dist/openapi.cjs +3 -3
  118. package/dist/openapi.d.cts +1 -1
  119. package/dist/openapi.d.mts +1 -1
  120. package/dist/openapi.mjs +3 -3
  121. package/dist/{publish-CS6xDvGT.cjs → publish-CgwqwKDs.cjs} +2 -2
  122. package/dist/{publish-CS6xDvGT.cjs.map → publish-CgwqwKDs.cjs.map} +1 -1
  123. package/dist/{publish-BsnDeOVL.mjs → publish-VYt4xOOc.mjs} +2 -2
  124. package/dist/{publish-BsnDeOVL.mjs.map → publish-VYt4xOOc.mjs.map} +1 -1
  125. package/dist/{ready-DrvaAwKv.mjs → ready-BwtLriAF.mjs} +5 -5
  126. package/dist/{ready-DrvaAwKv.mjs.map → ready-BwtLriAF.mjs.map} +1 -1
  127. package/dist/{ready-CxPJm0Hc.cjs → ready-Cj4pI0dw.cjs} +5 -5
  128. package/dist/{ready-CxPJm0Hc.cjs.map → ready-Cj4pI0dw.cjs.map} +1 -1
  129. package/dist/reconcile/public.cjs +1 -1
  130. package/dist/reconcile/public.d.cts +1 -1
  131. package/dist/reconcile/public.d.mts +1 -1
  132. package/dist/reconcile/public.mjs +1 -1
  133. package/dist/{resolve-zqCMFio0.cjs → resolve-C-W3kZM7.cjs} +4 -4
  134. package/dist/{resolve-zqCMFio0.cjs.map → resolve-C-W3kZM7.cjs.map} +1 -1
  135. package/dist/{resolve-fCGJmM-R.mjs → resolve-CCVCc43b.mjs} +4 -4
  136. package/dist/{resolve-fCGJmM-R.mjs.map → resolve-CCVCc43b.mjs.map} +1 -1
  137. package/dist/{rollback-qc79mj6L.cjs → rollback-CdBj1Boy.cjs} +4 -4
  138. package/dist/{rollback-qc79mj6L.cjs.map → rollback-CdBj1Boy.cjs.map} +1 -1
  139. package/dist/{rollback-BkhVrtAf.mjs → rollback-dUShCpZu.mjs} +4 -4
  140. package/dist/{rollback-BkhVrtAf.mjs.map → rollback-dUShCpZu.mjs.map} +1 -1
  141. package/dist/{runtime-DCkfI2E7.d.mts → runtime-C_1PteGG.d.cts} +2 -2
  142. package/dist/{runtime-DCkfI2E7.d.mts.map → runtime-C_1PteGG.d.cts.map} +1 -1
  143. package/dist/{runtime-Ie4znWIB.d.cts → runtime-jjB2e3-M.d.mts} +2 -2
  144. package/dist/{runtime-Ie4znWIB.d.cts.map → runtime-jjB2e3-M.d.mts.map} +1 -1
  145. package/dist/sandbox/credentials-worker.cjs +2 -2
  146. package/dist/sandbox/credentials-worker.mjs +2 -2
  147. package/dist/sandbox/discover-worker.cjs +1 -1
  148. package/dist/sandbox/discover-worker.mjs +1 -1
  149. package/dist/sandbox/migrate-worker.cjs +1 -1
  150. package/dist/sandbox/migrate-worker.mjs +1 -1
  151. package/dist/{seeds-Cfejy8wj.mjs → seeds-BX9pskrU.mjs} +66 -27
  152. package/dist/seeds-BX9pskrU.mjs.map +1 -0
  153. package/dist/{seeds-3awh33Wz.cjs → seeds-sMVADbkg.cjs} +66 -27
  154. package/dist/seeds-sMVADbkg.cjs.map +1 -0
  155. package/dist/{serverSetup-BFQuwUeL.cjs → serverSetup-4avavig6.cjs} +25 -5
  156. package/dist/serverSetup-4avavig6.cjs.map +1 -0
  157. package/dist/{serverSetup-C8ZbHTm_.mjs → serverSetup-BvHpm7QD.mjs} +25 -5
  158. package/dist/serverSetup-BvHpm7QD.mjs.map +1 -0
  159. package/dist/{sniffer-Cq1nhxIB.cjs → sniffer-DWLala8l.cjs} +7 -7
  160. package/dist/{sniffer-Cq1nhxIB.cjs.map → sniffer-DWLala8l.cjs.map} +1 -1
  161. package/dist/{sniffer-CCkdzjiU.mjs → sniffer-XnAnEbuI.mjs} +7 -7
  162. package/dist/{sniffer-CCkdzjiU.mjs.map → sniffer-XnAnEbuI.mjs.map} +1 -1
  163. package/dist/{snsSubscriptions-R_AKBZYK.mjs → snsSubscriptions-BxLJQH4u.mjs} +3 -3
  164. package/dist/{snsSubscriptions-R_AKBZYK.mjs.map → snsSubscriptions-BxLJQH4u.mjs.map} +1 -1
  165. package/dist/{snsSubscriptions-DYyYP0Xf.cjs → snsSubscriptions-f_z4PSFb.cjs} +3 -3
  166. package/dist/{snsSubscriptions-DYyYP0Xf.cjs.map → snsSubscriptions-f_z4PSFb.cjs.map} +1 -1
  167. package/dist/{sst-lEkBwzGc.cjs → sst-DmVJRu5l.cjs} +2 -2
  168. package/dist/{sst-lEkBwzGc.cjs.map → sst-DmVJRu5l.cjs.map} +1 -1
  169. package/dist/{sst-CTqZZl1_.mjs → sst-fop6uZ25.mjs} +2 -2
  170. package/dist/{sst-CTqZZl1_.mjs.map → sst-fop6uZ25.mjs.map} +1 -1
  171. package/dist/{stage-BkgbVJ9Z.cjs → stage-ZWOE0oLB.cjs} +5 -5
  172. package/dist/{stage-BkgbVJ9Z.cjs.map → stage-ZWOE0oLB.cjs.map} +1 -1
  173. package/dist/{stage-DCaxqm-G.mjs → stage-mta618Oy.mjs} +5 -5
  174. package/dist/{stage-DCaxqm-G.mjs.map → stage-mta618Oy.mjs.map} +1 -1
  175. package/dist/target/public.cjs +1 -1
  176. package/dist/target/public.d.cts +2 -2
  177. package/dist/target/public.d.mts +2 -2
  178. package/dist/target/public.mjs +1 -1
  179. package/dist/{terminal-bqY33lpN.mjs → terminal-CebFDhWQ.mjs} +2 -2
  180. package/dist/{terminal-bqY33lpN.mjs.map → terminal-CebFDhWQ.mjs.map} +1 -1
  181. package/dist/{terminal-PKhPAajN.cjs → terminal-DBwedwSS.cjs} +2 -2
  182. package/dist/{terminal-PKhPAajN.cjs.map → terminal-DBwedwSS.cjs.map} +1 -1
  183. package/dist/{types-D0IsQxES.d.cts → types-DLANxOpy.d.mts} +7 -2
  184. package/dist/types-DLANxOpy.d.mts.map +1 -0
  185. package/dist/{types-Bmjs4jZV.d.mts → types-SEHMr6Aw.d.cts} +8 -1
  186. package/dist/types-SEHMr6Aw.d.cts.map +1 -0
  187. package/dist/vitest.cjs +1 -1
  188. package/dist/vitest.mjs +1 -1
  189. package/dist/workspace/index.d.cts +2 -2
  190. package/dist/workspace/index.d.mts +2 -2
  191. package/package.json +15 -15
  192. package/dist/add-BsiBYkAW.cjs.map +0 -1
  193. package/dist/add-CamA9Uqv.mjs.map +0 -1
  194. package/dist/compose-8e2xJ61E.cjs.map +0 -1
  195. package/dist/compose-CvHIjMDd.mjs.map +0 -1
  196. package/dist/deploy-CIPwTJ47.mjs.map +0 -1
  197. package/dist/deploy-wSlIRPn5.cjs.map +0 -1
  198. package/dist/derive-B4vDpzgP.mjs.map +0 -1
  199. package/dist/derive-DAbo49sm.cjs.map +0 -1
  200. package/dist/discover-CzGM5P3p.mjs.map +0 -1
  201. package/dist/discover-DL6h-J97.cjs.map +0 -1
  202. package/dist/seeds-3awh33Wz.cjs.map +0 -1
  203. package/dist/seeds-Cfejy8wj.mjs.map +0 -1
  204. package/dist/serverSetup-BFQuwUeL.cjs.map +0 -1
  205. package/dist/serverSetup-C8ZbHTm_.mjs.map +0 -1
  206. package/dist/types-Bmjs4jZV.d.mts.map +0 -1
  207. package/dist/types-D0IsQxES.d.cts.map +0 -1
@@ -2,7 +2,7 @@ const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
2
2
  const require_errors = require("./errors-DMC4Umsb.cjs");
3
3
  const require_credentials = require("./credentials-D7XJypNd.cjs");
4
4
  const require_define = require("./define-crmMcXFS.cjs");
5
- const require_derive = require("./derive-DAbo49sm.cjs");
5
+ const require_derive = require("./derive-BtGidITm.cjs");
6
6
  const require_sandbox = require("./sandbox-CHv3OYXg.cjs");
7
7
  const require_local = require("./local-OPC9BCZt.cjs");
8
8
  const require_utils = require("./utils-D2Nk1Nv2.cjs");
@@ -460,4 +460,4 @@ Object.defineProperty(exports, "sst_exports", {
460
460
  }
461
461
  });
462
462
 
463
- //# sourceMappingURL=sst-lEkBwzGc.cjs.map
463
+ //# sourceMappingURL=sst-DmVJRu5l.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"sst-lEkBwzGc.cjs","names":["GkmError","join","GkmError","rm","existsSync","readFile","kebabCase","GkmError","appKey","CommandFailed","defineTarget","existsSync","join","MissingCredential","activeSandbox","LocalSandbox","detectPackageManager"],"sources":["../src/target/sst/health.ts","../src/target/sst/outputs.ts","../src/target/sst/index.ts"],"sourcesContent":["/**\n * Whether a released surface answers.\n *\n * The paths are the ones the local stack's healthchecks use\n * (`reconcile/apps.ts`): an API's `/health`, a site's `/`. A surface is\n * asked a few times, because a fresh deploy can take a moment to answer — a\n * Lambda's first start, a distribution still settling — and healthy means a\n * 2xx, after redirects.\n */\n\nimport { GkmError } from '../../errors';\nimport type { TargetEvent } from '../types';\n\nexport interface HealthCheckOptions {\n\t/** How many times a surface is asked before it counts as down. */\n\tattempts: number;\n\t/** The wait between two attempts. */\n\tdelayMs: number;\n\t/** How long one request may take. */\n\ttimeoutMs: number;\n\tsignal: AbortSignal;\n\temit: (event: TargetEvent) => void;\n}\n\n/** Surfaces that never answered healthy. */\nexport class SurfacesUnhealthy extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\t/** Each surface that failed, with the last thing it answered. */\n\t\treadonly surfaces: readonly {\n\t\t\tapp: string;\n\t\t\turl: string;\n\t\t\tstatus?: number;\n\t\t\terror?: string;\n\t\t}[],\n\t) {\n\t\tconst lines = surfaces.map(\n\t\t\t({ app, url, status, error }) =>\n\t\t\t\t` - ${app}: ${url} → ${status ?? error ?? 'no answer'}`,\n\t\t);\n\t\tsuper(\n\t\t\t`The \"${stage}\" stage was deployed, but ${surfaces.length === 1 ? 'a surface does' : 'surfaces do'} not answer healthy:\\n${lines.join('\\n')}\\nCheck its logs in CloudWatch, fix it, and deploy again.`,\n\t\t);\n\t\tthis.name = 'SurfacesUnhealthy';\n\t}\n}\n\n/** `path` under `base`, keeping any path `base` already has. */\nexport function healthUrl(base: string, path: string): string {\n\tconst root = base.endsWith('/') ? base : `${base}/`;\n\treturn new URL(path.replace(/^\\//, ''), root).toString();\n}\n\n/**\n * Ask `url` until it answers 2xx or the attempts run out, emitting\n * `health.checked` for each. Resolves with the last answer.\n */\nexport async function checkHealth(\n\tapp: string,\n\turl: string,\n\toptions: HealthCheckOptions,\n): Promise<{ healthy: boolean; status?: number; error?: string }> {\n\tlet last: { healthy: boolean; status?: number; error?: string } = {\n\t\thealthy: false,\n\t};\n\tfor (let attempt = 1; attempt <= options.attempts; attempt++) {\n\t\toptions.signal.throwIfAborted();\n\t\ttry {\n\t\t\tconst response = await fetch(url, {\n\t\t\t\tredirect: 'follow',\n\t\t\t\tsignal: AbortSignal.any([\n\t\t\t\t\toptions.signal,\n\t\t\t\t\tAbortSignal.timeout(options.timeoutMs),\n\t\t\t\t]),\n\t\t\t});\n\t\t\t// Drained, so the connection is not held open by an unread body.\n\t\t\tawait response.arrayBuffer().catch(() => undefined);\n\t\t\tlast = { healthy: response.ok, status: response.status };\n\t\t} catch (error) {\n\t\t\t// The run being stopped is not the surface being down.\n\t\t\toptions.signal.throwIfAborted();\n\t\t\tlast = {\n\t\t\t\thealthy: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t};\n\t\t}\n\t\toptions.emit({\n\t\t\ttype: 'health.checked',\n\t\t\tapp,\n\t\t\turl,\n\t\t\thealthy: last.healthy,\n\t\t\t...(last.status !== undefined ? { status: last.status } : {}),\n\t\t\tattempt,\n\t\t});\n\t\tif (last.healthy) return last;\n\t\tif (attempt < options.attempts) await wait(options.delayMs, options.signal);\n\t}\n\treturn last;\n}\n\nfunction wait(ms: number, signal: AbortSignal): Promise<void> {\n\tif (ms <= 0) return Promise.resolve();\n\treturn new Promise((resolve, reject) => {\n\t\tconst timer = setTimeout(() => {\n\t\t\tsignal.removeEventListener('abort', stop);\n\t\t\tresolve();\n\t\t}, ms);\n\t\tconst stop = () => {\n\t\t\tclearTimeout(timer);\n\t\t\treject(signal.reason);\n\t\t};\n\t\tsignal.addEventListener('abort', stop, { once: true });\n\t});\n}\n","/**\n * The surface URLs an `sst deploy` reports.\n *\n * SST writes whatever `run()` in `sst.config.ts` returns to\n * `.sst/outputs.json` after every successful deploy — `{\"Api\": \"https://…\"}`.\n * What `run()` returns is the project's to write, so this reads it leniently:\n * nested objects are walked, anything that is not an http(s) URL is ignored,\n * and an output names a surface by its id in any casing, with or without a\n * `Url` suffix (`Api`, `api`, `ApiUrl`, `API_URL`, `Api.url`).\n */\n\nimport { existsSync } from 'node:fs';\nimport { readFile, rm } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { kebabCase } from '@geekmidas/manifest';\nimport { GkmError } from '../../errors';\n\n/** Where SST leaves the last successful deploy's outputs, under the project. */\nexport const SST_OUTPUTS_FILE = join('.sst', 'outputs.json');\n\n/** `.sst/outputs.json` is there, and is not JSON. */\nexport class SstOutputsUnreadable extends GkmError {\n\tconstructor(\n\t\treadonly path: string,\n\t\treadonly reason: string,\n\t) {\n\t\tsuper(\n\t\t\t`Could not read SST's outputs at ${path}: ${reason}. It is rewritten by every successful \\`sst deploy\\`; delete it and deploy again.`,\n\t\t);\n\t\tthis.name = 'SstOutputsUnreadable';\n\t}\n}\n\n/**\n * Remove what an earlier deploy wrote, so the outputs read after this one\n * are this one's — not another stage's, deployed from the same checkout.\n */\nexport async function clearOutputs(cwd: string): Promise<void> {\n\tawait rm(join(cwd, SST_OUTPUTS_FILE), { force: true });\n}\n\n/** The deploy's outputs, or `{}` when it wrote none. */\nexport async function readOutputs(cwd: string): Promise<unknown> {\n\tconst path = join(cwd, SST_OUTPUTS_FILE);\n\tif (!existsSync(path)) return {};\n\ttry {\n\t\treturn JSON.parse(await readFile(path, 'utf8'));\n\t} catch (error) {\n\t\tthrow new SstOutputsUnreadable(\n\t\t\tpath,\n\t\t\terror instanceof Error ? error.message : String(error),\n\t\t);\n\t}\n}\n\n/** Every http(s) URL in `outputs`, by its key path: `['Api', 'url']`. */\nfunction urlsIn(\n\tvalue: unknown,\n\tpath: string[] = [],\n\tfound: { path: string[]; url: string }[] = [],\n): { path: string[]; url: string }[] {\n\tif (typeof value === 'string') {\n\t\tif (isHttpUrl(value)) found.push({ path, url: value });\n\t} else if (value && typeof value === 'object' && !Array.isArray(value)) {\n\t\tfor (const [key, child] of Object.entries(value)) {\n\t\t\turlsIn(child, [...path, key], found);\n\t\t}\n\t}\n\treturn found;\n}\n\nfunction isHttpUrl(value: string): boolean {\n\ttry {\n\t\tconst { protocol } = new URL(value);\n\t\treturn protocol === 'http:' || protocol === 'https:';\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * The app an output's key path names, kebab-cased as app keys are: its last\n * segment, without a `url` suffix — or its parent's, when the last segment\n * is only `url`. Exact names are tried first, so an app called `api-url`\n * still finds `ApiUrl`.\n */\nfunction namesOf(path: string[]): string[] {\n\tconst names: string[] = [];\n\tfor (const segment of [...path].reverse()) {\n\t\tconst name = kebabCase(segment);\n\t\tnames.push(name);\n\t\tconst bare = name.replace(/-?url$/, '');\n\t\tif (bare) {\n\t\t\tnames.push(bare);\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn names;\n}\n\n/**\n * Each app's URL from the outputs, for the apps given. An app no output\n * names is left out.\n */\nexport function surfaceUrls(\n\toutputs: unknown,\n\tapps: readonly string[],\n): Record<string, string> {\n\tconst found = urlsIn(outputs);\n\tconst urls: Record<string, string> = {};\n\tfor (const app of apps) {\n\t\t// An exact name beats a stripped one, wherever each is in the file.\n\t\tconst exact = found.find(({ path }) => namesOf(path)[0] === app);\n\t\tconst loose = found.find(({ path }) => namesOf(path).includes(app));\n\t\tconst match = exact ?? loose;\n\t\tif (match) urls[app] = match.url;\n\t}\n\treturn urls;\n}\n","/**\n * The built-in `sst` target: `gkm build` writes the manifest, `sst deploy`\n * provisions it, and the surfaces SST reports are health-checked.\n *\n * `@geekmidas/cloud` is unchanged by it: `sst.config.ts` still reads\n * `.gkm/manifest/aws.ts` and `fromManifest` still makes every resource. What\n * the target adds is the run around the two commands — the lock, the events,\n * verification, and credentials kept where they belong.\n *\n * Both commands run in the sandbox: the build executes the project's code,\n * and so does `sst deploy`, which evaluates `sst.config.ts`. Only the second\n * needs AWS, so the AWS credential goes into that one command's environment\n * and nowhere else — not the build's, not the process the deploy runs in.\n * What `sst deploy` needs beyond them, the sandbox already passes: `PATH`\n * for node and the package manager, and `HOME`, under which SST keeps its\n * toolchain (`~/.config/sst`: bun, pulumi and its plugins) and the AWS SDK\n * finds `~/.aws` for a profile. Its state — the Pulumi backend and the\n * passphrase — is in the stage's own account, reached with that credential.\n */\n\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { ConstructManifest } from '@geekmidas/manifest';\nimport {\n\ttype AwsCredential,\n\tMissingCredential,\n} from '../../deploy/credentials';\nimport type { DeployResult } from '../../deploy/types';\nimport { GkmError } from '../../errors';\nimport { detectPackageManager, type PackageManager } from '../../init/utils';\nimport { CommandFailed } from '../../run';\nimport { LocalSandbox } from '../../sandbox/local';\nimport { activeSandbox, type Sandbox } from '../../sandbox/sandbox';\nimport { appKey } from '../../workspace/derive';\nimport { defineTarget } from '../define';\nimport type { DeployPhaseContext, DeployTarget } from '../types';\nimport { checkHealth, healthUrl, SurfacesUnhealthy } from './health';\nimport { clearOutputs, readOutputs, surfaceUrls } from './outputs';\n\nexport { SurfacesUnhealthy } from './health';\nexport { SST_OUTPUTS_FILE, SstOutputsUnreadable } from './outputs';\n\n/** A cold build of every handler and site. */\nconst BUILD_TIMEOUT_MS = 30 * 60_000;\n/**\n * A first deploy creates CloudFront distributions and, with a database, a\n * VPC and an RDS instance — each of which alone can take a quarter of an hour.\n */\nconst DEPLOY_TIMEOUT_MS = 90 * 60_000;\n\n/** The workspace has no `sst.config.ts` for `sst deploy` to run. */\nexport class SstConfigNotFound extends GkmError {\n\tconstructor(readonly cwd: string) {\n\t\tsuper(\n\t\t\t`No sst.config.ts in ${cwd}. The sst target runs \\`sst deploy\\` at the workspace root: scaffold one with \\`gkm init --deploy sst\\` and copy its sst.config.ts, or deploy through another target with --target.`,\n\t\t);\n\t\tthis.name = 'SstConfigNotFound';\n\t}\n}\n\n/** What the phases hand on. */\nexport interface SstRun {\n\tsandbox: Sandbox;\n\tpm: PackageManager;\n\taws: AwsCredential;\n\t/** The apps SST deploys this run: `ctx.apps` without the skipped. */\n\tapps: string[];\n\t/** Each app's URL, from the deploy's outputs. */\n\turls: Record<string, string>;\n}\n\nexport interface SstTargetOptions {\n\t/** How often, and how patiently, `verify` asks each surface. */\n\thealth?: { attempts?: number; delayMs?: number; timeoutMs?: number };\n}\n\n/**\n * The project's own copy of `bin`, through its package manager — never one\n * `npx` would download, which would be code nobody installed.\n */\nexport function projectCommand(\n\tpm: PackageManager,\n\tbin: string,\n\targs: readonly string[],\n): [command: string, args: string[]] {\n\tswitch (pm) {\n\t\tcase 'pnpm':\n\t\t\treturn ['pnpm', ['exec', bin, ...args]];\n\t\tcase 'yarn':\n\t\t\treturn ['yarn', [bin, ...args]];\n\t\tcase 'bun':\n\t\t\treturn ['bunx', [bin, ...args]];\n\t\tcase 'npm':\n\t\t\treturn ['npx', ['--no-install', bin, ...args]];\n\t}\n}\n\n/**\n * The variables `credential` is to the AWS SDK and to SST.\n *\n * A profile is passed alone: given keys too, every SDK would take the keys.\n */\nexport function awsEnv(credential: AwsCredential): Record<string, string> {\n\tconst region: Record<string, string> = credential.region\n\t\t? { AWS_REGION: credential.region, AWS_DEFAULT_REGION: credential.region }\n\t\t: {};\n\tif ('profile' in credential) {\n\t\treturn { AWS_PROFILE: credential.profile, ...region };\n\t}\n\treturn {\n\t\tAWS_ACCESS_KEY_ID: credential.accessKeyId,\n\t\tAWS_SECRET_ACCESS_KEY: credential.secretAccessKey,\n\t\t...(credential.sessionToken\n\t\t\t? { AWS_SESSION_TOKEN: credential.sessionToken }\n\t\t\t: {}),\n\t\t...region,\n\t};\n}\n\n/**\n * `env` without any `AWS_*`, so a host sandbox that passes some cannot put\n * a second account beside the one the credential names.\n */\nfunction withoutAws(\n\tenv: Readonly<Record<string, string>>,\n): Record<string, string> {\n\treturn Object.fromEntries(\n\t\tObject.entries(env).filter(([name]) => !name.startsWith('AWS_')),\n\t);\n}\n\n/** Each app's kind in the manifest, by app key: `rest-api` or `site`. */\nfunction kindsOf(manifest: ConstructManifest): Map<string, string> {\n\treturn new Map(\n\t\tObject.entries(manifest).map(([id, declaration]) => [\n\t\t\tappKey(id),\n\t\t\tdeclaration.kind,\n\t\t]),\n\t);\n}\n\nasync function exec(\n\tctx: DeployPhaseContext<unknown>,\n\trun: SstRun,\n\t[command, args]: [string, string[]],\n\toptions: { env: Record<string, string>; timeoutMs: number },\n): Promise<void> {\n\tconst result = await run.sandbox.exec(command, args, {\n\t\tcwd: ctx.cwd,\n\t\tenv: options.env,\n\t\ttimeoutMs: options.timeoutMs,\n\t\toutput: ctx.childOutput,\n\t\tsignal: ctx.signal,\n\t});\n\tif (result.exitCode !== 0) {\n\t\tthrow new CommandFailed(\n\t\t\tcommand,\n\t\t\targs,\n\t\t\tresult.exitCode,\n\t\t\tresult.signal as NodeJS.Signals | null,\n\t\t);\n\t}\n}\n\n/** The `sst` target, with its health checks tuned — for tests and hosts. */\nexport function createSstTarget(\n\toptions: SstTargetOptions = {},\n): DeployTarget<undefined, SstRun> {\n\tconst health = {\n\t\tattempts: options.health?.attempts ?? 6,\n\t\tdelayMs: options.health?.delayMs ?? 5_000,\n\t\ttimeoutMs: options.health?.timeoutMs ?? 10_000,\n\t};\n\n\treturn defineTarget<undefined, SstRun>({\n\t\tname: 'sst',\n\t\truntime: 'aws',\n\t\t// SST keeps no previous release to return to; migrations run in the\n\t\t// stack, as `fromManifest` provisions the database; and nothing here\n\t\t// builds an image.\n\t\tcapabilities: { rollback: false, migrations: 'target', images: false },\n\t\tcredentials: ['aws'],\n\n\t\tasync validate(ctx) {\n\t\t\tif (!existsSync(join(ctx.cwd, 'sst.config.ts'))) {\n\t\t\t\tthrow new SstConfigNotFound(ctx.cwd);\n\t\t\t}\n\n\t\t\tconst aws = await ctx.credentials.get(\n\t\t\t\t{ kind: 'aws', stage: ctx.stage },\n\t\t\t\t{ signal: ctx.signal },\n\t\t\t);\n\t\t\tif (!aws) throw new MissingCredential('aws', ctx.stage);\n\t\t\tif (!('profile' in aws)) {\n\t\t\t\tctx.secrets.mask(aws.secretAccessKey);\n\t\t\t\tif (aws.sessionToken) ctx.secrets.mask(aws.sessionToken);\n\t\t\t}\n\n\t\t\t// A mobile app ships through its own store's toolchain.\n\t\t\tfor (const app of ctx.apps) {\n\t\t\t\tif (ctx.workspace.apps[app]?.type === 'mobile') {\n\t\t\t\t\tctx.skip(app, 'mobile apps deploy through their own toolchain');\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst skipped = new Set(ctx.skipped.map(({ app }) => app));\n\t\t\tconst apps = ctx.apps.filter((app) => !skipped.has(app));\n\n\t\t\tctx.logger.info(\n\t\t\t\t`\\n🚀 Deploying \"${ctx.workspace.name}\" to the \"${ctx.stage}\" stage with SST`,\n\t\t\t);\n\t\t\tctx.logger.info(\n\t\t\t\t` AWS: ${'profile' in aws ? `profile \"${aws.profile}\"` : 'access keys'}`,\n\t\t\t);\n\t\t\t// `sst deploy` takes the whole stack: there is no deploying one app.\n\t\t\tconst deployable = Object.values(ctx.workspace.apps).filter(\n\t\t\t\t(app) => app.type !== 'mobile',\n\t\t\t).length;\n\t\t\tif (apps.length < deployable) {\n\t\t\t\tctx.logger.warn(\n\t\t\t\t\t` sst deploy deploys every construct of the stage, not only ${apps.join(', ')}.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tsandbox: activeSandbox() ?? new LocalSandbox({ root: ctx.cwd }),\n\t\t\t\tpm: detectPackageManager(ctx.cwd),\n\t\t\t\taws,\n\t\t\t\tapps,\n\t\t\t\turls: {},\n\t\t\t};\n\t\t},\n\n\t\tasync plan(ctx, run) {\n\t\t\tconst kinds = kindsOf(ctx.manifest);\n\t\t\tfor (const app of run.apps) {\n\t\t\t\tctx.emit({\n\t\t\t\t\ttype: 'resource.planned',\n\t\t\t\t\tkey: `application:${app}`,\n\t\t\t\t\tresourceType: kinds.get(app) ?? 'application',\n\t\t\t\t\taction: 'ensure',\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst [build, buildArgs] = buildCommand(ctx, run);\n\t\t\tconst [deploy, deployArgs] = deployCommand(ctx, run);\n\t\t\tctx.logger.info(` Would run: ${[build, ...buildArgs].join(' ')}`);\n\t\t\tctx.logger.info(` Would run: ${[deploy, ...deployArgs].join(' ')}`);\n\t\t},\n\n\t\tasync build(ctx, run) {\n\t\t\tctx.logger.info('\\n📦 Building the manifest SST deploys');\n\t\t\t// No credential: the build runs the project's code and needs none.\n\t\t\tawait exec(ctx, run, buildCommand(ctx, run), {\n\t\t\t\tenv: { ...run.sandbox.env },\n\t\t\t\ttimeoutMs: BUILD_TIMEOUT_MS,\n\t\t\t});\n\t\t},\n\n\t\tasync release(ctx, run) {\n\t\t\tctx.logger.info(`\\n☁️ sst deploy --stage ${ctx.stage}`);\n\t\t\tawait clearOutputs(ctx.cwd);\n\t\t\tawait exec(ctx, run, deployCommand(ctx, run), {\n\t\t\t\tenv: { ...withoutAws(run.sandbox.env), ...awsEnv(run.aws) },\n\t\t\t\ttimeoutMs: DEPLOY_TIMEOUT_MS,\n\t\t\t});\n\n\t\t\trun.urls = surfaceUrls(await readOutputs(ctx.cwd), run.apps);\n\t\t\tfor (const app of run.apps) {\n\t\t\t\tctx.emit({\n\t\t\t\t\ttype: 'app.deployed',\n\t\t\t\t\tapp,\n\t\t\t\t\tapplicationId: ctx.name(app),\n\t\t\t\t\t// SST builds no image a registry holds.\n\t\t\t\t\timageRef: '',\n\t\t\t\t\turl: run.urls[app] ?? '',\n\t\t\t\t});\n\t\t\t}\n\t\t},\n\n\t\tasync verify(ctx, run) {\n\t\t\tconst unhealthy: SurfacesUnhealthy['surfaces'][number][] = [];\n\n\t\t\tfor (const app of run.apps) {\n\t\t\t\tconst base = run.urls[app];\n\t\t\t\tif (!base) {\n\t\t\t\t\tctx.logger.warn(\n\t\t\t\t\t\t` ⚠ No URL for ${app} in SST's outputs, so it was not checked. Return it from run() in sst.config.ts — { ${app}: <its url> } — to have it checked.`,\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// A surface — a `rest-api` — is the backend app it derives.\n\t\t\t\tconst backend = ctx.workspace.apps[app]?.type === 'backend';\n\t\t\t\tconst url = healthUrl(base, backend ? '/health' : '/');\n\t\t\t\tconst answer = await checkHealth(app, url, {\n\t\t\t\t\t...health,\n\t\t\t\t\tsignal: ctx.signal,\n\t\t\t\t\temit: ctx.emit,\n\t\t\t\t});\n\t\t\t\tif (answer.healthy) {\n\t\t\t\t\tctx.logger.info(` ✓ ${app}: ${url}`);\n\t\t\t\t} else {\n\t\t\t\t\tunhealthy.push({\n\t\t\t\t\t\tapp,\n\t\t\t\t\t\turl,\n\t\t\t\t\t\t...(answer.status !== undefined ? { status: answer.status } : {}),\n\t\t\t\t\t\t...(answer.error ? { error: answer.error } : {}),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (unhealthy.length > 0) {\n\t\t\t\tthrow new SurfacesUnhealthy(ctx.stage, unhealthy);\n\t\t\t}\n\t\t},\n\n\t\tresult(ctx, run): DeployResult {\n\t\t\treturn {\n\t\t\t\tapps: run.apps.map((appName) => ({\n\t\t\t\t\tappName,\n\t\t\t\t\ttype: ctx.workspace.apps[appName]?.type ?? 'backend',\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\t...(run.urls[appName] ? { url: run.urls[appName] } : {}),\n\t\t\t\t})),\n\t\t\t\tprojectId: '',\n\t\t\t\tenvironmentId: '',\n\t\t\t\tsuccessCount: ctx.dryRun ? 0 : run.apps.length,\n\t\t\t\tfailedCount: 0,\n\t\t\t\tstage: ctx.stage,\n\t\t\t\tidentity: ctx.identity.key,\n\t\t\t\ttag: ctx.tag,\n\t\t\t\tdryRun: ctx.dryRun,\n\t\t\t\tskipped: [...ctx.skipped],\n\t\t\t\turls: { ...run.urls },\n\t\t\t\tchanges: [],\n\t\t\t};\n\t\t},\n\t});\n}\n\nfunction buildCommand(\n\tctx: DeployPhaseContext<unknown>,\n\trun: SstRun,\n): [string, string[]] {\n\treturn projectCommand(run.pm, 'gkm', [\n\t\t'build',\n\t\t'--provider',\n\t\t'aws',\n\t\t'--stage',\n\t\tctx.stage,\n\t]);\n}\n\nfunction deployCommand(\n\tctx: DeployPhaseContext<unknown>,\n\trun: SstRun,\n): [string, string[]] {\n\treturn projectCommand(run.pm, 'sst', ['deploy', '--stage', ctx.stage]);\n}\n\nexport const sstTarget = createSstTarget();\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAyBA,IAAa,oBAAb,cAAuCA,eAAAA,SAAS;CAErC;CAEA;CAHV,YACC,OAEA,UAMC;EACD,MAAM,QAAQ,SAAS,KACrB,EAAE,KAAK,KAAK,QAAQ,YACpB,OAAO,IAAI,IAAI,IAAI,KAAK,UAAU,SAAS,aAC7C;EACA,MACC,QAAQ,MAAM,4BAA4B,SAAS,WAAW,IAAI,mBAAmB,cAAc,wBAAwB,MAAM,KAAK,IAAI,EAAE,0DAC7I;EAfS,KAAA,QAAA;EAEA,KAAA,WAAA;EAcT,KAAK,OAAO;CACb;AACD;;AAGA,SAAgB,UAAU,MAAc,MAAsB;CAC7D,MAAM,OAAO,KAAK,SAAS,GAAG,IAAI,OAAO,GAAG,KAAK;CACjD,OAAO,IAAI,IAAI,KAAK,QAAQ,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC,SAAS;AACxD;;;;;AAMA,eAAsB,YACrB,KACA,KACA,SACiE;CACjE,IAAI,OAA8D,EACjE,SAAS,MACV;CACA,KAAK,IAAI,UAAU,GAAG,WAAW,QAAQ,UAAU,WAAW;EAC7D,QAAQ,OAAO,eAAe;EAC9B,IAAI;GACH,MAAM,WAAW,MAAM,MAAM,KAAK;IACjC,UAAU;IACV,QAAQ,YAAY,IAAI,CACvB,QAAQ,QACR,YAAY,QAAQ,QAAQ,SAAS,CACtC,CAAC;GACF,CAAC;GAED,MAAM,SAAS,YAAY,CAAC,CAAC,YAAY,KAAA,CAAS;GAClD,OAAO;IAAE,SAAS,SAAS;IAAI,QAAQ,SAAS;GAAO;EACxD,SAAS,OAAO;GAEf,QAAQ,OAAO,eAAe;GAC9B,OAAO;IACN,SAAS;IACT,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GAC7D;EACD;EACA,QAAQ,KAAK;GACZ,MAAM;GACN;GACA;GACA,SAAS,KAAK;GACd,GAAI,KAAK,WAAW,KAAA,IAAY,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;GAC3D;EACD,CAAC;EACD,IAAI,KAAK,SAAS,OAAO;EACzB,IAAI,UAAU,QAAQ,UAAU,MAAM,KAAK,QAAQ,SAAS,QAAQ,MAAM;CAC3E;CACA,OAAO;AACR;AAEA,SAAS,KAAK,IAAY,QAAoC;CAC7D,IAAI,MAAM,GAAG,OAAO,QAAQ,QAAQ;CACpC,OAAO,IAAI,SAAS,SAAS,WAAW;EACvC,MAAM,QAAQ,iBAAiB;GAC9B,OAAO,oBAAoB,SAAS,IAAI;GACxC,QAAQ;EACT,GAAG,EAAE;EACL,MAAM,aAAa;GAClB,aAAa,KAAK;GAClB,OAAO,OAAO,MAAM;EACrB;EACA,OAAO,iBAAiB,SAAS,MAAM,EAAE,MAAM,KAAK,CAAC;CACtD,CAAC;AACF;;;;;;;;;;;;;;AC/FA,MAAa,oBAAA,GAAmBC,UAAAA,KAAAA,CAAK,QAAQ,cAAc;;AAG3D,IAAa,uBAAb,cAA0CC,eAAAA,SAAS;CAExC;CACA;CAFV,YACC,MACA,QACC;EACD,MACC,mCAAmC,KAAK,IAAI,OAAO,kFACpD;EALS,KAAA,OAAA;EACA,KAAA,SAAA;EAKT,KAAK,OAAO;CACb;AACD;;;;;AAMA,eAAsB,aAAa,KAA4B;CAC9D,OAAA,GAAMC,iBAAAA,GAAAA,EAAAA,GAAGF,UAAAA,KAAAA,CAAK,KAAK,gBAAgB,GAAG,EAAE,OAAO,KAAK,CAAC;AACtD;;AAGA,eAAsB,YAAY,KAA+B;CAChE,MAAM,QAAA,GAAOA,UAAAA,KAAAA,CAAK,KAAK,gBAAgB;CACvC,IAAI,EAAA,GAACG,QAAAA,WAAAA,CAAW,IAAI,GAAG,OAAO,CAAC;CAC/B,IAAI;EACH,OAAO,KAAK,MAAM,OAAA,GAAMC,iBAAAA,SAAAA,CAAS,MAAM,MAAM,CAAC;CAC/C,SAAS,OAAO;EACf,MAAM,IAAI,qBACT,MACA,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CACtD;CACD;AACD;;AAGA,SAAS,OACR,OACA,OAAiB,CAAC,GAClB,QAA2C,CAAC,GACR;CACpC,IAAI,OAAO,UAAU,UAChB;MAAA,UAAU,KAAK,GAAG,MAAM,KAAK;GAAE;GAAM,KAAK;EAAM,CAAC;CAAA,OAC/C,IAAI,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,GACpE,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,GAC9C,OAAO,OAAO,CAAC,GAAG,MAAM,GAAG,GAAG,KAAK;CAGrC,OAAO;AACR;AAEA,SAAS,UAAU,OAAwB;CAC1C,IAAI;EACH,MAAM,EAAE,aAAa,IAAI,IAAI,KAAK;EAClC,OAAO,aAAa,WAAW,aAAa;CAC7C,QAAQ;EACP,OAAO;CACR;AACD;;;;;;;AAQA,SAAS,QAAQ,MAA0B;CAC1C,MAAM,QAAkB,CAAC;CACzB,KAAK,MAAM,WAAW,CAAC,GAAG,IAAI,CAAC,CAAC,QAAQ,GAAG;EAC1C,MAAM,QAAA,GAAOC,oBAAAA,UAAAA,CAAU,OAAO;EAC9B,MAAM,KAAK,IAAI;EACf,MAAM,OAAO,KAAK,QAAQ,UAAU,EAAE;EACtC,IAAI,MAAM;GACT,MAAM,KAAK,IAAI;GACf;EACD;CACD;CACA,OAAO;AACR;;;;;AAMA,SAAgB,YACf,SACA,MACyB;CACzB,MAAM,QAAQ,OAAO,OAAO;CAC5B,MAAM,OAA+B,CAAC;CACtC,KAAK,MAAM,OAAO,MAAM;EAEvB,MAAM,QAAQ,MAAM,MAAM,EAAE,WAAW,QAAQ,IAAI,CAAC,CAAC,OAAO,GAAG;EAC/D,MAAM,QAAQ,MAAM,MAAM,EAAE,WAAW,QAAQ,IAAI,CAAC,CAAC,SAAS,GAAG,CAAC;EAClE,MAAM,QAAQ,SAAS;EACvB,IAAI,OAAO,KAAK,OAAO,MAAM;CAC9B;CACA,OAAO;AACR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC3EA,MAAM,mBAAmB;;;;;AAKzB,MAAM,oBAAoB;;AAG1B,IAAa,oBAAb,cAAuCC,eAAAA,SAAS;CAC1B;CAArB,YAAY,KAAsB;EACjC,MACC,uBAAuB,IAAI,oLAC5B;EAHoB,KAAA,MAAA;EAIpB,KAAK,OAAO;CACb;AACD;;;;;AAsBA,SAAgB,eACf,IACA,KACA,MACoC;CACpC,QAAQ,IAAR;EACC,KAAK,QACJ,OAAO,CAAC,QAAQ;GAAC;GAAQ;GAAK,GAAG;EAAI,CAAC;EACvC,KAAK,QACJ,OAAO,CAAC,QAAQ,CAAC,KAAK,GAAG,IAAI,CAAC;EAC/B,KAAK,OACJ,OAAO,CAAC,QAAQ,CAAC,KAAK,GAAG,IAAI,CAAC;EAC/B,KAAK,OACJ,OAAO,CAAC,OAAO;GAAC;GAAgB;GAAK,GAAG;EAAI,CAAC;CAC/C;AACD;;;;;;AAOA,SAAgB,OAAO,YAAmD;CACzE,MAAM,SAAiC,WAAW,SAC/C;EAAE,YAAY,WAAW;EAAQ,oBAAoB,WAAW;CAAO,IACvE,CAAC;CACJ,IAAI,aAAa,YAChB,OAAO;EAAE,aAAa,WAAW;EAAS,GAAG;CAAO;CAErD,OAAO;EACN,mBAAmB,WAAW;EAC9B,uBAAuB,WAAW;EAClC,GAAI,WAAW,eACZ,EAAE,mBAAmB,WAAW,aAAa,IAC7C,CAAC;EACJ,GAAG;CACJ;AACD;;;;;AAMA,SAAS,WACR,KACyB;CACzB,OAAO,OAAO,YACb,OAAO,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,KAAK,WAAW,MAAM,CAAC,CAChE;AACD;;AAGA,SAAS,QAAQ,UAAkD;CAClE,OAAO,IAAI,IACV,OAAO,QAAQ,QAAQ,CAAC,CAAC,KAAK,CAAC,IAAI,iBAAiB,CACnDC,eAAAA,OAAO,EAAE,GACT,YAAY,IACb,CAAC,CACF;AACD;AAEA,eAAe,KACd,KACA,KACA,CAAC,SAAS,OACV,SACgB;CAChB,MAAM,SAAS,MAAM,IAAI,QAAQ,KAAK,SAAS,MAAM;EACpD,KAAK,IAAI;EACT,KAAK,QAAQ;EACb,WAAW,QAAQ;EACnB,QAAQ,IAAI;EACZ,QAAQ,IAAI;CACb,CAAC;CACD,IAAI,OAAO,aAAa,GACvB,MAAM,IAAIC,oBAAAA,cACT,SACA,MACA,OAAO,UACP,OAAO,MACR;AAEF;;AAGA,SAAgB,gBACf,UAA4B,CAAC,GACK;CAClC,MAAM,SAAS;EACd,UAAU,QAAQ,QAAQ,YAAY;EACtC,SAAS,QAAQ,QAAQ,WAAW;EACpC,WAAW,QAAQ,QAAQ,aAAa;CACzC;CAEA,OAAOC,eAAAA,aAAgC;EACtC,MAAM;EACN,SAAS;EAIT,cAAc;GAAE,UAAU;GAAO,YAAY;GAAU,QAAQ;EAAM;EACrE,aAAa,CAAC,KAAK;EAEnB,MAAM,SAAS,KAAK;GACnB,IAAI,EAAA,GAACC,QAAAA,WAAAA,EAAAA,GAAWC,UAAAA,KAAAA,CAAK,IAAI,KAAK,eAAe,CAAC,GAC7C,MAAM,IAAI,kBAAkB,IAAI,GAAG;GAGpC,MAAM,MAAM,MAAM,IAAI,YAAY,IACjC;IAAE,MAAM;IAAO,OAAO,IAAI;GAAM,GAChC,EAAE,QAAQ,IAAI,OAAO,CACtB;GACA,IAAI,CAAC,KAAK,MAAM,IAAIC,oBAAAA,kBAAkB,OAAO,IAAI,KAAK;GACtD,IAAI,EAAE,aAAa,MAAM;IACxB,IAAI,QAAQ,KAAK,IAAI,eAAe;IACpC,IAAI,IAAI,cAAc,IAAI,QAAQ,KAAK,IAAI,YAAY;GACxD;GAGA,KAAK,MAAM,OAAO,IAAI,MACrB,IAAI,IAAI,UAAU,KAAK,IAAI,EAAE,SAAS,UACrC,IAAI,KAAK,KAAK,gDAAgD;GAGhE,MAAM,UAAU,IAAI,IAAI,IAAI,QAAQ,KAAK,EAAE,UAAU,GAAG,CAAC;GACzD,MAAM,OAAO,IAAI,KAAK,QAAQ,QAAQ,CAAC,QAAQ,IAAI,GAAG,CAAC;GAEvD,IAAI,OAAO,KACV,mBAAmB,IAAI,UAAU,KAAK,YAAY,IAAI,MAAM,iBAC7D;GACA,IAAI,OAAO,KACV,WAAW,aAAa,MAAM,YAAY,IAAI,QAAQ,KAAK,eAC5D;GAEA,MAAM,aAAa,OAAO,OAAO,IAAI,UAAU,IAAI,CAAC,CAAC,QACnD,QAAQ,IAAI,SAAS,QACvB,CAAC,CAAC;GACF,IAAI,KAAK,SAAS,YACjB,IAAI,OAAO,KACV,gEAAgE,KAAK,KAAK,IAAI,EAAE,EACjF;GAGD,OAAO;IACN,SAASC,gBAAAA,cAAc,KAAK,IAAIC,cAAAA,aAAa,EAAE,MAAM,IAAI,IAAI,CAAC;IAC9D,IAAIC,cAAAA,qBAAqB,IAAI,GAAG;IAChC;IACA;IACA,MAAM,CAAC;GACR;EACD;EAEA,MAAM,KAAK,KAAK,KAAK;GACpB,MAAM,QAAQ,QAAQ,IAAI,QAAQ;GAClC,KAAK,MAAM,OAAO,IAAI,MACrB,IAAI,KAAK;IACR,MAAM;IACN,KAAK,eAAe;IACpB,cAAc,MAAM,IAAI,GAAG,KAAK;IAChC,QAAQ;GACT,CAAC;GAEF,MAAM,CAAC,OAAO,aAAa,aAAa,KAAK,GAAG;GAChD,MAAM,CAAC,QAAQ,cAAc,cAAc,KAAK,GAAG;GACnD,IAAI,OAAO,KAAK,iBAAiB,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC,KAAK,GAAG,GAAG;GAClE,IAAI,OAAO,KAAK,iBAAiB,CAAC,QAAQ,GAAG,UAAU,CAAC,CAAC,KAAK,GAAG,GAAG;EACrE;EAEA,MAAM,MAAM,KAAK,KAAK;GACrB,IAAI,OAAO,KAAK,wCAAwC;GAExD,MAAM,KAAK,KAAK,KAAK,aAAa,KAAK,GAAG,GAAG;IAC5C,KAAK,EAAE,GAAG,IAAI,QAAQ,IAAI;IAC1B,WAAW;GACZ,CAAC;EACF;EAEA,MAAM,QAAQ,KAAK,KAAK;GACvB,IAAI,OAAO,KAAK,4BAA4B,IAAI,OAAO;GACvD,MAAM,aAAa,IAAI,GAAG;GAC1B,MAAM,KAAK,KAAK,KAAK,cAAc,KAAK,GAAG,GAAG;IAC7C,KAAK;KAAE,GAAG,WAAW,IAAI,QAAQ,GAAG;KAAG,GAAG,OAAO,IAAI,GAAG;IAAE;IAC1D,WAAW;GACZ,CAAC;GAED,IAAI,OAAO,YAAY,MAAM,YAAY,IAAI,GAAG,GAAG,IAAI,IAAI;GAC3D,KAAK,MAAM,OAAO,IAAI,MACrB,IAAI,KAAK;IACR,MAAM;IACN;IACA,eAAe,IAAI,KAAK,GAAG;IAE3B,UAAU;IACV,KAAK,IAAI,KAAK,QAAQ;GACvB,CAAC;EAEH;EAEA,MAAM,OAAO,KAAK,KAAK;GACtB,MAAM,YAAqD,CAAC;GAE5D,KAAK,MAAM,OAAO,IAAI,MAAM;IAC3B,MAAM,OAAO,IAAI,KAAK;IACtB,IAAI,CAAC,MAAM;KACV,IAAI,OAAO,KACV,mBAAmB,IAAI,sFAAsF,IAAI,oCAClH;KACA;IACD;IAGA,MAAM,MAAM,UAAU,MADN,IAAI,UAAU,KAAK,IAAI,EAAE,SAAS,YACZ,YAAY,GAAG;IACrD,MAAM,SAAS,MAAM,YAAY,KAAK,KAAK;KAC1C,GAAG;KACH,QAAQ,IAAI;KACZ,MAAM,IAAI;IACX,CAAC;IACD,IAAI,OAAO,SACV,IAAI,OAAO,KAAK,QAAQ,IAAI,IAAI,KAAK;SAErC,UAAU,KAAK;KACd;KACA;KACA,GAAI,OAAO,WAAW,KAAA,IAAY,EAAE,QAAQ,OAAO,OAAO,IAAI,CAAC;KAC/D,GAAI,OAAO,QAAQ,EAAE,OAAO,OAAO,MAAM,IAAI,CAAC;IAC/C,CAAC;GAEH;GAEA,IAAI,UAAU,SAAS,GACtB,MAAM,IAAI,kBAAkB,IAAI,OAAO,SAAS;EAElD;EAEA,OAAO,KAAK,KAAmB;GAC9B,OAAO;IACN,MAAM,IAAI,KAAK,KAAK,aAAa;KAChC;KACA,MAAM,IAAI,UAAU,KAAK,QAAQ,EAAE,QAAQ;KAC3C,SAAS;KACT,GAAI,IAAI,KAAK,WAAW,EAAE,KAAK,IAAI,KAAK,SAAS,IAAI,CAAC;IACvD,EAAE;IACF,WAAW;IACX,eAAe;IACf,cAAc,IAAI,SAAS,IAAI,IAAI,KAAK;IACxC,aAAa;IACb,OAAO,IAAI;IACX,UAAU,IAAI,SAAS;IACvB,KAAK,IAAI;IACT,QAAQ,IAAI;IACZ,SAAS,CAAC,GAAG,IAAI,OAAO;IACxB,MAAM,EAAE,GAAG,IAAI,KAAK;IACpB,SAAS,CAAC;GACX;EACD;CACD,CAAC;AACF;AAEA,SAAS,aACR,KACA,KACqB;CACrB,OAAO,eAAe,IAAI,IAAI,OAAO;EACpC;EACA;EACA;EACA;EACA,IAAI;CACL,CAAC;AACF;AAEA,SAAS,cACR,KACA,KACqB;CACrB,OAAO,eAAe,IAAI,IAAI,OAAO;EAAC;EAAU;EAAW,IAAI;CAAK,CAAC;AACtE;AAEA,MAAa,YAAY,gBAAgB"}
1
+ {"version":3,"file":"sst-DmVJRu5l.cjs","names":["GkmError","join","GkmError","rm","existsSync","readFile","kebabCase","GkmError","appKey","CommandFailed","defineTarget","existsSync","join","MissingCredential","activeSandbox","LocalSandbox","detectPackageManager"],"sources":["../src/target/sst/health.ts","../src/target/sst/outputs.ts","../src/target/sst/index.ts"],"sourcesContent":["/**\n * Whether a released surface answers.\n *\n * The paths are the ones the local stack's healthchecks use\n * (`reconcile/apps.ts`): an API's `/health`, a site's `/`. A surface is\n * asked a few times, because a fresh deploy can take a moment to answer — a\n * Lambda's first start, a distribution still settling — and healthy means a\n * 2xx, after redirects.\n */\n\nimport { GkmError } from '../../errors';\nimport type { TargetEvent } from '../types';\n\nexport interface HealthCheckOptions {\n\t/** How many times a surface is asked before it counts as down. */\n\tattempts: number;\n\t/** The wait between two attempts. */\n\tdelayMs: number;\n\t/** How long one request may take. */\n\ttimeoutMs: number;\n\tsignal: AbortSignal;\n\temit: (event: TargetEvent) => void;\n}\n\n/** Surfaces that never answered healthy. */\nexport class SurfacesUnhealthy extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\t/** Each surface that failed, with the last thing it answered. */\n\t\treadonly surfaces: readonly {\n\t\t\tapp: string;\n\t\t\turl: string;\n\t\t\tstatus?: number;\n\t\t\terror?: string;\n\t\t}[],\n\t) {\n\t\tconst lines = surfaces.map(\n\t\t\t({ app, url, status, error }) =>\n\t\t\t\t` - ${app}: ${url} → ${status ?? error ?? 'no answer'}`,\n\t\t);\n\t\tsuper(\n\t\t\t`The \"${stage}\" stage was deployed, but ${surfaces.length === 1 ? 'a surface does' : 'surfaces do'} not answer healthy:\\n${lines.join('\\n')}\\nCheck its logs in CloudWatch, fix it, and deploy again.`,\n\t\t);\n\t\tthis.name = 'SurfacesUnhealthy';\n\t}\n}\n\n/** `path` under `base`, keeping any path `base` already has. */\nexport function healthUrl(base: string, path: string): string {\n\tconst root = base.endsWith('/') ? base : `${base}/`;\n\treturn new URL(path.replace(/^\\//, ''), root).toString();\n}\n\n/**\n * Ask `url` until it answers 2xx or the attempts run out, emitting\n * `health.checked` for each. Resolves with the last answer.\n */\nexport async function checkHealth(\n\tapp: string,\n\turl: string,\n\toptions: HealthCheckOptions,\n): Promise<{ healthy: boolean; status?: number; error?: string }> {\n\tlet last: { healthy: boolean; status?: number; error?: string } = {\n\t\thealthy: false,\n\t};\n\tfor (let attempt = 1; attempt <= options.attempts; attempt++) {\n\t\toptions.signal.throwIfAborted();\n\t\ttry {\n\t\t\tconst response = await fetch(url, {\n\t\t\t\tredirect: 'follow',\n\t\t\t\tsignal: AbortSignal.any([\n\t\t\t\t\toptions.signal,\n\t\t\t\t\tAbortSignal.timeout(options.timeoutMs),\n\t\t\t\t]),\n\t\t\t});\n\t\t\t// Drained, so the connection is not held open by an unread body.\n\t\t\tawait response.arrayBuffer().catch(() => undefined);\n\t\t\tlast = { healthy: response.ok, status: response.status };\n\t\t} catch (error) {\n\t\t\t// The run being stopped is not the surface being down.\n\t\t\toptions.signal.throwIfAborted();\n\t\t\tlast = {\n\t\t\t\thealthy: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t};\n\t\t}\n\t\toptions.emit({\n\t\t\ttype: 'health.checked',\n\t\t\tapp,\n\t\t\turl,\n\t\t\thealthy: last.healthy,\n\t\t\t...(last.status !== undefined ? { status: last.status } : {}),\n\t\t\tattempt,\n\t\t});\n\t\tif (last.healthy) return last;\n\t\tif (attempt < options.attempts) await wait(options.delayMs, options.signal);\n\t}\n\treturn last;\n}\n\nfunction wait(ms: number, signal: AbortSignal): Promise<void> {\n\tif (ms <= 0) return Promise.resolve();\n\treturn new Promise((resolve, reject) => {\n\t\tconst timer = setTimeout(() => {\n\t\t\tsignal.removeEventListener('abort', stop);\n\t\t\tresolve();\n\t\t}, ms);\n\t\tconst stop = () => {\n\t\t\tclearTimeout(timer);\n\t\t\treject(signal.reason);\n\t\t};\n\t\tsignal.addEventListener('abort', stop, { once: true });\n\t});\n}\n","/**\n * The surface URLs an `sst deploy` reports.\n *\n * SST writes whatever `run()` in `sst.config.ts` returns to\n * `.sst/outputs.json` after every successful deploy — `{\"Api\": \"https://…\"}`.\n * What `run()` returns is the project's to write, so this reads it leniently:\n * nested objects are walked, anything that is not an http(s) URL is ignored,\n * and an output names a surface by its id in any casing, with or without a\n * `Url` suffix (`Api`, `api`, `ApiUrl`, `API_URL`, `Api.url`).\n */\n\nimport { existsSync } from 'node:fs';\nimport { readFile, rm } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { kebabCase } from '@geekmidas/manifest';\nimport { GkmError } from '../../errors';\n\n/** Where SST leaves the last successful deploy's outputs, under the project. */\nexport const SST_OUTPUTS_FILE = join('.sst', 'outputs.json');\n\n/** `.sst/outputs.json` is there, and is not JSON. */\nexport class SstOutputsUnreadable extends GkmError {\n\tconstructor(\n\t\treadonly path: string,\n\t\treadonly reason: string,\n\t) {\n\t\tsuper(\n\t\t\t`Could not read SST's outputs at ${path}: ${reason}. It is rewritten by every successful \\`sst deploy\\`; delete it and deploy again.`,\n\t\t);\n\t\tthis.name = 'SstOutputsUnreadable';\n\t}\n}\n\n/**\n * Remove what an earlier deploy wrote, so the outputs read after this one\n * are this one's — not another stage's, deployed from the same checkout.\n */\nexport async function clearOutputs(cwd: string): Promise<void> {\n\tawait rm(join(cwd, SST_OUTPUTS_FILE), { force: true });\n}\n\n/** The deploy's outputs, or `{}` when it wrote none. */\nexport async function readOutputs(cwd: string): Promise<unknown> {\n\tconst path = join(cwd, SST_OUTPUTS_FILE);\n\tif (!existsSync(path)) return {};\n\ttry {\n\t\treturn JSON.parse(await readFile(path, 'utf8'));\n\t} catch (error) {\n\t\tthrow new SstOutputsUnreadable(\n\t\t\tpath,\n\t\t\terror instanceof Error ? error.message : String(error),\n\t\t);\n\t}\n}\n\n/** Every http(s) URL in `outputs`, by its key path: `['Api', 'url']`. */\nfunction urlsIn(\n\tvalue: unknown,\n\tpath: string[] = [],\n\tfound: { path: string[]; url: string }[] = [],\n): { path: string[]; url: string }[] {\n\tif (typeof value === 'string') {\n\t\tif (isHttpUrl(value)) found.push({ path, url: value });\n\t} else if (value && typeof value === 'object' && !Array.isArray(value)) {\n\t\tfor (const [key, child] of Object.entries(value)) {\n\t\t\turlsIn(child, [...path, key], found);\n\t\t}\n\t}\n\treturn found;\n}\n\nfunction isHttpUrl(value: string): boolean {\n\ttry {\n\t\tconst { protocol } = new URL(value);\n\t\treturn protocol === 'http:' || protocol === 'https:';\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * The app an output's key path names, kebab-cased as app keys are: its last\n * segment, without a `url` suffix — or its parent's, when the last segment\n * is only `url`. Exact names are tried first, so an app called `api-url`\n * still finds `ApiUrl`.\n */\nfunction namesOf(path: string[]): string[] {\n\tconst names: string[] = [];\n\tfor (const segment of [...path].reverse()) {\n\t\tconst name = kebabCase(segment);\n\t\tnames.push(name);\n\t\tconst bare = name.replace(/-?url$/, '');\n\t\tif (bare) {\n\t\t\tnames.push(bare);\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn names;\n}\n\n/**\n * Each app's URL from the outputs, for the apps given. An app no output\n * names is left out.\n */\nexport function surfaceUrls(\n\toutputs: unknown,\n\tapps: readonly string[],\n): Record<string, string> {\n\tconst found = urlsIn(outputs);\n\tconst urls: Record<string, string> = {};\n\tfor (const app of apps) {\n\t\t// An exact name beats a stripped one, wherever each is in the file.\n\t\tconst exact = found.find(({ path }) => namesOf(path)[0] === app);\n\t\tconst loose = found.find(({ path }) => namesOf(path).includes(app));\n\t\tconst match = exact ?? loose;\n\t\tif (match) urls[app] = match.url;\n\t}\n\treturn urls;\n}\n","/**\n * The built-in `sst` target: `gkm build` writes the manifest, `sst deploy`\n * provisions it, and the surfaces SST reports are health-checked.\n *\n * `@geekmidas/cloud` is unchanged by it: `sst.config.ts` still reads\n * `.gkm/manifest/aws.ts` and `fromManifest` still makes every resource. What\n * the target adds is the run around the two commands — the lock, the events,\n * verification, and credentials kept where they belong.\n *\n * Both commands run in the sandbox: the build executes the project's code,\n * and so does `sst deploy`, which evaluates `sst.config.ts`. Only the second\n * needs AWS, so the AWS credential goes into that one command's environment\n * and nowhere else — not the build's, not the process the deploy runs in.\n * What `sst deploy` needs beyond them, the sandbox already passes: `PATH`\n * for node and the package manager, and `HOME`, under which SST keeps its\n * toolchain (`~/.config/sst`: bun, pulumi and its plugins) and the AWS SDK\n * finds `~/.aws` for a profile. Its state — the Pulumi backend and the\n * passphrase — is in the stage's own account, reached with that credential.\n */\n\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { ConstructManifest } from '@geekmidas/manifest';\nimport {\n\ttype AwsCredential,\n\tMissingCredential,\n} from '../../deploy/credentials';\nimport type { DeployResult } from '../../deploy/types';\nimport { GkmError } from '../../errors';\nimport { detectPackageManager, type PackageManager } from '../../init/utils';\nimport { CommandFailed } from '../../run';\nimport { LocalSandbox } from '../../sandbox/local';\nimport { activeSandbox, type Sandbox } from '../../sandbox/sandbox';\nimport { appKey } from '../../workspace/derive';\nimport { defineTarget } from '../define';\nimport type { DeployPhaseContext, DeployTarget } from '../types';\nimport { checkHealth, healthUrl, SurfacesUnhealthy } from './health';\nimport { clearOutputs, readOutputs, surfaceUrls } from './outputs';\n\nexport { SurfacesUnhealthy } from './health';\nexport { SST_OUTPUTS_FILE, SstOutputsUnreadable } from './outputs';\n\n/** A cold build of every handler and site. */\nconst BUILD_TIMEOUT_MS = 30 * 60_000;\n/**\n * A first deploy creates CloudFront distributions and, with a database, a\n * VPC and an RDS instance — each of which alone can take a quarter of an hour.\n */\nconst DEPLOY_TIMEOUT_MS = 90 * 60_000;\n\n/** The workspace has no `sst.config.ts` for `sst deploy` to run. */\nexport class SstConfigNotFound extends GkmError {\n\tconstructor(readonly cwd: string) {\n\t\tsuper(\n\t\t\t`No sst.config.ts in ${cwd}. The sst target runs \\`sst deploy\\` at the workspace root: scaffold one with \\`gkm init --deploy sst\\` and copy its sst.config.ts, or deploy through another target with --target.`,\n\t\t);\n\t\tthis.name = 'SstConfigNotFound';\n\t}\n}\n\n/** What the phases hand on. */\nexport interface SstRun {\n\tsandbox: Sandbox;\n\tpm: PackageManager;\n\taws: AwsCredential;\n\t/** The apps SST deploys this run: `ctx.apps` without the skipped. */\n\tapps: string[];\n\t/** Each app's URL, from the deploy's outputs. */\n\turls: Record<string, string>;\n}\n\nexport interface SstTargetOptions {\n\t/** How often, and how patiently, `verify` asks each surface. */\n\thealth?: { attempts?: number; delayMs?: number; timeoutMs?: number };\n}\n\n/**\n * The project's own copy of `bin`, through its package manager — never one\n * `npx` would download, which would be code nobody installed.\n */\nexport function projectCommand(\n\tpm: PackageManager,\n\tbin: string,\n\targs: readonly string[],\n): [command: string, args: string[]] {\n\tswitch (pm) {\n\t\tcase 'pnpm':\n\t\t\treturn ['pnpm', ['exec', bin, ...args]];\n\t\tcase 'yarn':\n\t\t\treturn ['yarn', [bin, ...args]];\n\t\tcase 'bun':\n\t\t\treturn ['bunx', [bin, ...args]];\n\t\tcase 'npm':\n\t\t\treturn ['npx', ['--no-install', bin, ...args]];\n\t}\n}\n\n/**\n * The variables `credential` is to the AWS SDK and to SST.\n *\n * A profile is passed alone: given keys too, every SDK would take the keys.\n */\nexport function awsEnv(credential: AwsCredential): Record<string, string> {\n\tconst region: Record<string, string> = credential.region\n\t\t? { AWS_REGION: credential.region, AWS_DEFAULT_REGION: credential.region }\n\t\t: {};\n\tif ('profile' in credential) {\n\t\treturn { AWS_PROFILE: credential.profile, ...region };\n\t}\n\treturn {\n\t\tAWS_ACCESS_KEY_ID: credential.accessKeyId,\n\t\tAWS_SECRET_ACCESS_KEY: credential.secretAccessKey,\n\t\t...(credential.sessionToken\n\t\t\t? { AWS_SESSION_TOKEN: credential.sessionToken }\n\t\t\t: {}),\n\t\t...region,\n\t};\n}\n\n/**\n * `env` without any `AWS_*`, so a host sandbox that passes some cannot put\n * a second account beside the one the credential names.\n */\nfunction withoutAws(\n\tenv: Readonly<Record<string, string>>,\n): Record<string, string> {\n\treturn Object.fromEntries(\n\t\tObject.entries(env).filter(([name]) => !name.startsWith('AWS_')),\n\t);\n}\n\n/** Each app's kind in the manifest, by app key: `rest-api` or `site`. */\nfunction kindsOf(manifest: ConstructManifest): Map<string, string> {\n\treturn new Map(\n\t\tObject.entries(manifest).map(([id, declaration]) => [\n\t\t\tappKey(id),\n\t\t\tdeclaration.kind,\n\t\t]),\n\t);\n}\n\nasync function exec(\n\tctx: DeployPhaseContext<unknown>,\n\trun: SstRun,\n\t[command, args]: [string, string[]],\n\toptions: { env: Record<string, string>; timeoutMs: number },\n): Promise<void> {\n\tconst result = await run.sandbox.exec(command, args, {\n\t\tcwd: ctx.cwd,\n\t\tenv: options.env,\n\t\ttimeoutMs: options.timeoutMs,\n\t\toutput: ctx.childOutput,\n\t\tsignal: ctx.signal,\n\t});\n\tif (result.exitCode !== 0) {\n\t\tthrow new CommandFailed(\n\t\t\tcommand,\n\t\t\targs,\n\t\t\tresult.exitCode,\n\t\t\tresult.signal as NodeJS.Signals | null,\n\t\t);\n\t}\n}\n\n/** The `sst` target, with its health checks tuned — for tests and hosts. */\nexport function createSstTarget(\n\toptions: SstTargetOptions = {},\n): DeployTarget<undefined, SstRun> {\n\tconst health = {\n\t\tattempts: options.health?.attempts ?? 6,\n\t\tdelayMs: options.health?.delayMs ?? 5_000,\n\t\ttimeoutMs: options.health?.timeoutMs ?? 10_000,\n\t};\n\n\treturn defineTarget<undefined, SstRun>({\n\t\tname: 'sst',\n\t\truntime: 'aws',\n\t\t// SST keeps no previous release to return to; migrations run in the\n\t\t// stack, as `fromManifest` provisions the database; and nothing here\n\t\t// builds an image.\n\t\tcapabilities: { rollback: false, migrations: 'target', images: false },\n\t\tcredentials: ['aws'],\n\n\t\tasync validate(ctx) {\n\t\t\tif (!existsSync(join(ctx.cwd, 'sst.config.ts'))) {\n\t\t\t\tthrow new SstConfigNotFound(ctx.cwd);\n\t\t\t}\n\n\t\t\tconst aws = await ctx.credentials.get(\n\t\t\t\t{ kind: 'aws', stage: ctx.stage },\n\t\t\t\t{ signal: ctx.signal },\n\t\t\t);\n\t\t\tif (!aws) throw new MissingCredential('aws', ctx.stage);\n\t\t\tif (!('profile' in aws)) {\n\t\t\t\tctx.secrets.mask(aws.secretAccessKey);\n\t\t\t\tif (aws.sessionToken) ctx.secrets.mask(aws.sessionToken);\n\t\t\t}\n\n\t\t\t// A mobile app ships through its own store's toolchain.\n\t\t\tfor (const app of ctx.apps) {\n\t\t\t\tif (ctx.workspace.apps[app]?.type === 'mobile') {\n\t\t\t\t\tctx.skip(app, 'mobile apps deploy through their own toolchain');\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst skipped = new Set(ctx.skipped.map(({ app }) => app));\n\t\t\tconst apps = ctx.apps.filter((app) => !skipped.has(app));\n\n\t\t\tctx.logger.info(\n\t\t\t\t`\\n🚀 Deploying \"${ctx.workspace.name}\" to the \"${ctx.stage}\" stage with SST`,\n\t\t\t);\n\t\t\tctx.logger.info(\n\t\t\t\t` AWS: ${'profile' in aws ? `profile \"${aws.profile}\"` : 'access keys'}`,\n\t\t\t);\n\t\t\t// `sst deploy` takes the whole stack: there is no deploying one app.\n\t\t\tconst deployable = Object.values(ctx.workspace.apps).filter(\n\t\t\t\t(app) => app.type !== 'mobile',\n\t\t\t).length;\n\t\t\tif (apps.length < deployable) {\n\t\t\t\tctx.logger.warn(\n\t\t\t\t\t` sst deploy deploys every construct of the stage, not only ${apps.join(', ')}.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tsandbox: activeSandbox() ?? new LocalSandbox({ root: ctx.cwd }),\n\t\t\t\tpm: detectPackageManager(ctx.cwd),\n\t\t\t\taws,\n\t\t\t\tapps,\n\t\t\t\turls: {},\n\t\t\t};\n\t\t},\n\n\t\tasync plan(ctx, run) {\n\t\t\tconst kinds = kindsOf(ctx.manifest);\n\t\t\tfor (const app of run.apps) {\n\t\t\t\tctx.emit({\n\t\t\t\t\ttype: 'resource.planned',\n\t\t\t\t\tkey: `application:${app}`,\n\t\t\t\t\tresourceType: kinds.get(app) ?? 'application',\n\t\t\t\t\taction: 'ensure',\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst [build, buildArgs] = buildCommand(ctx, run);\n\t\t\tconst [deploy, deployArgs] = deployCommand(ctx, run);\n\t\t\tctx.logger.info(` Would run: ${[build, ...buildArgs].join(' ')}`);\n\t\t\tctx.logger.info(` Would run: ${[deploy, ...deployArgs].join(' ')}`);\n\t\t},\n\n\t\tasync build(ctx, run) {\n\t\t\tctx.logger.info('\\n📦 Building the manifest SST deploys');\n\t\t\t// No credential: the build runs the project's code and needs none.\n\t\t\tawait exec(ctx, run, buildCommand(ctx, run), {\n\t\t\t\tenv: { ...run.sandbox.env },\n\t\t\t\ttimeoutMs: BUILD_TIMEOUT_MS,\n\t\t\t});\n\t\t},\n\n\t\tasync release(ctx, run) {\n\t\t\tctx.logger.info(`\\n☁️ sst deploy --stage ${ctx.stage}`);\n\t\t\tawait clearOutputs(ctx.cwd);\n\t\t\tawait exec(ctx, run, deployCommand(ctx, run), {\n\t\t\t\tenv: { ...withoutAws(run.sandbox.env), ...awsEnv(run.aws) },\n\t\t\t\ttimeoutMs: DEPLOY_TIMEOUT_MS,\n\t\t\t});\n\n\t\t\trun.urls = surfaceUrls(await readOutputs(ctx.cwd), run.apps);\n\t\t\tfor (const app of run.apps) {\n\t\t\t\tctx.emit({\n\t\t\t\t\ttype: 'app.deployed',\n\t\t\t\t\tapp,\n\t\t\t\t\tapplicationId: ctx.name(app),\n\t\t\t\t\t// SST builds no image a registry holds.\n\t\t\t\t\timageRef: '',\n\t\t\t\t\turl: run.urls[app] ?? '',\n\t\t\t\t});\n\t\t\t}\n\t\t},\n\n\t\tasync verify(ctx, run) {\n\t\t\tconst unhealthy: SurfacesUnhealthy['surfaces'][number][] = [];\n\n\t\t\tfor (const app of run.apps) {\n\t\t\t\tconst base = run.urls[app];\n\t\t\t\tif (!base) {\n\t\t\t\t\tctx.logger.warn(\n\t\t\t\t\t\t` ⚠ No URL for ${app} in SST's outputs, so it was not checked. Return it from run() in sst.config.ts — { ${app}: <its url> } — to have it checked.`,\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// A surface — a `rest-api` — is the backend app it derives.\n\t\t\t\tconst backend = ctx.workspace.apps[app]?.type === 'backend';\n\t\t\t\tconst url = healthUrl(base, backend ? '/health' : '/');\n\t\t\t\tconst answer = await checkHealth(app, url, {\n\t\t\t\t\t...health,\n\t\t\t\t\tsignal: ctx.signal,\n\t\t\t\t\temit: ctx.emit,\n\t\t\t\t});\n\t\t\t\tif (answer.healthy) {\n\t\t\t\t\tctx.logger.info(` ✓ ${app}: ${url}`);\n\t\t\t\t} else {\n\t\t\t\t\tunhealthy.push({\n\t\t\t\t\t\tapp,\n\t\t\t\t\t\turl,\n\t\t\t\t\t\t...(answer.status !== undefined ? { status: answer.status } : {}),\n\t\t\t\t\t\t...(answer.error ? { error: answer.error } : {}),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (unhealthy.length > 0) {\n\t\t\t\tthrow new SurfacesUnhealthy(ctx.stage, unhealthy);\n\t\t\t}\n\t\t},\n\n\t\tresult(ctx, run): DeployResult {\n\t\t\treturn {\n\t\t\t\tapps: run.apps.map((appName) => ({\n\t\t\t\t\tappName,\n\t\t\t\t\ttype: ctx.workspace.apps[appName]?.type ?? 'backend',\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\t...(run.urls[appName] ? { url: run.urls[appName] } : {}),\n\t\t\t\t})),\n\t\t\t\tprojectId: '',\n\t\t\t\tenvironmentId: '',\n\t\t\t\tsuccessCount: ctx.dryRun ? 0 : run.apps.length,\n\t\t\t\tfailedCount: 0,\n\t\t\t\tstage: ctx.stage,\n\t\t\t\tidentity: ctx.identity.key,\n\t\t\t\ttag: ctx.tag,\n\t\t\t\tdryRun: ctx.dryRun,\n\t\t\t\tskipped: [...ctx.skipped],\n\t\t\t\turls: { ...run.urls },\n\t\t\t\tchanges: [],\n\t\t\t};\n\t\t},\n\t});\n}\n\nfunction buildCommand(\n\tctx: DeployPhaseContext<unknown>,\n\trun: SstRun,\n): [string, string[]] {\n\treturn projectCommand(run.pm, 'gkm', [\n\t\t'build',\n\t\t'--provider',\n\t\t'aws',\n\t\t'--stage',\n\t\tctx.stage,\n\t]);\n}\n\nfunction deployCommand(\n\tctx: DeployPhaseContext<unknown>,\n\trun: SstRun,\n): [string, string[]] {\n\treturn projectCommand(run.pm, 'sst', ['deploy', '--stage', ctx.stage]);\n}\n\nexport const sstTarget = createSstTarget();\n"],"mappings":"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"}
@@ -2,7 +2,7 @@ import { t as __exportAll } from "./rolldown-runtime-BhDjJH2R.mjs";
2
2
  import { t as GkmError } from "./errors-D8Gwp5kU.mjs";
3
3
  import { i as CommandFailed, t as MissingCredential } from "./credentials-DpfnUvIR.mjs";
4
4
  import { t as defineTarget } from "./define-6poNqm5O.mjs";
5
- import { t as appKey } from "./derive-B4vDpzgP.mjs";
5
+ import { t as appKey } from "./derive-Dew0QoGe.mjs";
6
6
  import { i as activeSandbox } from "./sandbox-CbVB7uOD.mjs";
7
7
  import { t as LocalSandbox } from "./local-B7Zv1NoA.mjs";
8
8
  import { n as detectPackageManager } from "./utils-D_fxUqvU.mjs";
@@ -437,4 +437,4 @@ const sstTarget = createSstTarget();
437
437
  //#endregion
438
438
  export { SurfacesUnhealthy as i, sst_exports as n, SstOutputsUnreadable as r, SstConfigNotFound as t };
439
439
 
440
- //# sourceMappingURL=sst-CTqZZl1_.mjs.map
440
+ //# sourceMappingURL=sst-fop6uZ25.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"sst-CTqZZl1_.mjs","names":[],"sources":["../src/target/sst/health.ts","../src/target/sst/outputs.ts","../src/target/sst/index.ts"],"sourcesContent":["/**\n * Whether a released surface answers.\n *\n * The paths are the ones the local stack's healthchecks use\n * (`reconcile/apps.ts`): an API's `/health`, a site's `/`. A surface is\n * asked a few times, because a fresh deploy can take a moment to answer — a\n * Lambda's first start, a distribution still settling — and healthy means a\n * 2xx, after redirects.\n */\n\nimport { GkmError } from '../../errors';\nimport type { TargetEvent } from '../types';\n\nexport interface HealthCheckOptions {\n\t/** How many times a surface is asked before it counts as down. */\n\tattempts: number;\n\t/** The wait between two attempts. */\n\tdelayMs: number;\n\t/** How long one request may take. */\n\ttimeoutMs: number;\n\tsignal: AbortSignal;\n\temit: (event: TargetEvent) => void;\n}\n\n/** Surfaces that never answered healthy. */\nexport class SurfacesUnhealthy extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\t/** Each surface that failed, with the last thing it answered. */\n\t\treadonly surfaces: readonly {\n\t\t\tapp: string;\n\t\t\turl: string;\n\t\t\tstatus?: number;\n\t\t\terror?: string;\n\t\t}[],\n\t) {\n\t\tconst lines = surfaces.map(\n\t\t\t({ app, url, status, error }) =>\n\t\t\t\t` - ${app}: ${url} → ${status ?? error ?? 'no answer'}`,\n\t\t);\n\t\tsuper(\n\t\t\t`The \"${stage}\" stage was deployed, but ${surfaces.length === 1 ? 'a surface does' : 'surfaces do'} not answer healthy:\\n${lines.join('\\n')}\\nCheck its logs in CloudWatch, fix it, and deploy again.`,\n\t\t);\n\t\tthis.name = 'SurfacesUnhealthy';\n\t}\n}\n\n/** `path` under `base`, keeping any path `base` already has. */\nexport function healthUrl(base: string, path: string): string {\n\tconst root = base.endsWith('/') ? base : `${base}/`;\n\treturn new URL(path.replace(/^\\//, ''), root).toString();\n}\n\n/**\n * Ask `url` until it answers 2xx or the attempts run out, emitting\n * `health.checked` for each. Resolves with the last answer.\n */\nexport async function checkHealth(\n\tapp: string,\n\turl: string,\n\toptions: HealthCheckOptions,\n): Promise<{ healthy: boolean; status?: number; error?: string }> {\n\tlet last: { healthy: boolean; status?: number; error?: string } = {\n\t\thealthy: false,\n\t};\n\tfor (let attempt = 1; attempt <= options.attempts; attempt++) {\n\t\toptions.signal.throwIfAborted();\n\t\ttry {\n\t\t\tconst response = await fetch(url, {\n\t\t\t\tredirect: 'follow',\n\t\t\t\tsignal: AbortSignal.any([\n\t\t\t\t\toptions.signal,\n\t\t\t\t\tAbortSignal.timeout(options.timeoutMs),\n\t\t\t\t]),\n\t\t\t});\n\t\t\t// Drained, so the connection is not held open by an unread body.\n\t\t\tawait response.arrayBuffer().catch(() => undefined);\n\t\t\tlast = { healthy: response.ok, status: response.status };\n\t\t} catch (error) {\n\t\t\t// The run being stopped is not the surface being down.\n\t\t\toptions.signal.throwIfAborted();\n\t\t\tlast = {\n\t\t\t\thealthy: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t};\n\t\t}\n\t\toptions.emit({\n\t\t\ttype: 'health.checked',\n\t\t\tapp,\n\t\t\turl,\n\t\t\thealthy: last.healthy,\n\t\t\t...(last.status !== undefined ? { status: last.status } : {}),\n\t\t\tattempt,\n\t\t});\n\t\tif (last.healthy) return last;\n\t\tif (attempt < options.attempts) await wait(options.delayMs, options.signal);\n\t}\n\treturn last;\n}\n\nfunction wait(ms: number, signal: AbortSignal): Promise<void> {\n\tif (ms <= 0) return Promise.resolve();\n\treturn new Promise((resolve, reject) => {\n\t\tconst timer = setTimeout(() => {\n\t\t\tsignal.removeEventListener('abort', stop);\n\t\t\tresolve();\n\t\t}, ms);\n\t\tconst stop = () => {\n\t\t\tclearTimeout(timer);\n\t\t\treject(signal.reason);\n\t\t};\n\t\tsignal.addEventListener('abort', stop, { once: true });\n\t});\n}\n","/**\n * The surface URLs an `sst deploy` reports.\n *\n * SST writes whatever `run()` in `sst.config.ts` returns to\n * `.sst/outputs.json` after every successful deploy — `{\"Api\": \"https://…\"}`.\n * What `run()` returns is the project's to write, so this reads it leniently:\n * nested objects are walked, anything that is not an http(s) URL is ignored,\n * and an output names a surface by its id in any casing, with or without a\n * `Url` suffix (`Api`, `api`, `ApiUrl`, `API_URL`, `Api.url`).\n */\n\nimport { existsSync } from 'node:fs';\nimport { readFile, rm } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { kebabCase } from '@geekmidas/manifest';\nimport { GkmError } from '../../errors';\n\n/** Where SST leaves the last successful deploy's outputs, under the project. */\nexport const SST_OUTPUTS_FILE = join('.sst', 'outputs.json');\n\n/** `.sst/outputs.json` is there, and is not JSON. */\nexport class SstOutputsUnreadable extends GkmError {\n\tconstructor(\n\t\treadonly path: string,\n\t\treadonly reason: string,\n\t) {\n\t\tsuper(\n\t\t\t`Could not read SST's outputs at ${path}: ${reason}. It is rewritten by every successful \\`sst deploy\\`; delete it and deploy again.`,\n\t\t);\n\t\tthis.name = 'SstOutputsUnreadable';\n\t}\n}\n\n/**\n * Remove what an earlier deploy wrote, so the outputs read after this one\n * are this one's — not another stage's, deployed from the same checkout.\n */\nexport async function clearOutputs(cwd: string): Promise<void> {\n\tawait rm(join(cwd, SST_OUTPUTS_FILE), { force: true });\n}\n\n/** The deploy's outputs, or `{}` when it wrote none. */\nexport async function readOutputs(cwd: string): Promise<unknown> {\n\tconst path = join(cwd, SST_OUTPUTS_FILE);\n\tif (!existsSync(path)) return {};\n\ttry {\n\t\treturn JSON.parse(await readFile(path, 'utf8'));\n\t} catch (error) {\n\t\tthrow new SstOutputsUnreadable(\n\t\t\tpath,\n\t\t\terror instanceof Error ? error.message : String(error),\n\t\t);\n\t}\n}\n\n/** Every http(s) URL in `outputs`, by its key path: `['Api', 'url']`. */\nfunction urlsIn(\n\tvalue: unknown,\n\tpath: string[] = [],\n\tfound: { path: string[]; url: string }[] = [],\n): { path: string[]; url: string }[] {\n\tif (typeof value === 'string') {\n\t\tif (isHttpUrl(value)) found.push({ path, url: value });\n\t} else if (value && typeof value === 'object' && !Array.isArray(value)) {\n\t\tfor (const [key, child] of Object.entries(value)) {\n\t\t\turlsIn(child, [...path, key], found);\n\t\t}\n\t}\n\treturn found;\n}\n\nfunction isHttpUrl(value: string): boolean {\n\ttry {\n\t\tconst { protocol } = new URL(value);\n\t\treturn protocol === 'http:' || protocol === 'https:';\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * The app an output's key path names, kebab-cased as app keys are: its last\n * segment, without a `url` suffix — or its parent's, when the last segment\n * is only `url`. Exact names are tried first, so an app called `api-url`\n * still finds `ApiUrl`.\n */\nfunction namesOf(path: string[]): string[] {\n\tconst names: string[] = [];\n\tfor (const segment of [...path].reverse()) {\n\t\tconst name = kebabCase(segment);\n\t\tnames.push(name);\n\t\tconst bare = name.replace(/-?url$/, '');\n\t\tif (bare) {\n\t\t\tnames.push(bare);\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn names;\n}\n\n/**\n * Each app's URL from the outputs, for the apps given. An app no output\n * names is left out.\n */\nexport function surfaceUrls(\n\toutputs: unknown,\n\tapps: readonly string[],\n): Record<string, string> {\n\tconst found = urlsIn(outputs);\n\tconst urls: Record<string, string> = {};\n\tfor (const app of apps) {\n\t\t// An exact name beats a stripped one, wherever each is in the file.\n\t\tconst exact = found.find(({ path }) => namesOf(path)[0] === app);\n\t\tconst loose = found.find(({ path }) => namesOf(path).includes(app));\n\t\tconst match = exact ?? loose;\n\t\tif (match) urls[app] = match.url;\n\t}\n\treturn urls;\n}\n","/**\n * The built-in `sst` target: `gkm build` writes the manifest, `sst deploy`\n * provisions it, and the surfaces SST reports are health-checked.\n *\n * `@geekmidas/cloud` is unchanged by it: `sst.config.ts` still reads\n * `.gkm/manifest/aws.ts` and `fromManifest` still makes every resource. What\n * the target adds is the run around the two commands — the lock, the events,\n * verification, and credentials kept where they belong.\n *\n * Both commands run in the sandbox: the build executes the project's code,\n * and so does `sst deploy`, which evaluates `sst.config.ts`. Only the second\n * needs AWS, so the AWS credential goes into that one command's environment\n * and nowhere else — not the build's, not the process the deploy runs in.\n * What `sst deploy` needs beyond them, the sandbox already passes: `PATH`\n * for node and the package manager, and `HOME`, under which SST keeps its\n * toolchain (`~/.config/sst`: bun, pulumi and its plugins) and the AWS SDK\n * finds `~/.aws` for a profile. Its state — the Pulumi backend and the\n * passphrase — is in the stage's own account, reached with that credential.\n */\n\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { ConstructManifest } from '@geekmidas/manifest';\nimport {\n\ttype AwsCredential,\n\tMissingCredential,\n} from '../../deploy/credentials';\nimport type { DeployResult } from '../../deploy/types';\nimport { GkmError } from '../../errors';\nimport { detectPackageManager, type PackageManager } from '../../init/utils';\nimport { CommandFailed } from '../../run';\nimport { LocalSandbox } from '../../sandbox/local';\nimport { activeSandbox, type Sandbox } from '../../sandbox/sandbox';\nimport { appKey } from '../../workspace/derive';\nimport { defineTarget } from '../define';\nimport type { DeployPhaseContext, DeployTarget } from '../types';\nimport { checkHealth, healthUrl, SurfacesUnhealthy } from './health';\nimport { clearOutputs, readOutputs, surfaceUrls } from './outputs';\n\nexport { SurfacesUnhealthy } from './health';\nexport { SST_OUTPUTS_FILE, SstOutputsUnreadable } from './outputs';\n\n/** A cold build of every handler and site. */\nconst BUILD_TIMEOUT_MS = 30 * 60_000;\n/**\n * A first deploy creates CloudFront distributions and, with a database, a\n * VPC and an RDS instance — each of which alone can take a quarter of an hour.\n */\nconst DEPLOY_TIMEOUT_MS = 90 * 60_000;\n\n/** The workspace has no `sst.config.ts` for `sst deploy` to run. */\nexport class SstConfigNotFound extends GkmError {\n\tconstructor(readonly cwd: string) {\n\t\tsuper(\n\t\t\t`No sst.config.ts in ${cwd}. The sst target runs \\`sst deploy\\` at the workspace root: scaffold one with \\`gkm init --deploy sst\\` and copy its sst.config.ts, or deploy through another target with --target.`,\n\t\t);\n\t\tthis.name = 'SstConfigNotFound';\n\t}\n}\n\n/** What the phases hand on. */\nexport interface SstRun {\n\tsandbox: Sandbox;\n\tpm: PackageManager;\n\taws: AwsCredential;\n\t/** The apps SST deploys this run: `ctx.apps` without the skipped. */\n\tapps: string[];\n\t/** Each app's URL, from the deploy's outputs. */\n\turls: Record<string, string>;\n}\n\nexport interface SstTargetOptions {\n\t/** How often, and how patiently, `verify` asks each surface. */\n\thealth?: { attempts?: number; delayMs?: number; timeoutMs?: number };\n}\n\n/**\n * The project's own copy of `bin`, through its package manager — never one\n * `npx` would download, which would be code nobody installed.\n */\nexport function projectCommand(\n\tpm: PackageManager,\n\tbin: string,\n\targs: readonly string[],\n): [command: string, args: string[]] {\n\tswitch (pm) {\n\t\tcase 'pnpm':\n\t\t\treturn ['pnpm', ['exec', bin, ...args]];\n\t\tcase 'yarn':\n\t\t\treturn ['yarn', [bin, ...args]];\n\t\tcase 'bun':\n\t\t\treturn ['bunx', [bin, ...args]];\n\t\tcase 'npm':\n\t\t\treturn ['npx', ['--no-install', bin, ...args]];\n\t}\n}\n\n/**\n * The variables `credential` is to the AWS SDK and to SST.\n *\n * A profile is passed alone: given keys too, every SDK would take the keys.\n */\nexport function awsEnv(credential: AwsCredential): Record<string, string> {\n\tconst region: Record<string, string> = credential.region\n\t\t? { AWS_REGION: credential.region, AWS_DEFAULT_REGION: credential.region }\n\t\t: {};\n\tif ('profile' in credential) {\n\t\treturn { AWS_PROFILE: credential.profile, ...region };\n\t}\n\treturn {\n\t\tAWS_ACCESS_KEY_ID: credential.accessKeyId,\n\t\tAWS_SECRET_ACCESS_KEY: credential.secretAccessKey,\n\t\t...(credential.sessionToken\n\t\t\t? { AWS_SESSION_TOKEN: credential.sessionToken }\n\t\t\t: {}),\n\t\t...region,\n\t};\n}\n\n/**\n * `env` without any `AWS_*`, so a host sandbox that passes some cannot put\n * a second account beside the one the credential names.\n */\nfunction withoutAws(\n\tenv: Readonly<Record<string, string>>,\n): Record<string, string> {\n\treturn Object.fromEntries(\n\t\tObject.entries(env).filter(([name]) => !name.startsWith('AWS_')),\n\t);\n}\n\n/** Each app's kind in the manifest, by app key: `rest-api` or `site`. */\nfunction kindsOf(manifest: ConstructManifest): Map<string, string> {\n\treturn new Map(\n\t\tObject.entries(manifest).map(([id, declaration]) => [\n\t\t\tappKey(id),\n\t\t\tdeclaration.kind,\n\t\t]),\n\t);\n}\n\nasync function exec(\n\tctx: DeployPhaseContext<unknown>,\n\trun: SstRun,\n\t[command, args]: [string, string[]],\n\toptions: { env: Record<string, string>; timeoutMs: number },\n): Promise<void> {\n\tconst result = await run.sandbox.exec(command, args, {\n\t\tcwd: ctx.cwd,\n\t\tenv: options.env,\n\t\ttimeoutMs: options.timeoutMs,\n\t\toutput: ctx.childOutput,\n\t\tsignal: ctx.signal,\n\t});\n\tif (result.exitCode !== 0) {\n\t\tthrow new CommandFailed(\n\t\t\tcommand,\n\t\t\targs,\n\t\t\tresult.exitCode,\n\t\t\tresult.signal as NodeJS.Signals | null,\n\t\t);\n\t}\n}\n\n/** The `sst` target, with its health checks tuned — for tests and hosts. */\nexport function createSstTarget(\n\toptions: SstTargetOptions = {},\n): DeployTarget<undefined, SstRun> {\n\tconst health = {\n\t\tattempts: options.health?.attempts ?? 6,\n\t\tdelayMs: options.health?.delayMs ?? 5_000,\n\t\ttimeoutMs: options.health?.timeoutMs ?? 10_000,\n\t};\n\n\treturn defineTarget<undefined, SstRun>({\n\t\tname: 'sst',\n\t\truntime: 'aws',\n\t\t// SST keeps no previous release to return to; migrations run in the\n\t\t// stack, as `fromManifest` provisions the database; and nothing here\n\t\t// builds an image.\n\t\tcapabilities: { rollback: false, migrations: 'target', images: false },\n\t\tcredentials: ['aws'],\n\n\t\tasync validate(ctx) {\n\t\t\tif (!existsSync(join(ctx.cwd, 'sst.config.ts'))) {\n\t\t\t\tthrow new SstConfigNotFound(ctx.cwd);\n\t\t\t}\n\n\t\t\tconst aws = await ctx.credentials.get(\n\t\t\t\t{ kind: 'aws', stage: ctx.stage },\n\t\t\t\t{ signal: ctx.signal },\n\t\t\t);\n\t\t\tif (!aws) throw new MissingCredential('aws', ctx.stage);\n\t\t\tif (!('profile' in aws)) {\n\t\t\t\tctx.secrets.mask(aws.secretAccessKey);\n\t\t\t\tif (aws.sessionToken) ctx.secrets.mask(aws.sessionToken);\n\t\t\t}\n\n\t\t\t// A mobile app ships through its own store's toolchain.\n\t\t\tfor (const app of ctx.apps) {\n\t\t\t\tif (ctx.workspace.apps[app]?.type === 'mobile') {\n\t\t\t\t\tctx.skip(app, 'mobile apps deploy through their own toolchain');\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst skipped = new Set(ctx.skipped.map(({ app }) => app));\n\t\t\tconst apps = ctx.apps.filter((app) => !skipped.has(app));\n\n\t\t\tctx.logger.info(\n\t\t\t\t`\\n🚀 Deploying \"${ctx.workspace.name}\" to the \"${ctx.stage}\" stage with SST`,\n\t\t\t);\n\t\t\tctx.logger.info(\n\t\t\t\t` AWS: ${'profile' in aws ? `profile \"${aws.profile}\"` : 'access keys'}`,\n\t\t\t);\n\t\t\t// `sst deploy` takes the whole stack: there is no deploying one app.\n\t\t\tconst deployable = Object.values(ctx.workspace.apps).filter(\n\t\t\t\t(app) => app.type !== 'mobile',\n\t\t\t).length;\n\t\t\tif (apps.length < deployable) {\n\t\t\t\tctx.logger.warn(\n\t\t\t\t\t` sst deploy deploys every construct of the stage, not only ${apps.join(', ')}.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tsandbox: activeSandbox() ?? new LocalSandbox({ root: ctx.cwd }),\n\t\t\t\tpm: detectPackageManager(ctx.cwd),\n\t\t\t\taws,\n\t\t\t\tapps,\n\t\t\t\turls: {},\n\t\t\t};\n\t\t},\n\n\t\tasync plan(ctx, run) {\n\t\t\tconst kinds = kindsOf(ctx.manifest);\n\t\t\tfor (const app of run.apps) {\n\t\t\t\tctx.emit({\n\t\t\t\t\ttype: 'resource.planned',\n\t\t\t\t\tkey: `application:${app}`,\n\t\t\t\t\tresourceType: kinds.get(app) ?? 'application',\n\t\t\t\t\taction: 'ensure',\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst [build, buildArgs] = buildCommand(ctx, run);\n\t\t\tconst [deploy, deployArgs] = deployCommand(ctx, run);\n\t\t\tctx.logger.info(` Would run: ${[build, ...buildArgs].join(' ')}`);\n\t\t\tctx.logger.info(` Would run: ${[deploy, ...deployArgs].join(' ')}`);\n\t\t},\n\n\t\tasync build(ctx, run) {\n\t\t\tctx.logger.info('\\n📦 Building the manifest SST deploys');\n\t\t\t// No credential: the build runs the project's code and needs none.\n\t\t\tawait exec(ctx, run, buildCommand(ctx, run), {\n\t\t\t\tenv: { ...run.sandbox.env },\n\t\t\t\ttimeoutMs: BUILD_TIMEOUT_MS,\n\t\t\t});\n\t\t},\n\n\t\tasync release(ctx, run) {\n\t\t\tctx.logger.info(`\\n☁️ sst deploy --stage ${ctx.stage}`);\n\t\t\tawait clearOutputs(ctx.cwd);\n\t\t\tawait exec(ctx, run, deployCommand(ctx, run), {\n\t\t\t\tenv: { ...withoutAws(run.sandbox.env), ...awsEnv(run.aws) },\n\t\t\t\ttimeoutMs: DEPLOY_TIMEOUT_MS,\n\t\t\t});\n\n\t\t\trun.urls = surfaceUrls(await readOutputs(ctx.cwd), run.apps);\n\t\t\tfor (const app of run.apps) {\n\t\t\t\tctx.emit({\n\t\t\t\t\ttype: 'app.deployed',\n\t\t\t\t\tapp,\n\t\t\t\t\tapplicationId: ctx.name(app),\n\t\t\t\t\t// SST builds no image a registry holds.\n\t\t\t\t\timageRef: '',\n\t\t\t\t\turl: run.urls[app] ?? '',\n\t\t\t\t});\n\t\t\t}\n\t\t},\n\n\t\tasync verify(ctx, run) {\n\t\t\tconst unhealthy: SurfacesUnhealthy['surfaces'][number][] = [];\n\n\t\t\tfor (const app of run.apps) {\n\t\t\t\tconst base = run.urls[app];\n\t\t\t\tif (!base) {\n\t\t\t\t\tctx.logger.warn(\n\t\t\t\t\t\t` ⚠ No URL for ${app} in SST's outputs, so it was not checked. Return it from run() in sst.config.ts — { ${app}: <its url> } — to have it checked.`,\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// A surface — a `rest-api` — is the backend app it derives.\n\t\t\t\tconst backend = ctx.workspace.apps[app]?.type === 'backend';\n\t\t\t\tconst url = healthUrl(base, backend ? '/health' : '/');\n\t\t\t\tconst answer = await checkHealth(app, url, {\n\t\t\t\t\t...health,\n\t\t\t\t\tsignal: ctx.signal,\n\t\t\t\t\temit: ctx.emit,\n\t\t\t\t});\n\t\t\t\tif (answer.healthy) {\n\t\t\t\t\tctx.logger.info(` ✓ ${app}: ${url}`);\n\t\t\t\t} else {\n\t\t\t\t\tunhealthy.push({\n\t\t\t\t\t\tapp,\n\t\t\t\t\t\turl,\n\t\t\t\t\t\t...(answer.status !== undefined ? { status: answer.status } : {}),\n\t\t\t\t\t\t...(answer.error ? { error: answer.error } : {}),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (unhealthy.length > 0) {\n\t\t\t\tthrow new SurfacesUnhealthy(ctx.stage, unhealthy);\n\t\t\t}\n\t\t},\n\n\t\tresult(ctx, run): DeployResult {\n\t\t\treturn {\n\t\t\t\tapps: run.apps.map((appName) => ({\n\t\t\t\t\tappName,\n\t\t\t\t\ttype: ctx.workspace.apps[appName]?.type ?? 'backend',\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\t...(run.urls[appName] ? { url: run.urls[appName] } : {}),\n\t\t\t\t})),\n\t\t\t\tprojectId: '',\n\t\t\t\tenvironmentId: '',\n\t\t\t\tsuccessCount: ctx.dryRun ? 0 : run.apps.length,\n\t\t\t\tfailedCount: 0,\n\t\t\t\tstage: ctx.stage,\n\t\t\t\tidentity: ctx.identity.key,\n\t\t\t\ttag: ctx.tag,\n\t\t\t\tdryRun: ctx.dryRun,\n\t\t\t\tskipped: [...ctx.skipped],\n\t\t\t\turls: { ...run.urls },\n\t\t\t\tchanges: [],\n\t\t\t};\n\t\t},\n\t});\n}\n\nfunction buildCommand(\n\tctx: DeployPhaseContext<unknown>,\n\trun: SstRun,\n): [string, string[]] {\n\treturn projectCommand(run.pm, 'gkm', [\n\t\t'build',\n\t\t'--provider',\n\t\t'aws',\n\t\t'--stage',\n\t\tctx.stage,\n\t]);\n}\n\nfunction deployCommand(\n\tctx: DeployPhaseContext<unknown>,\n\trun: SstRun,\n): [string, string[]] {\n\treturn projectCommand(run.pm, 'sst', ['deploy', '--stage', ctx.stage]);\n}\n\nexport const sstTarget = createSstTarget();\n"],"mappings":"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"}
1
+ {"version":3,"file":"sst-fop6uZ25.mjs","names":[],"sources":["../src/target/sst/health.ts","../src/target/sst/outputs.ts","../src/target/sst/index.ts"],"sourcesContent":["/**\n * Whether a released surface answers.\n *\n * The paths are the ones the local stack's healthchecks use\n * (`reconcile/apps.ts`): an API's `/health`, a site's `/`. A surface is\n * asked a few times, because a fresh deploy can take a moment to answer — a\n * Lambda's first start, a distribution still settling — and healthy means a\n * 2xx, after redirects.\n */\n\nimport { GkmError } from '../../errors';\nimport type { TargetEvent } from '../types';\n\nexport interface HealthCheckOptions {\n\t/** How many times a surface is asked before it counts as down. */\n\tattempts: number;\n\t/** The wait between two attempts. */\n\tdelayMs: number;\n\t/** How long one request may take. */\n\ttimeoutMs: number;\n\tsignal: AbortSignal;\n\temit: (event: TargetEvent) => void;\n}\n\n/** Surfaces that never answered healthy. */\nexport class SurfacesUnhealthy extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\t/** Each surface that failed, with the last thing it answered. */\n\t\treadonly surfaces: readonly {\n\t\t\tapp: string;\n\t\t\turl: string;\n\t\t\tstatus?: number;\n\t\t\terror?: string;\n\t\t}[],\n\t) {\n\t\tconst lines = surfaces.map(\n\t\t\t({ app, url, status, error }) =>\n\t\t\t\t` - ${app}: ${url} → ${status ?? error ?? 'no answer'}`,\n\t\t);\n\t\tsuper(\n\t\t\t`The \"${stage}\" stage was deployed, but ${surfaces.length === 1 ? 'a surface does' : 'surfaces do'} not answer healthy:\\n${lines.join('\\n')}\\nCheck its logs in CloudWatch, fix it, and deploy again.`,\n\t\t);\n\t\tthis.name = 'SurfacesUnhealthy';\n\t}\n}\n\n/** `path` under `base`, keeping any path `base` already has. */\nexport function healthUrl(base: string, path: string): string {\n\tconst root = base.endsWith('/') ? base : `${base}/`;\n\treturn new URL(path.replace(/^\\//, ''), root).toString();\n}\n\n/**\n * Ask `url` until it answers 2xx or the attempts run out, emitting\n * `health.checked` for each. Resolves with the last answer.\n */\nexport async function checkHealth(\n\tapp: string,\n\turl: string,\n\toptions: HealthCheckOptions,\n): Promise<{ healthy: boolean; status?: number; error?: string }> {\n\tlet last: { healthy: boolean; status?: number; error?: string } = {\n\t\thealthy: false,\n\t};\n\tfor (let attempt = 1; attempt <= options.attempts; attempt++) {\n\t\toptions.signal.throwIfAborted();\n\t\ttry {\n\t\t\tconst response = await fetch(url, {\n\t\t\t\tredirect: 'follow',\n\t\t\t\tsignal: AbortSignal.any([\n\t\t\t\t\toptions.signal,\n\t\t\t\t\tAbortSignal.timeout(options.timeoutMs),\n\t\t\t\t]),\n\t\t\t});\n\t\t\t// Drained, so the connection is not held open by an unread body.\n\t\t\tawait response.arrayBuffer().catch(() => undefined);\n\t\t\tlast = { healthy: response.ok, status: response.status };\n\t\t} catch (error) {\n\t\t\t// The run being stopped is not the surface being down.\n\t\t\toptions.signal.throwIfAborted();\n\t\t\tlast = {\n\t\t\t\thealthy: false,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t};\n\t\t}\n\t\toptions.emit({\n\t\t\ttype: 'health.checked',\n\t\t\tapp,\n\t\t\turl,\n\t\t\thealthy: last.healthy,\n\t\t\t...(last.status !== undefined ? { status: last.status } : {}),\n\t\t\tattempt,\n\t\t});\n\t\tif (last.healthy) return last;\n\t\tif (attempt < options.attempts) await wait(options.delayMs, options.signal);\n\t}\n\treturn last;\n}\n\nfunction wait(ms: number, signal: AbortSignal): Promise<void> {\n\tif (ms <= 0) return Promise.resolve();\n\treturn new Promise((resolve, reject) => {\n\t\tconst timer = setTimeout(() => {\n\t\t\tsignal.removeEventListener('abort', stop);\n\t\t\tresolve();\n\t\t}, ms);\n\t\tconst stop = () => {\n\t\t\tclearTimeout(timer);\n\t\t\treject(signal.reason);\n\t\t};\n\t\tsignal.addEventListener('abort', stop, { once: true });\n\t});\n}\n","/**\n * The surface URLs an `sst deploy` reports.\n *\n * SST writes whatever `run()` in `sst.config.ts` returns to\n * `.sst/outputs.json` after every successful deploy — `{\"Api\": \"https://…\"}`.\n * What `run()` returns is the project's to write, so this reads it leniently:\n * nested objects are walked, anything that is not an http(s) URL is ignored,\n * and an output names a surface by its id in any casing, with or without a\n * `Url` suffix (`Api`, `api`, `ApiUrl`, `API_URL`, `Api.url`).\n */\n\nimport { existsSync } from 'node:fs';\nimport { readFile, rm } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { kebabCase } from '@geekmidas/manifest';\nimport { GkmError } from '../../errors';\n\n/** Where SST leaves the last successful deploy's outputs, under the project. */\nexport const SST_OUTPUTS_FILE = join('.sst', 'outputs.json');\n\n/** `.sst/outputs.json` is there, and is not JSON. */\nexport class SstOutputsUnreadable extends GkmError {\n\tconstructor(\n\t\treadonly path: string,\n\t\treadonly reason: string,\n\t) {\n\t\tsuper(\n\t\t\t`Could not read SST's outputs at ${path}: ${reason}. It is rewritten by every successful \\`sst deploy\\`; delete it and deploy again.`,\n\t\t);\n\t\tthis.name = 'SstOutputsUnreadable';\n\t}\n}\n\n/**\n * Remove what an earlier deploy wrote, so the outputs read after this one\n * are this one's — not another stage's, deployed from the same checkout.\n */\nexport async function clearOutputs(cwd: string): Promise<void> {\n\tawait rm(join(cwd, SST_OUTPUTS_FILE), { force: true });\n}\n\n/** The deploy's outputs, or `{}` when it wrote none. */\nexport async function readOutputs(cwd: string): Promise<unknown> {\n\tconst path = join(cwd, SST_OUTPUTS_FILE);\n\tif (!existsSync(path)) return {};\n\ttry {\n\t\treturn JSON.parse(await readFile(path, 'utf8'));\n\t} catch (error) {\n\t\tthrow new SstOutputsUnreadable(\n\t\t\tpath,\n\t\t\terror instanceof Error ? error.message : String(error),\n\t\t);\n\t}\n}\n\n/** Every http(s) URL in `outputs`, by its key path: `['Api', 'url']`. */\nfunction urlsIn(\n\tvalue: unknown,\n\tpath: string[] = [],\n\tfound: { path: string[]; url: string }[] = [],\n): { path: string[]; url: string }[] {\n\tif (typeof value === 'string') {\n\t\tif (isHttpUrl(value)) found.push({ path, url: value });\n\t} else if (value && typeof value === 'object' && !Array.isArray(value)) {\n\t\tfor (const [key, child] of Object.entries(value)) {\n\t\t\turlsIn(child, [...path, key], found);\n\t\t}\n\t}\n\treturn found;\n}\n\nfunction isHttpUrl(value: string): boolean {\n\ttry {\n\t\tconst { protocol } = new URL(value);\n\t\treturn protocol === 'http:' || protocol === 'https:';\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * The app an output's key path names, kebab-cased as app keys are: its last\n * segment, without a `url` suffix — or its parent's, when the last segment\n * is only `url`. Exact names are tried first, so an app called `api-url`\n * still finds `ApiUrl`.\n */\nfunction namesOf(path: string[]): string[] {\n\tconst names: string[] = [];\n\tfor (const segment of [...path].reverse()) {\n\t\tconst name = kebabCase(segment);\n\t\tnames.push(name);\n\t\tconst bare = name.replace(/-?url$/, '');\n\t\tif (bare) {\n\t\t\tnames.push(bare);\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn names;\n}\n\n/**\n * Each app's URL from the outputs, for the apps given. An app no output\n * names is left out.\n */\nexport function surfaceUrls(\n\toutputs: unknown,\n\tapps: readonly string[],\n): Record<string, string> {\n\tconst found = urlsIn(outputs);\n\tconst urls: Record<string, string> = {};\n\tfor (const app of apps) {\n\t\t// An exact name beats a stripped one, wherever each is in the file.\n\t\tconst exact = found.find(({ path }) => namesOf(path)[0] === app);\n\t\tconst loose = found.find(({ path }) => namesOf(path).includes(app));\n\t\tconst match = exact ?? loose;\n\t\tif (match) urls[app] = match.url;\n\t}\n\treturn urls;\n}\n","/**\n * The built-in `sst` target: `gkm build` writes the manifest, `sst deploy`\n * provisions it, and the surfaces SST reports are health-checked.\n *\n * `@geekmidas/cloud` is unchanged by it: `sst.config.ts` still reads\n * `.gkm/manifest/aws.ts` and `fromManifest` still makes every resource. What\n * the target adds is the run around the two commands — the lock, the events,\n * verification, and credentials kept where they belong.\n *\n * Both commands run in the sandbox: the build executes the project's code,\n * and so does `sst deploy`, which evaluates `sst.config.ts`. Only the second\n * needs AWS, so the AWS credential goes into that one command's environment\n * and nowhere else — not the build's, not the process the deploy runs in.\n * What `sst deploy` needs beyond them, the sandbox already passes: `PATH`\n * for node and the package manager, and `HOME`, under which SST keeps its\n * toolchain (`~/.config/sst`: bun, pulumi and its plugins) and the AWS SDK\n * finds `~/.aws` for a profile. Its state — the Pulumi backend and the\n * passphrase — is in the stage's own account, reached with that credential.\n */\n\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { ConstructManifest } from '@geekmidas/manifest';\nimport {\n\ttype AwsCredential,\n\tMissingCredential,\n} from '../../deploy/credentials';\nimport type { DeployResult } from '../../deploy/types';\nimport { GkmError } from '../../errors';\nimport { detectPackageManager, type PackageManager } from '../../init/utils';\nimport { CommandFailed } from '../../run';\nimport { LocalSandbox } from '../../sandbox/local';\nimport { activeSandbox, type Sandbox } from '../../sandbox/sandbox';\nimport { appKey } from '../../workspace/derive';\nimport { defineTarget } from '../define';\nimport type { DeployPhaseContext, DeployTarget } from '../types';\nimport { checkHealth, healthUrl, SurfacesUnhealthy } from './health';\nimport { clearOutputs, readOutputs, surfaceUrls } from './outputs';\n\nexport { SurfacesUnhealthy } from './health';\nexport { SST_OUTPUTS_FILE, SstOutputsUnreadable } from './outputs';\n\n/** A cold build of every handler and site. */\nconst BUILD_TIMEOUT_MS = 30 * 60_000;\n/**\n * A first deploy creates CloudFront distributions and, with a database, a\n * VPC and an RDS instance — each of which alone can take a quarter of an hour.\n */\nconst DEPLOY_TIMEOUT_MS = 90 * 60_000;\n\n/** The workspace has no `sst.config.ts` for `sst deploy` to run. */\nexport class SstConfigNotFound extends GkmError {\n\tconstructor(readonly cwd: string) {\n\t\tsuper(\n\t\t\t`No sst.config.ts in ${cwd}. The sst target runs \\`sst deploy\\` at the workspace root: scaffold one with \\`gkm init --deploy sst\\` and copy its sst.config.ts, or deploy through another target with --target.`,\n\t\t);\n\t\tthis.name = 'SstConfigNotFound';\n\t}\n}\n\n/** What the phases hand on. */\nexport interface SstRun {\n\tsandbox: Sandbox;\n\tpm: PackageManager;\n\taws: AwsCredential;\n\t/** The apps SST deploys this run: `ctx.apps` without the skipped. */\n\tapps: string[];\n\t/** Each app's URL, from the deploy's outputs. */\n\turls: Record<string, string>;\n}\n\nexport interface SstTargetOptions {\n\t/** How often, and how patiently, `verify` asks each surface. */\n\thealth?: { attempts?: number; delayMs?: number; timeoutMs?: number };\n}\n\n/**\n * The project's own copy of `bin`, through its package manager — never one\n * `npx` would download, which would be code nobody installed.\n */\nexport function projectCommand(\n\tpm: PackageManager,\n\tbin: string,\n\targs: readonly string[],\n): [command: string, args: string[]] {\n\tswitch (pm) {\n\t\tcase 'pnpm':\n\t\t\treturn ['pnpm', ['exec', bin, ...args]];\n\t\tcase 'yarn':\n\t\t\treturn ['yarn', [bin, ...args]];\n\t\tcase 'bun':\n\t\t\treturn ['bunx', [bin, ...args]];\n\t\tcase 'npm':\n\t\t\treturn ['npx', ['--no-install', bin, ...args]];\n\t}\n}\n\n/**\n * The variables `credential` is to the AWS SDK and to SST.\n *\n * A profile is passed alone: given keys too, every SDK would take the keys.\n */\nexport function awsEnv(credential: AwsCredential): Record<string, string> {\n\tconst region: Record<string, string> = credential.region\n\t\t? { AWS_REGION: credential.region, AWS_DEFAULT_REGION: credential.region }\n\t\t: {};\n\tif ('profile' in credential) {\n\t\treturn { AWS_PROFILE: credential.profile, ...region };\n\t}\n\treturn {\n\t\tAWS_ACCESS_KEY_ID: credential.accessKeyId,\n\t\tAWS_SECRET_ACCESS_KEY: credential.secretAccessKey,\n\t\t...(credential.sessionToken\n\t\t\t? { AWS_SESSION_TOKEN: credential.sessionToken }\n\t\t\t: {}),\n\t\t...region,\n\t};\n}\n\n/**\n * `env` without any `AWS_*`, so a host sandbox that passes some cannot put\n * a second account beside the one the credential names.\n */\nfunction withoutAws(\n\tenv: Readonly<Record<string, string>>,\n): Record<string, string> {\n\treturn Object.fromEntries(\n\t\tObject.entries(env).filter(([name]) => !name.startsWith('AWS_')),\n\t);\n}\n\n/** Each app's kind in the manifest, by app key: `rest-api` or `site`. */\nfunction kindsOf(manifest: ConstructManifest): Map<string, string> {\n\treturn new Map(\n\t\tObject.entries(manifest).map(([id, declaration]) => [\n\t\t\tappKey(id),\n\t\t\tdeclaration.kind,\n\t\t]),\n\t);\n}\n\nasync function exec(\n\tctx: DeployPhaseContext<unknown>,\n\trun: SstRun,\n\t[command, args]: [string, string[]],\n\toptions: { env: Record<string, string>; timeoutMs: number },\n): Promise<void> {\n\tconst result = await run.sandbox.exec(command, args, {\n\t\tcwd: ctx.cwd,\n\t\tenv: options.env,\n\t\ttimeoutMs: options.timeoutMs,\n\t\toutput: ctx.childOutput,\n\t\tsignal: ctx.signal,\n\t});\n\tif (result.exitCode !== 0) {\n\t\tthrow new CommandFailed(\n\t\t\tcommand,\n\t\t\targs,\n\t\t\tresult.exitCode,\n\t\t\tresult.signal as NodeJS.Signals | null,\n\t\t);\n\t}\n}\n\n/** The `sst` target, with its health checks tuned — for tests and hosts. */\nexport function createSstTarget(\n\toptions: SstTargetOptions = {},\n): DeployTarget<undefined, SstRun> {\n\tconst health = {\n\t\tattempts: options.health?.attempts ?? 6,\n\t\tdelayMs: options.health?.delayMs ?? 5_000,\n\t\ttimeoutMs: options.health?.timeoutMs ?? 10_000,\n\t};\n\n\treturn defineTarget<undefined, SstRun>({\n\t\tname: 'sst',\n\t\truntime: 'aws',\n\t\t// SST keeps no previous release to return to; migrations run in the\n\t\t// stack, as `fromManifest` provisions the database; and nothing here\n\t\t// builds an image.\n\t\tcapabilities: { rollback: false, migrations: 'target', images: false },\n\t\tcredentials: ['aws'],\n\n\t\tasync validate(ctx) {\n\t\t\tif (!existsSync(join(ctx.cwd, 'sst.config.ts'))) {\n\t\t\t\tthrow new SstConfigNotFound(ctx.cwd);\n\t\t\t}\n\n\t\t\tconst aws = await ctx.credentials.get(\n\t\t\t\t{ kind: 'aws', stage: ctx.stage },\n\t\t\t\t{ signal: ctx.signal },\n\t\t\t);\n\t\t\tif (!aws) throw new MissingCredential('aws', ctx.stage);\n\t\t\tif (!('profile' in aws)) {\n\t\t\t\tctx.secrets.mask(aws.secretAccessKey);\n\t\t\t\tif (aws.sessionToken) ctx.secrets.mask(aws.sessionToken);\n\t\t\t}\n\n\t\t\t// A mobile app ships through its own store's toolchain.\n\t\t\tfor (const app of ctx.apps) {\n\t\t\t\tif (ctx.workspace.apps[app]?.type === 'mobile') {\n\t\t\t\t\tctx.skip(app, 'mobile apps deploy through their own toolchain');\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst skipped = new Set(ctx.skipped.map(({ app }) => app));\n\t\t\tconst apps = ctx.apps.filter((app) => !skipped.has(app));\n\n\t\t\tctx.logger.info(\n\t\t\t\t`\\n🚀 Deploying \"${ctx.workspace.name}\" to the \"${ctx.stage}\" stage with SST`,\n\t\t\t);\n\t\t\tctx.logger.info(\n\t\t\t\t` AWS: ${'profile' in aws ? `profile \"${aws.profile}\"` : 'access keys'}`,\n\t\t\t);\n\t\t\t// `sst deploy` takes the whole stack: there is no deploying one app.\n\t\t\tconst deployable = Object.values(ctx.workspace.apps).filter(\n\t\t\t\t(app) => app.type !== 'mobile',\n\t\t\t).length;\n\t\t\tif (apps.length < deployable) {\n\t\t\t\tctx.logger.warn(\n\t\t\t\t\t` sst deploy deploys every construct of the stage, not only ${apps.join(', ')}.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tsandbox: activeSandbox() ?? new LocalSandbox({ root: ctx.cwd }),\n\t\t\t\tpm: detectPackageManager(ctx.cwd),\n\t\t\t\taws,\n\t\t\t\tapps,\n\t\t\t\turls: {},\n\t\t\t};\n\t\t},\n\n\t\tasync plan(ctx, run) {\n\t\t\tconst kinds = kindsOf(ctx.manifest);\n\t\t\tfor (const app of run.apps) {\n\t\t\t\tctx.emit({\n\t\t\t\t\ttype: 'resource.planned',\n\t\t\t\t\tkey: `application:${app}`,\n\t\t\t\t\tresourceType: kinds.get(app) ?? 'application',\n\t\t\t\t\taction: 'ensure',\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst [build, buildArgs] = buildCommand(ctx, run);\n\t\t\tconst [deploy, deployArgs] = deployCommand(ctx, run);\n\t\t\tctx.logger.info(` Would run: ${[build, ...buildArgs].join(' ')}`);\n\t\t\tctx.logger.info(` Would run: ${[deploy, ...deployArgs].join(' ')}`);\n\t\t},\n\n\t\tasync build(ctx, run) {\n\t\t\tctx.logger.info('\\n📦 Building the manifest SST deploys');\n\t\t\t// No credential: the build runs the project's code and needs none.\n\t\t\tawait exec(ctx, run, buildCommand(ctx, run), {\n\t\t\t\tenv: { ...run.sandbox.env },\n\t\t\t\ttimeoutMs: BUILD_TIMEOUT_MS,\n\t\t\t});\n\t\t},\n\n\t\tasync release(ctx, run) {\n\t\t\tctx.logger.info(`\\n☁️ sst deploy --stage ${ctx.stage}`);\n\t\t\tawait clearOutputs(ctx.cwd);\n\t\t\tawait exec(ctx, run, deployCommand(ctx, run), {\n\t\t\t\tenv: { ...withoutAws(run.sandbox.env), ...awsEnv(run.aws) },\n\t\t\t\ttimeoutMs: DEPLOY_TIMEOUT_MS,\n\t\t\t});\n\n\t\t\trun.urls = surfaceUrls(await readOutputs(ctx.cwd), run.apps);\n\t\t\tfor (const app of run.apps) {\n\t\t\t\tctx.emit({\n\t\t\t\t\ttype: 'app.deployed',\n\t\t\t\t\tapp,\n\t\t\t\t\tapplicationId: ctx.name(app),\n\t\t\t\t\t// SST builds no image a registry holds.\n\t\t\t\t\timageRef: '',\n\t\t\t\t\turl: run.urls[app] ?? '',\n\t\t\t\t});\n\t\t\t}\n\t\t},\n\n\t\tasync verify(ctx, run) {\n\t\t\tconst unhealthy: SurfacesUnhealthy['surfaces'][number][] = [];\n\n\t\t\tfor (const app of run.apps) {\n\t\t\t\tconst base = run.urls[app];\n\t\t\t\tif (!base) {\n\t\t\t\t\tctx.logger.warn(\n\t\t\t\t\t\t` ⚠ No URL for ${app} in SST's outputs, so it was not checked. Return it from run() in sst.config.ts — { ${app}: <its url> } — to have it checked.`,\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// A surface — a `rest-api` — is the backend app it derives.\n\t\t\t\tconst backend = ctx.workspace.apps[app]?.type === 'backend';\n\t\t\t\tconst url = healthUrl(base, backend ? '/health' : '/');\n\t\t\t\tconst answer = await checkHealth(app, url, {\n\t\t\t\t\t...health,\n\t\t\t\t\tsignal: ctx.signal,\n\t\t\t\t\temit: ctx.emit,\n\t\t\t\t});\n\t\t\t\tif (answer.healthy) {\n\t\t\t\t\tctx.logger.info(` ✓ ${app}: ${url}`);\n\t\t\t\t} else {\n\t\t\t\t\tunhealthy.push({\n\t\t\t\t\t\tapp,\n\t\t\t\t\t\turl,\n\t\t\t\t\t\t...(answer.status !== undefined ? { status: answer.status } : {}),\n\t\t\t\t\t\t...(answer.error ? { error: answer.error } : {}),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (unhealthy.length > 0) {\n\t\t\t\tthrow new SurfacesUnhealthy(ctx.stage, unhealthy);\n\t\t\t}\n\t\t},\n\n\t\tresult(ctx, run): DeployResult {\n\t\t\treturn {\n\t\t\t\tapps: run.apps.map((appName) => ({\n\t\t\t\t\tappName,\n\t\t\t\t\ttype: ctx.workspace.apps[appName]?.type ?? 'backend',\n\t\t\t\t\tsuccess: true,\n\t\t\t\t\t...(run.urls[appName] ? { url: run.urls[appName] } : {}),\n\t\t\t\t})),\n\t\t\t\tprojectId: '',\n\t\t\t\tenvironmentId: '',\n\t\t\t\tsuccessCount: ctx.dryRun ? 0 : run.apps.length,\n\t\t\t\tfailedCount: 0,\n\t\t\t\tstage: ctx.stage,\n\t\t\t\tidentity: ctx.identity.key,\n\t\t\t\ttag: ctx.tag,\n\t\t\t\tdryRun: ctx.dryRun,\n\t\t\t\tskipped: [...ctx.skipped],\n\t\t\t\turls: { ...run.urls },\n\t\t\t\tchanges: [],\n\t\t\t};\n\t\t},\n\t});\n}\n\nfunction buildCommand(\n\tctx: DeployPhaseContext<unknown>,\n\trun: SstRun,\n): [string, string[]] {\n\treturn projectCommand(run.pm, 'gkm', [\n\t\t'build',\n\t\t'--provider',\n\t\t'aws',\n\t\t'--stage',\n\t\tctx.stage,\n\t]);\n}\n\nfunction deployCommand(\n\tctx: DeployPhaseContext<unknown>,\n\trun: SstRun,\n): [string, string[]] {\n\treturn projectCommand(run.pm, 'sst', ['deploy', '--stage', ctx.stage]);\n}\n\nexport const sstTarget = createSstTarget();\n"],"mappings":"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"}
@@ -1,11 +1,11 @@
1
1
  const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
2
2
  const require_errors = require("./errors-DMC4Umsb.cjs");
3
- const require_compose = require("./compose-8e2xJ61E.cjs");
3
+ const require_compose = require("./compose-BK9s_2Qe.cjs");
4
4
  const require_workspace = require("./workspace-Du4Afb03.cjs");
5
- const require_snsSubscriptions = require("./snsSubscriptions-DYyYP0Xf.cjs");
6
- const require_seeds = require("./seeds-3awh33Wz.cjs");
5
+ const require_snsSubscriptions = require("./snsSubscriptions-f_z4PSFb.cjs");
6
+ const require_seeds = require("./seeds-sMVADbkg.cjs");
7
7
  const require_identity = require("./identity-CbLfgQNT.cjs");
8
- const require_discover = require("./discover-DL6h-J97.cjs");
8
+ const require_discover = require("./discover-DEOspD9M.cjs");
9
9
  const require_config = require("./config.cjs");
10
10
  const require_index = require("./index.cjs");
11
11
  let node_os = require("node:os");
@@ -310,4 +310,4 @@ Object.defineProperty(exports, "stage_exports", {
310
310
  }
311
311
  });
312
312
 
313
- //# sourceMappingURL=stage-BkgbVJ9Z.cjs.map
313
+ //# sourceMappingURL=stage-ZWOE0oLB.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"stage-BkgbVJ9Z.cjs","names":["GkmError","dockerCompose","sshAccess","prompt","edgeAlias","loadWorkspaceAppInfo","NotInAnApp","loadWorkspaceConfig","deploysWithCompose","discover","constructGlobs","secretsStoreFor","deployedStackSecrets","composeStack","deployIdentity","edgePorts","composeServer","dockerHost","spawn","constants"],"sources":["../src/exec/stage.ts"],"sourcesContent":["/**\n * `gkm exec --stage <stage> -- <command>`: a command run on this machine as\n * a deployed compose stage's app — a data import, a backfill, a report.\n *\n * The app's environment is the one its container runs with, derived the way\n * the deploy derives it (`composeStack`) from the stage's secrets, read from\n * the stage's store the way the deploy reads them. Only what the container\n * reaches on the stack's network is moved: each service it names that the\n * server publishes on its loopback — Postgres, MinIO — is reached through an\n * SSH tunnel on the deploy's own login (`ServerAccess.tunnel`), and each\n * sibling app through its public address.\n *\n * Nothing is written to disk. The values exist only in the command's\n * environment, and its Credentials are filled from there by a preload that\n * is a `data:` URL naming the keys — never their values.\n */\n\nimport { spawn } from 'node:child_process';\nimport { constants } from 'node:os';\nimport type { ConstructManifest } from '@geekmidas/manifest';\nimport { type ComposeDocker, dockerCompose } from '../compose/docker.js';\nimport { deployedStackSecrets } from '../compose/secrets.js';\nimport {\n\ttype ComposeServer,\n\tcomposeServer,\n\tdockerHost,\n\ttype ServerAccess,\n\tsshAccess,\n\ttype Tunnel,\n} from '../compose/server.js';\nimport { type ComposeStack, composeStack } from '../compose/stack.js';\nimport { edgeAlias } from '../compose/traefik.js';\nimport {\n\tloadWorkspaceAppInfo,\n\tloadWorkspaceConfig,\n\tNotInAnApp,\n} from '../config.js';\nimport { deployIdentity } from '../deploy/identity.js';\nimport { GkmError } from '../errors';\nimport { prompt } from '../prompt.js';\nimport { deploysWithCompose } from '../providers/dns.js';\nimport { discover } from '../reconcile/discover.js';\nimport { constructGlobs } from '../reconcile/workspace.js';\nimport { secretsStoreFor } from '../secrets/store.js';\nimport type { StageSecrets } from '../secrets/types.js';\nimport { edgePorts } from '../target/compose/phases.js';\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\n\nconst logger = console;\n\n/** The stage is not one the compose target deploys. */\nexport class ExecStageNotComposed extends GkmError {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`gkm exec --stage runs a command against a compose stage's server, and ` +\n\t\t\t\t`'${stage}' is not deployed with compose. Set deploy.default: 'compose' ` +\n\t\t\t\t'in gkm.config.ts, or run the command where the stage runs.',\n\t\t);\n\t\tthis.name = 'ExecStageNotComposed';\n\t}\n}\n\n/** The stage's secrets lack what its deploy generates: it was never deployed. */\nexport class ExecStageNotDeployed extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly missing: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`'${stage}' has no ${missing.join(', ')} in its secrets yet, so what its ` +\n\t\t\t\t'apps run with is not decided. Deploy it first: ' +\n\t\t\t\t`gkm deploy --stage ${stage}.`,\n\t\t);\n\t\tthis.name = 'ExecStageNotDeployed';\n\t}\n}\n\n/** The directory is not one of the stack's apps. */\nexport class ExecAppNotInStack extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly app: string | undefined,\n\t\treadonly apps: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`${app ? `'${app}' is not` : 'This directory is not in'} one of the apps ` +\n\t\t\t\t`'${stage}' runs (${apps.join(', ')}), so there is no environment to run ` +\n\t\t\t\t`as. Run from an app's directory: cd apps/${apps[0] ?? '<app>'} && ` +\n\t\t\t\t`gkm exec --stage ${stage} -- <command>.`,\n\t\t);\n\t\tthis.name = 'ExecAppNotInStack';\n\t}\n}\n\n/** A protected stage, and nobody said yes. */\nexport class ExecNotConfirmed extends GkmError {\n\tconstructor(readonly stage: string) {\n\t\tsuper(`Nothing was run against '${stage}'.`);\n\t\tthis.name = 'ExecNotConfirmed';\n\t}\n}\n\nexport interface StageExecOptions {\n\tstage: string;\n\t/** The AWS profile for the stage's account, where its secrets are kept. */\n\tprofile?: string;\n\t/**\n\t * The SSH login to reach the server as, in place of\n\t * `deploy.compose.server.<stage>.user` — a person's own, where the\n\t * deploy's key lives only in CI.\n\t */\n\tsshUser?: string;\n\t/** Run against a protected stage without asking. */\n\tyes?: boolean;\n\tcwd?: string;\n}\n\n/** What the command needs from outside the process, so tests can stand in. */\nexport interface StageExecDeps {\n\t/** The server's Docker, asked which port each service publishes. */\n\tdocker: ComposeDocker;\n\t/** The tunnels to the server's loopback. */\n\tserver: ServerAccess;\n\t/** Asked before running against a protected stage. */\n\tconfirm: (question: string) => Promise<boolean>;\n\t/** Where the edge's ports are read from, as the deploy reads them. */\n\tenv: NodeJS.ProcessEnv;\n}\n\nexport const defaultStageExecDeps: StageExecDeps = {\n\tdocker: dockerCompose,\n\tserver: sshAccess,\n\tconfirm: async (question) => {\n\t\tconst answer = await prompt(`${question} [y/N] `, {\n\t\t\tinstead: 'Pass --yes to run without asking.',\n\t\t});\n\t\treturn /^y(es)?$/i.test(answer.trim());\n\t},\n\tenv: process.env,\n};\n\n/** A service on the stack's network the app's environment names. */\nexport interface StackAddress {\n\tservice: string;\n\tport: number;\n}\n\n/**\n * The environment an app runs with on the stage: a backend's container's,\n * over the keys of each worker built from it; a site's or a mobile app's,\n * the public values its build reads.\n *\n * @throws {ExecAppNotInStack}\n */\nexport function appEnvironment(\n\tstack: Pick<ComposeStack, 'stage' | 'apps' | 'workers' | 'mobileApps'>,\n\tappName: string | undefined,\n): Record<string, string> {\n\tconst mobile = stack.mobileApps.find(\n\t\t(candidate) => candidate.name === appName,\n\t);\n\tif (mobile) return { ...mobile.values };\n\tconst app = stack.apps.find((candidate) => candidate.name === appName);\n\tif (!app) {\n\t\tthrow new ExecAppNotInStack(stack.stage, appName, [\n\t\t\t...stack.apps.map((candidate) => candidate.name),\n\t\t\t...stack.mobileApps.map((candidate) => candidate.name),\n\t\t]);\n\t}\n\tif (app.kind === 'site') return { ...app.build?.args };\n\tconst workers = stack.workers.filter((worker) => worker.host === app.name);\n\treturn Object.assign(\n\t\t{},\n\t\t...workers.map((worker) => worker.env ?? {}),\n\t\tapp.env ?? {},\n\t);\n}\n\n/** `host:port`, at a URL's authority — after `//` or a login's `@`. */\nfunction authority(host: string, port: number): RegExp {\n\treturn new RegExp(\n\t\t`(?<=[/@,])${host.replace(/[.-]/g, '\\\\$&')}:${port}(?!\\\\d)`,\n\t\t'g',\n\t);\n}\n\n/**\n * Each of the stack's services the environment reaches on the stack's\n * network, once: `postgres:5432`, `minio:9000`.\n */\nexport function stackAddresses(\n\tenv: Readonly<Record<string, string>>,\n\tstack: Pick<ComposeStack, 'infra'>,\n): StackAddress[] {\n\tconst found = new Map<string, StackAddress>();\n\tconst pattern = /(?<=[/@,])([a-z0-9-]+):(\\d+)(?!\\d)/g;\n\tfor (const value of Object.values(env)) {\n\t\tfor (const match of value.matchAll(pattern)) {\n\t\t\tconst service = match[1]!;\n\t\t\tif (!stack.infra.includes(service)) continue;\n\t\t\tconst port = Number(match[2]);\n\t\t\tfound.set(`${service}:${port}`, { service, port });\n\t\t}\n\t}\n\treturn [...found.values()];\n}\n\n/**\n * The environment as this machine reaches it: each service at its tunnel's\n * local port, and each sibling app at its public address. What has no\n * tunnel is left as the container has it.\n */\nexport function throughTunnels(\n\tenv: Readonly<Record<string, string>>,\n\tstack: Pick<ComposeStack, 'apps' | 'project'>,\n\ttunnels: ReadonlyMap<string, number>,\n): Record<string, string> {\n\tconst rewrites: [RegExp, string][] = [];\n\tfor (const [address, port] of tunnels) {\n\t\tconst [service, inside] = address.split(':');\n\t\trewrites.push([authority(service!, Number(inside)), `127.0.0.1:${port}`]);\n\t}\n\tfor (const app of stack.apps) {\n\t\tfor (const host of [app.name, edgeAlias(stack.project, app.name)]) {\n\t\t\trewrites.push([\n\t\t\t\tnew RegExp(\n\t\t\t\t\t`http://${host.replace(/[.-]/g, '\\\\$&')}:${app.port}(?!\\\\d)`,\n\t\t\t\t\t'g',\n\t\t\t\t),\n\t\t\t\tapp.url,\n\t\t\t]);\n\t\t}\n\t}\n\tconst out: Record<string, string> = {};\n\tfor (const [key, value] of Object.entries(env)) {\n\t\tout[key] = rewrites.reduce(\n\t\t\t(current, [pattern, replacement]) =>\n\t\t\t\tcurrent.replace(pattern, () => replacement),\n\t\t\tvalue,\n\t\t);\n\t}\n\treturn out;\n}\n\n/**\n * The preload that fills Credentials from the command's own environment: a\n * `data:` URL holding the keys, so no file — and no value — is written.\n */\nexport function credentialsImport(keys: readonly string[]): string {\n\tconst source =\n\t\t'globalThis.__gkm_credentials__=Object.fromEntries(' +\n\t\t`${JSON.stringify(keys)}.map((k)=>[k,process.env[k]]));`;\n\treturn `--import=data:text/javascript,${encodeURIComponent(source)}`;\n}\n\n/** A deployed compose stage, as its deploy has it, and the app here. */\nexport interface LoadedStage {\n\tworkspace: NormalizedWorkspace;\n\tmanifest: ConstructManifest;\n\tstack: ComposeStack;\n\t/** The stage's secrets, as its deploy reads them. */\n\tsecrets: StageSecrets;\n\t/** The app this directory is, if it is one. */\n\tappName: string | undefined;\n\tprotectedStage: boolean;\n}\n\n/**\n * The stage's stack, and the app this directory is, as the deploy has them:\n * its secrets read from its store, its values derived as the deploy derives\n * them. Nothing is reached but the store — no server, no SSH.\n *\n * @throws {UndeclaredStage}, {ExecStageNotComposed}, {ExecStageNotDeployed}\n */\nexport async function loadStage(\n\toptions: Pick<StageExecOptions, 'stage' | 'profile' | 'cwd'>,\n\tenv: NodeJS.ProcessEnv = process.env,\n): Promise<LoadedStage> {\n\tconst cwd = options.cwd ?? process.cwd();\n\tconst { stage } = options;\n\t// Outside an app the workspace is still loaded, so the stage is checked\n\t// before the directory is refused.\n\tconst info = await loadWorkspaceAppInfo(cwd).catch((error) => {\n\t\tif (error instanceof NotInAnApp) return undefined;\n\t\tthrow error;\n\t});\n\tconst workspace =\n\t\tinfo?.workspace ?? (await loadWorkspaceConfig(cwd)).workspace;\n\n\tassertDeployedStage(workspace.stages, stage);\n\tif (!deploysWithCompose(workspace)) throw new ExecStageNotComposed(stage);\n\n\tconst runnables: Record<string, string[]> = {};\n\tconst background: Record<string, string[]> = {};\n\tconst manifest: ConstructManifest = await discover({\n\t\tpatterns: constructGlobs(workspace),\n\t\tcwd: workspace.root,\n\t\trunnables,\n\t\tbackground,\n\t});\n\n\tconst store = await secretsStoreFor(workspace, stage, {\n\t\t...(options.profile ? { profile: options.profile } : {}),\n\t});\n\tconst { secrets, generated } = deployedStackSecrets(\n\t\tworkspace,\n\t\tstage,\n\t\tawait store.read(stage),\n\t\tmanifest,\n\t);\n\tif (generated.length > 0) throw new ExecStageNotDeployed(stage, generated);\n\n\tconst stack = composeStack({\n\t\tworkspace,\n\t\tmanifest,\n\t\trunnables,\n\t\tbackground,\n\t\tstage,\n\t\tidentity: deployIdentity(workspace, stage),\n\t\t// Built, so a site's public values are resolved as its build has them.\n\t\timages: { mode: 'build', tag: 'exec' },\n\t\tsecrets,\n\t\tports: edgePorts(env),\n\t});\n\treturn {\n\t\tworkspace,\n\t\tmanifest,\n\t\tstack,\n\t\tsecrets,\n\t\tappName: info?.appName,\n\t\tprotectedStage: workspace.stages.protected?.includes(stage) ?? false,\n\t};\n}\n\n/** The stage's server, as SSH reaches it: `--ssh-user` over the config's. */\nfunction stageServer(\n\tloaded: LoadedStage,\n\toptions: StageExecOptions,\n): ComposeServer {\n\tconst server = composeServer({\n\t\tstage: options.stage,\n\t\tlocal: false,\n\t\tconfig: loaded.workspace.deploy?.compose?.server,\n\t\tcustom: loaded.secrets.custom,\n\t})!;\n\tif (options.sshUser) server.user = options.sshUser;\n\treturn server;\n}\n\n/**\n * Run `commandArgs` as the app this directory is, on the stage — through a\n * tunnel to each service it reaches — and return its exit code. Every tunnel\n * is closed once it exits, whatever it exits with.\n *\n * @throws {ExecStageNotComposed}, {ExecStageNotDeployed}, {ExecAppNotInStack},\n * {ExecNotConfirmed}, {ComposeServerUnreachable}\n */\nexport async function execOnStage(\n\tcommandArgs: readonly [string, ...string[]],\n\toptions: StageExecOptions,\n\tdeps: StageExecDeps = defaultStageExecDeps,\n): Promise<number> {\n\tconst { stage } = options;\n\tconst loaded = await loadStage(options, deps.env);\n\tconst { stack, appName, protectedStage } = loaded;\n\tconst env = appEnvironment(stack, appName);\n\n\tif (protectedStage && !options.yes) {\n\t\tconst question = `'${stage}' is a protected stage. Run ${commandArgs.join(' ')} against it?`;\n\t\tif (!(await deps.confirm(question))) throw new ExecNotConfirmed(stage);\n\t}\n\n\t// Only what reaches a service on the stack needs the server: a site's or\n\t// a mobile app's values are public addresses, and need no SSH at all.\n\tconst addresses = stackAddresses(env, stack);\n\tif (addresses.length === 0) {\n\t\tlogger.log(`📦 App: ${appName} on '${stage}'`);\n\t\tlogger.log(`🔐 Loaded ${Object.keys(env).length} value(s) for '${stage}'`);\n\t\treturn run(commandArgs, env, options.cwd);\n\t}\n\n\tconst server = stageServer(loaded, options);\n\tlogger.log(`📦 App: ${appName} on '${stage}' (${server.host})`);\n\n\t// The deploy's own check: SSH logs in and Docker answers, before any\n\t// tunnel is asked for — so a key the server refuses is said as that.\n\tawait deps.server.check(server);\n\n\tconst tunnels: Tunnel[] = [];\n\tconst ports = new Map<string, number>();\n\ttry {\n\t\tfor (const address of addresses) {\n\t\t\tconst published = await deps.docker.published(\n\t\t\t\t{ host: dockerHost(server) },\n\t\t\t\tstack.project,\n\t\t\t\taddress.service,\n\t\t\t\taddress.port,\n\t\t\t);\n\t\t\tif (published === undefined) {\n\t\t\t\tlogger.log(\n\t\t\t\t\t`⚠️ ${address.service} is not published on the server (no running ` +\n\t\t\t\t\t\t`${stack.project} ${address.service} publishes ${address.port}), so ` +\n\t\t\t\t\t\t'it is unreachable from here; its address is passed on unchanged.',\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst tunnel = await deps.server.tunnel(server, published);\n\t\t\ttunnels.push(tunnel);\n\t\t\tports.set(`${address.service}:${address.port}`, tunnel.port);\n\t\t\tlogger.log(`🚇 ${address.service} → 127.0.0.1:${tunnel.port} (over SSH)`);\n\t\t}\n\n\t\tconst credentials = throughTunnels(env, stack, ports);\n\t\tlogger.log(\n\t\t\t`🔐 Loaded ${Object.keys(credentials).length} value(s) for '${stage}'`,\n\t\t);\n\t\treturn await run(commandArgs, credentials, options.cwd);\n\t} finally {\n\t\tawait Promise.all(tunnels.map((tunnel) => tunnel.close()));\n\t}\n}\n\n/**\n * The command, with the values in its environment and its Credentials. A\n * Ctrl-C reaches it — it is in the terminal's process group — and this\n * process waits for it to exit rather than leaving before the tunnels close.\n */\nfunction run(\n\tcommandArgs: readonly [string, ...string[]],\n\tcredentials: Record<string, string>,\n\tcwd: string | undefined,\n): Promise<number> {\n\tconst [cmd, ...args] = commandArgs;\n\tlogger.log(`🚀 Running: ${commandArgs.join(' ')}`);\n\n\tconst inherited = (process.env.NODE_OPTIONS ?? '')\n\t\t.replace(/--import[= ]\\S+/g, '')\n\t\t.trim();\n\tconst nodeOptions = [inherited, credentialsImport(Object.keys(credentials))]\n\t\t.filter(Boolean)\n\t\t.join(' ');\n\n\tconst wait = () => {};\n\tprocess.on('SIGINT', wait);\n\tprocess.on('SIGTERM', wait);\n\treturn new Promise<number>((resolve) => {\n\t\tconst child = spawn(cmd, args, {\n\t\t\t...(cwd ? { cwd } : {}),\n\t\t\tstdio: 'inherit',\n\t\t\tenv: { ...process.env, ...credentials, NODE_OPTIONS: nodeOptions },\n\t\t});\n\t\tchild.on('close', (code, signal) => {\n\t\t\tresolve(code ?? 128 + (signal ? constants.signals[signal] : 0));\n\t\t});\n\t\tchild.on('error', (error) => {\n\t\t\tlogger.error(`Failed to run command: ${error.message}`);\n\t\t\tresolve(1);\n\t\t});\n\t}).finally(() => {\n\t\tprocess.off('SIGINT', wait);\n\t\tprocess.off('SIGTERM', wait);\n\t});\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"stage-ZWOE0oLB.cjs","names":["GkmError","dockerCompose","sshAccess","prompt","edgeAlias","loadWorkspaceAppInfo","NotInAnApp","loadWorkspaceConfig","deploysWithCompose","discover","constructGlobs","secretsStoreFor","deployedStackSecrets","composeStack","deployIdentity","edgePorts","composeServer","dockerHost","spawn","constants"],"sources":["../src/exec/stage.ts"],"sourcesContent":["/**\n * `gkm exec --stage <stage> -- <command>`: a command run on this machine as\n * a deployed compose stage's app — a data import, a backfill, a report.\n *\n * The app's environment is the one its container runs with, derived the way\n * the deploy derives it (`composeStack`) from the stage's secrets, read from\n * the stage's store the way the deploy reads them. Only what the container\n * reaches on the stack's network is moved: each service it names that the\n * server publishes on its loopback — Postgres, MinIO — is reached through an\n * SSH tunnel on the deploy's own login (`ServerAccess.tunnel`), and each\n * sibling app through its public address.\n *\n * Nothing is written to disk. The values exist only in the command's\n * environment, and its Credentials are filled from there by a preload that\n * is a `data:` URL naming the keys — never their values.\n */\n\nimport { spawn } from 'node:child_process';\nimport { constants } from 'node:os';\nimport type { ConstructManifest } from '@geekmidas/manifest';\nimport { type ComposeDocker, dockerCompose } from '../compose/docker.js';\nimport { deployedStackSecrets } from '../compose/secrets.js';\nimport {\n\ttype ComposeServer,\n\tcomposeServer,\n\tdockerHost,\n\ttype ServerAccess,\n\tsshAccess,\n\ttype Tunnel,\n} from '../compose/server.js';\nimport { type ComposeStack, composeStack } from '../compose/stack.js';\nimport { edgeAlias } from '../compose/traefik.js';\nimport {\n\tloadWorkspaceAppInfo,\n\tloadWorkspaceConfig,\n\tNotInAnApp,\n} from '../config.js';\nimport { deployIdentity } from '../deploy/identity.js';\nimport { GkmError } from '../errors';\nimport { prompt } from '../prompt.js';\nimport { deploysWithCompose } from '../providers/dns.js';\nimport { discover } from '../reconcile/discover.js';\nimport { constructGlobs } from '../reconcile/workspace.js';\nimport { secretsStoreFor } from '../secrets/store.js';\nimport type { StageSecrets } from '../secrets/types.js';\nimport { edgePorts } from '../target/compose/phases.js';\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\n\nconst logger = console;\n\n/** The stage is not one the compose target deploys. */\nexport class ExecStageNotComposed extends GkmError {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`gkm exec --stage runs a command against a compose stage's server, and ` +\n\t\t\t\t`'${stage}' is not deployed with compose. Set deploy.default: 'compose' ` +\n\t\t\t\t'in gkm.config.ts, or run the command where the stage runs.',\n\t\t);\n\t\tthis.name = 'ExecStageNotComposed';\n\t}\n}\n\n/** The stage's secrets lack what its deploy generates: it was never deployed. */\nexport class ExecStageNotDeployed extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly missing: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`'${stage}' has no ${missing.join(', ')} in its secrets yet, so what its ` +\n\t\t\t\t'apps run with is not decided. Deploy it first: ' +\n\t\t\t\t`gkm deploy --stage ${stage}.`,\n\t\t);\n\t\tthis.name = 'ExecStageNotDeployed';\n\t}\n}\n\n/** The directory is not one of the stack's apps. */\nexport class ExecAppNotInStack extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly app: string | undefined,\n\t\treadonly apps: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`${app ? `'${app}' is not` : 'This directory is not in'} one of the apps ` +\n\t\t\t\t`'${stage}' runs (${apps.join(', ')}), so there is no environment to run ` +\n\t\t\t\t`as. Run from an app's directory: cd apps/${apps[0] ?? '<app>'} && ` +\n\t\t\t\t`gkm exec --stage ${stage} -- <command>.`,\n\t\t);\n\t\tthis.name = 'ExecAppNotInStack';\n\t}\n}\n\n/** A protected stage, and nobody said yes. */\nexport class ExecNotConfirmed extends GkmError {\n\tconstructor(readonly stage: string) {\n\t\tsuper(`Nothing was run against '${stage}'.`);\n\t\tthis.name = 'ExecNotConfirmed';\n\t}\n}\n\nexport interface StageExecOptions {\n\tstage: string;\n\t/** The AWS profile for the stage's account, where its secrets are kept. */\n\tprofile?: string;\n\t/**\n\t * The SSH login to reach the server as, in place of\n\t * `deploy.compose.server.<stage>.user` — a person's own, where the\n\t * deploy's key lives only in CI.\n\t */\n\tsshUser?: string;\n\t/** Run against a protected stage without asking. */\n\tyes?: boolean;\n\tcwd?: string;\n}\n\n/** What the command needs from outside the process, so tests can stand in. */\nexport interface StageExecDeps {\n\t/** The server's Docker, asked which port each service publishes. */\n\tdocker: ComposeDocker;\n\t/** The tunnels to the server's loopback. */\n\tserver: ServerAccess;\n\t/** Asked before running against a protected stage. */\n\tconfirm: (question: string) => Promise<boolean>;\n\t/** Where the edge's ports are read from, as the deploy reads them. */\n\tenv: NodeJS.ProcessEnv;\n}\n\nexport const defaultStageExecDeps: StageExecDeps = {\n\tdocker: dockerCompose,\n\tserver: sshAccess,\n\tconfirm: async (question) => {\n\t\tconst answer = await prompt(`${question} [y/N] `, {\n\t\t\tinstead: 'Pass --yes to run without asking.',\n\t\t});\n\t\treturn /^y(es)?$/i.test(answer.trim());\n\t},\n\tenv: process.env,\n};\n\n/** A service on the stack's network the app's environment names. */\nexport interface StackAddress {\n\tservice: string;\n\tport: number;\n}\n\n/**\n * The environment an app runs with on the stage: a backend's container's,\n * over the keys of each worker built from it; a site's or a mobile app's,\n * the public values its build reads.\n *\n * @throws {ExecAppNotInStack}\n */\nexport function appEnvironment(\n\tstack: Pick<ComposeStack, 'stage' | 'apps' | 'workers' | 'mobileApps'>,\n\tappName: string | undefined,\n): Record<string, string> {\n\tconst mobile = stack.mobileApps.find(\n\t\t(candidate) => candidate.name === appName,\n\t);\n\tif (mobile) return { ...mobile.values };\n\tconst app = stack.apps.find((candidate) => candidate.name === appName);\n\tif (!app) {\n\t\tthrow new ExecAppNotInStack(stack.stage, appName, [\n\t\t\t...stack.apps.map((candidate) => candidate.name),\n\t\t\t...stack.mobileApps.map((candidate) => candidate.name),\n\t\t]);\n\t}\n\tif (app.kind === 'site') return { ...app.build?.args };\n\tconst workers = stack.workers.filter((worker) => worker.host === app.name);\n\treturn Object.assign(\n\t\t{},\n\t\t...workers.map((worker) => worker.env ?? {}),\n\t\tapp.env ?? {},\n\t);\n}\n\n/** `host:port`, at a URL's authority — after `//` or a login's `@`. */\nfunction authority(host: string, port: number): RegExp {\n\treturn new RegExp(\n\t\t`(?<=[/@,])${host.replace(/[.-]/g, '\\\\$&')}:${port}(?!\\\\d)`,\n\t\t'g',\n\t);\n}\n\n/**\n * Each of the stack's services the environment reaches on the stack's\n * network, once: `postgres:5432`, `minio:9000`.\n */\nexport function stackAddresses(\n\tenv: Readonly<Record<string, string>>,\n\tstack: Pick<ComposeStack, 'infra'>,\n): StackAddress[] {\n\tconst found = new Map<string, StackAddress>();\n\tconst pattern = /(?<=[/@,])([a-z0-9-]+):(\\d+)(?!\\d)/g;\n\tfor (const value of Object.values(env)) {\n\t\tfor (const match of value.matchAll(pattern)) {\n\t\t\tconst service = match[1]!;\n\t\t\tif (!stack.infra.includes(service)) continue;\n\t\t\tconst port = Number(match[2]);\n\t\t\tfound.set(`${service}:${port}`, { service, port });\n\t\t}\n\t}\n\treturn [...found.values()];\n}\n\n/**\n * The environment as this machine reaches it: each service at its tunnel's\n * local port, and each sibling app at its public address. What has no\n * tunnel is left as the container has it.\n */\nexport function throughTunnels(\n\tenv: Readonly<Record<string, string>>,\n\tstack: Pick<ComposeStack, 'apps' | 'project'>,\n\ttunnels: ReadonlyMap<string, number>,\n): Record<string, string> {\n\tconst rewrites: [RegExp, string][] = [];\n\tfor (const [address, port] of tunnels) {\n\t\tconst [service, inside] = address.split(':');\n\t\trewrites.push([authority(service!, Number(inside)), `127.0.0.1:${port}`]);\n\t}\n\tfor (const app of stack.apps) {\n\t\tfor (const host of [app.name, edgeAlias(stack.project, app.name)]) {\n\t\t\trewrites.push([\n\t\t\t\tnew RegExp(\n\t\t\t\t\t`http://${host.replace(/[.-]/g, '\\\\$&')}:${app.port}(?!\\\\d)`,\n\t\t\t\t\t'g',\n\t\t\t\t),\n\t\t\t\tapp.url,\n\t\t\t]);\n\t\t}\n\t}\n\tconst out: Record<string, string> = {};\n\tfor (const [key, value] of Object.entries(env)) {\n\t\tout[key] = rewrites.reduce(\n\t\t\t(current, [pattern, replacement]) =>\n\t\t\t\tcurrent.replace(pattern, () => replacement),\n\t\t\tvalue,\n\t\t);\n\t}\n\treturn out;\n}\n\n/**\n * The preload that fills Credentials from the command's own environment: a\n * `data:` URL holding the keys, so no file — and no value — is written.\n */\nexport function credentialsImport(keys: readonly string[]): string {\n\tconst source =\n\t\t'globalThis.__gkm_credentials__=Object.fromEntries(' +\n\t\t`${JSON.stringify(keys)}.map((k)=>[k,process.env[k]]));`;\n\treturn `--import=data:text/javascript,${encodeURIComponent(source)}`;\n}\n\n/** A deployed compose stage, as its deploy has it, and the app here. */\nexport interface LoadedStage {\n\tworkspace: NormalizedWorkspace;\n\tmanifest: ConstructManifest;\n\tstack: ComposeStack;\n\t/** The stage's secrets, as its deploy reads them. */\n\tsecrets: StageSecrets;\n\t/** The app this directory is, if it is one. */\n\tappName: string | undefined;\n\tprotectedStage: boolean;\n}\n\n/**\n * The stage's stack, and the app this directory is, as the deploy has them:\n * its secrets read from its store, its values derived as the deploy derives\n * them. Nothing is reached but the store — no server, no SSH.\n *\n * @throws {UndeclaredStage}, {ExecStageNotComposed}, {ExecStageNotDeployed}\n */\nexport async function loadStage(\n\toptions: Pick<StageExecOptions, 'stage' | 'profile' | 'cwd'>,\n\tenv: NodeJS.ProcessEnv = process.env,\n): Promise<LoadedStage> {\n\tconst cwd = options.cwd ?? process.cwd();\n\tconst { stage } = options;\n\t// Outside an app the workspace is still loaded, so the stage is checked\n\t// before the directory is refused.\n\tconst info = await loadWorkspaceAppInfo(cwd).catch((error) => {\n\t\tif (error instanceof NotInAnApp) return undefined;\n\t\tthrow error;\n\t});\n\tconst workspace =\n\t\tinfo?.workspace ?? (await loadWorkspaceConfig(cwd)).workspace;\n\n\tassertDeployedStage(workspace.stages, stage);\n\tif (!deploysWithCompose(workspace)) throw new ExecStageNotComposed(stage);\n\n\tconst runnables: Record<string, string[]> = {};\n\tconst background: Record<string, string[]> = {};\n\tconst manifest: ConstructManifest = await discover({\n\t\tpatterns: constructGlobs(workspace),\n\t\tcwd: workspace.root,\n\t\trunnables,\n\t\tbackground,\n\t});\n\n\tconst store = await secretsStoreFor(workspace, stage, {\n\t\t...(options.profile ? { profile: options.profile } : {}),\n\t});\n\tconst { secrets, generated } = deployedStackSecrets(\n\t\tworkspace,\n\t\tstage,\n\t\tawait store.read(stage),\n\t\tmanifest,\n\t);\n\tif (generated.length > 0) throw new ExecStageNotDeployed(stage, generated);\n\n\tconst stack = composeStack({\n\t\tworkspace,\n\t\tmanifest,\n\t\trunnables,\n\t\tbackground,\n\t\tstage,\n\t\tidentity: deployIdentity(workspace, stage),\n\t\t// Built, so a site's public values are resolved as its build has them.\n\t\timages: { mode: 'build', tag: 'exec' },\n\t\tsecrets,\n\t\tports: edgePorts(env),\n\t});\n\treturn {\n\t\tworkspace,\n\t\tmanifest,\n\t\tstack,\n\t\tsecrets,\n\t\tappName: info?.appName,\n\t\tprotectedStage: workspace.stages.protected?.includes(stage) ?? false,\n\t};\n}\n\n/** The stage's server, as SSH reaches it: `--ssh-user` over the config's. */\nfunction stageServer(\n\tloaded: LoadedStage,\n\toptions: StageExecOptions,\n): ComposeServer {\n\tconst server = composeServer({\n\t\tstage: options.stage,\n\t\tlocal: false,\n\t\tconfig: loaded.workspace.deploy?.compose?.server,\n\t\tcustom: loaded.secrets.custom,\n\t})!;\n\tif (options.sshUser) server.user = options.sshUser;\n\treturn server;\n}\n\n/**\n * Run `commandArgs` as the app this directory is, on the stage — through a\n * tunnel to each service it reaches — and return its exit code. Every tunnel\n * is closed once it exits, whatever it exits with.\n *\n * @throws {ExecStageNotComposed}, {ExecStageNotDeployed}, {ExecAppNotInStack},\n * {ExecNotConfirmed}, {ComposeServerUnreachable}\n */\nexport async function execOnStage(\n\tcommandArgs: readonly [string, ...string[]],\n\toptions: StageExecOptions,\n\tdeps: StageExecDeps = defaultStageExecDeps,\n): Promise<number> {\n\tconst { stage } = options;\n\tconst loaded = await loadStage(options, deps.env);\n\tconst { stack, appName, protectedStage } = loaded;\n\tconst env = appEnvironment(stack, appName);\n\n\tif (protectedStage && !options.yes) {\n\t\tconst question = `'${stage}' is a protected stage. Run ${commandArgs.join(' ')} against it?`;\n\t\tif (!(await deps.confirm(question))) throw new ExecNotConfirmed(stage);\n\t}\n\n\t// Only what reaches a service on the stack needs the server: a site's or\n\t// a mobile app's values are public addresses, and need no SSH at all.\n\tconst addresses = stackAddresses(env, stack);\n\tif (addresses.length === 0) {\n\t\tlogger.log(`📦 App: ${appName} on '${stage}'`);\n\t\tlogger.log(`🔐 Loaded ${Object.keys(env).length} value(s) for '${stage}'`);\n\t\treturn run(commandArgs, env, options.cwd);\n\t}\n\n\tconst server = stageServer(loaded, options);\n\tlogger.log(`📦 App: ${appName} on '${stage}' (${server.host})`);\n\n\t// The deploy's own check: SSH logs in and Docker answers, before any\n\t// tunnel is asked for — so a key the server refuses is said as that.\n\tawait deps.server.check(server);\n\n\tconst tunnels: Tunnel[] = [];\n\tconst ports = new Map<string, number>();\n\ttry {\n\t\tfor (const address of addresses) {\n\t\t\tconst published = await deps.docker.published(\n\t\t\t\t{ host: dockerHost(server) },\n\t\t\t\tstack.project,\n\t\t\t\taddress.service,\n\t\t\t\taddress.port,\n\t\t\t);\n\t\t\tif (published === undefined) {\n\t\t\t\tlogger.log(\n\t\t\t\t\t`⚠️ ${address.service} is not published on the server (no running ` +\n\t\t\t\t\t\t`${stack.project} ${address.service} publishes ${address.port}), so ` +\n\t\t\t\t\t\t'it is unreachable from here; its address is passed on unchanged.',\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst tunnel = await deps.server.tunnel(server, published);\n\t\t\ttunnels.push(tunnel);\n\t\t\tports.set(`${address.service}:${address.port}`, tunnel.port);\n\t\t\tlogger.log(`🚇 ${address.service} → 127.0.0.1:${tunnel.port} (over SSH)`);\n\t\t}\n\n\t\tconst credentials = throughTunnels(env, stack, ports);\n\t\tlogger.log(\n\t\t\t`🔐 Loaded ${Object.keys(credentials).length} value(s) for '${stage}'`,\n\t\t);\n\t\treturn await run(commandArgs, credentials, options.cwd);\n\t} finally {\n\t\tawait Promise.all(tunnels.map((tunnel) => tunnel.close()));\n\t}\n}\n\n/**\n * The command, with the values in its environment and its Credentials. A\n * Ctrl-C reaches it — it is in the terminal's process group — and this\n * process waits for it to exit rather than leaving before the tunnels close.\n */\nfunction run(\n\tcommandArgs: readonly [string, ...string[]],\n\tcredentials: Record<string, string>,\n\tcwd: string | undefined,\n): Promise<number> {\n\tconst [cmd, ...args] = commandArgs;\n\tlogger.log(`🚀 Running: ${commandArgs.join(' ')}`);\n\n\tconst inherited = (process.env.NODE_OPTIONS ?? '')\n\t\t.replace(/--import[= ]\\S+/g, '')\n\t\t.trim();\n\tconst nodeOptions = [inherited, credentialsImport(Object.keys(credentials))]\n\t\t.filter(Boolean)\n\t\t.join(' ');\n\n\tconst wait = () => {};\n\tprocess.on('SIGINT', wait);\n\tprocess.on('SIGTERM', wait);\n\treturn new Promise<number>((resolve) => {\n\t\tconst child = spawn(cmd, args, {\n\t\t\t...(cwd ? { cwd } : {}),\n\t\t\tstdio: 'inherit',\n\t\t\tenv: { ...process.env, ...credentials, NODE_OPTIONS: nodeOptions },\n\t\t});\n\t\tchild.on('close', (code, signal) => {\n\t\t\tresolve(code ?? 128 + (signal ? constants.signals[signal] : 0));\n\t\t});\n\t\tchild.on('error', (error) => {\n\t\t\tlogger.error(`Failed to run command: ${error.message}`);\n\t\t\tresolve(1);\n\t\t});\n\t}).finally(() => {\n\t\tprocess.off('SIGINT', wait);\n\t\tprocess.off('SIGTERM', wait);\n\t});\n}\n"],"mappings":"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"}
@@ -1,11 +1,11 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-BhDjJH2R.mjs";
2
2
  import { t as GkmError } from "./errors-D8Gwp5kU.mjs";
3
- import { C as dockerCompose, a as edgePorts, b as dockerHost, d as composeStack, g as edgeAlias, s as deployedStackSecrets, x as sshAccess, y as composeServer } from "./compose-CvHIjMDd.mjs";
3
+ import { C as dockerCompose, a as edgePorts, b as dockerHost, d as composeStack, g as edgeAlias, s as deployedStackSecrets, x as sshAccess, y as composeServer } from "./compose-CIzCcsoz.mjs";
4
4
  import { N as assertDeployedStage } from "./workspace-DBHF9Gba.mjs";
5
- import { z as deploysWithCompose } from "./snsSubscriptions-R_AKBZYK.mjs";
6
- import { Yt as secretsStoreFor, wt as constructGlobs } from "./seeds-Cfejy8wj.mjs";
5
+ import { z as deploysWithCompose } from "./snsSubscriptions-BxLJQH4u.mjs";
6
+ import { Yt as secretsStoreFor, wt as constructGlobs } from "./seeds-BX9pskrU.mjs";
7
7
  import { n as deployIdentity } from "./identity-CBXr4wNi.mjs";
8
- import { i as discover } from "./discover-CzGM5P3p.mjs";
8
+ import { i as discover } from "./discover-DOhYSKY_.mjs";
9
9
  import { NotInAnApp, loadWorkspaceAppInfo, loadWorkspaceConfig } from "./config.mjs";
10
10
  import { u as prompt } from "./index.mjs";
11
11
  import { constants } from "node:os";
@@ -299,4 +299,4 @@ function run(commandArgs, credentials, cwd) {
299
299
  //#endregion
300
300
  export { stage_exports as n, loadStage as t };
301
301
 
302
- //# sourceMappingURL=stage-DCaxqm-G.mjs.map
302
+ //# sourceMappingURL=stage-mta618Oy.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"stage-DCaxqm-G.mjs","names":[],"sources":["../src/exec/stage.ts"],"sourcesContent":["/**\n * `gkm exec --stage <stage> -- <command>`: a command run on this machine as\n * a deployed compose stage's app — a data import, a backfill, a report.\n *\n * The app's environment is the one its container runs with, derived the way\n * the deploy derives it (`composeStack`) from the stage's secrets, read from\n * the stage's store the way the deploy reads them. Only what the container\n * reaches on the stack's network is moved: each service it names that the\n * server publishes on its loopback — Postgres, MinIO — is reached through an\n * SSH tunnel on the deploy's own login (`ServerAccess.tunnel`), and each\n * sibling app through its public address.\n *\n * Nothing is written to disk. The values exist only in the command's\n * environment, and its Credentials are filled from there by a preload that\n * is a `data:` URL naming the keys — never their values.\n */\n\nimport { spawn } from 'node:child_process';\nimport { constants } from 'node:os';\nimport type { ConstructManifest } from '@geekmidas/manifest';\nimport { type ComposeDocker, dockerCompose } from '../compose/docker.js';\nimport { deployedStackSecrets } from '../compose/secrets.js';\nimport {\n\ttype ComposeServer,\n\tcomposeServer,\n\tdockerHost,\n\ttype ServerAccess,\n\tsshAccess,\n\ttype Tunnel,\n} from '../compose/server.js';\nimport { type ComposeStack, composeStack } from '../compose/stack.js';\nimport { edgeAlias } from '../compose/traefik.js';\nimport {\n\tloadWorkspaceAppInfo,\n\tloadWorkspaceConfig,\n\tNotInAnApp,\n} from '../config.js';\nimport { deployIdentity } from '../deploy/identity.js';\nimport { GkmError } from '../errors';\nimport { prompt } from '../prompt.js';\nimport { deploysWithCompose } from '../providers/dns.js';\nimport { discover } from '../reconcile/discover.js';\nimport { constructGlobs } from '../reconcile/workspace.js';\nimport { secretsStoreFor } from '../secrets/store.js';\nimport type { StageSecrets } from '../secrets/types.js';\nimport { edgePorts } from '../target/compose/phases.js';\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\n\nconst logger = console;\n\n/** The stage is not one the compose target deploys. */\nexport class ExecStageNotComposed extends GkmError {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`gkm exec --stage runs a command against a compose stage's server, and ` +\n\t\t\t\t`'${stage}' is not deployed with compose. Set deploy.default: 'compose' ` +\n\t\t\t\t'in gkm.config.ts, or run the command where the stage runs.',\n\t\t);\n\t\tthis.name = 'ExecStageNotComposed';\n\t}\n}\n\n/** The stage's secrets lack what its deploy generates: it was never deployed. */\nexport class ExecStageNotDeployed extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly missing: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`'${stage}' has no ${missing.join(', ')} in its secrets yet, so what its ` +\n\t\t\t\t'apps run with is not decided. Deploy it first: ' +\n\t\t\t\t`gkm deploy --stage ${stage}.`,\n\t\t);\n\t\tthis.name = 'ExecStageNotDeployed';\n\t}\n}\n\n/** The directory is not one of the stack's apps. */\nexport class ExecAppNotInStack extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly app: string | undefined,\n\t\treadonly apps: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`${app ? `'${app}' is not` : 'This directory is not in'} one of the apps ` +\n\t\t\t\t`'${stage}' runs (${apps.join(', ')}), so there is no environment to run ` +\n\t\t\t\t`as. Run from an app's directory: cd apps/${apps[0] ?? '<app>'} && ` +\n\t\t\t\t`gkm exec --stage ${stage} -- <command>.`,\n\t\t);\n\t\tthis.name = 'ExecAppNotInStack';\n\t}\n}\n\n/** A protected stage, and nobody said yes. */\nexport class ExecNotConfirmed extends GkmError {\n\tconstructor(readonly stage: string) {\n\t\tsuper(`Nothing was run against '${stage}'.`);\n\t\tthis.name = 'ExecNotConfirmed';\n\t}\n}\n\nexport interface StageExecOptions {\n\tstage: string;\n\t/** The AWS profile for the stage's account, where its secrets are kept. */\n\tprofile?: string;\n\t/**\n\t * The SSH login to reach the server as, in place of\n\t * `deploy.compose.server.<stage>.user` — a person's own, where the\n\t * deploy's key lives only in CI.\n\t */\n\tsshUser?: string;\n\t/** Run against a protected stage without asking. */\n\tyes?: boolean;\n\tcwd?: string;\n}\n\n/** What the command needs from outside the process, so tests can stand in. */\nexport interface StageExecDeps {\n\t/** The server's Docker, asked which port each service publishes. */\n\tdocker: ComposeDocker;\n\t/** The tunnels to the server's loopback. */\n\tserver: ServerAccess;\n\t/** Asked before running against a protected stage. */\n\tconfirm: (question: string) => Promise<boolean>;\n\t/** Where the edge's ports are read from, as the deploy reads them. */\n\tenv: NodeJS.ProcessEnv;\n}\n\nexport const defaultStageExecDeps: StageExecDeps = {\n\tdocker: dockerCompose,\n\tserver: sshAccess,\n\tconfirm: async (question) => {\n\t\tconst answer = await prompt(`${question} [y/N] `, {\n\t\t\tinstead: 'Pass --yes to run without asking.',\n\t\t});\n\t\treturn /^y(es)?$/i.test(answer.trim());\n\t},\n\tenv: process.env,\n};\n\n/** A service on the stack's network the app's environment names. */\nexport interface StackAddress {\n\tservice: string;\n\tport: number;\n}\n\n/**\n * The environment an app runs with on the stage: a backend's container's,\n * over the keys of each worker built from it; a site's or a mobile app's,\n * the public values its build reads.\n *\n * @throws {ExecAppNotInStack}\n */\nexport function appEnvironment(\n\tstack: Pick<ComposeStack, 'stage' | 'apps' | 'workers' | 'mobileApps'>,\n\tappName: string | undefined,\n): Record<string, string> {\n\tconst mobile = stack.mobileApps.find(\n\t\t(candidate) => candidate.name === appName,\n\t);\n\tif (mobile) return { ...mobile.values };\n\tconst app = stack.apps.find((candidate) => candidate.name === appName);\n\tif (!app) {\n\t\tthrow new ExecAppNotInStack(stack.stage, appName, [\n\t\t\t...stack.apps.map((candidate) => candidate.name),\n\t\t\t...stack.mobileApps.map((candidate) => candidate.name),\n\t\t]);\n\t}\n\tif (app.kind === 'site') return { ...app.build?.args };\n\tconst workers = stack.workers.filter((worker) => worker.host === app.name);\n\treturn Object.assign(\n\t\t{},\n\t\t...workers.map((worker) => worker.env ?? {}),\n\t\tapp.env ?? {},\n\t);\n}\n\n/** `host:port`, at a URL's authority — after `//` or a login's `@`. */\nfunction authority(host: string, port: number): RegExp {\n\treturn new RegExp(\n\t\t`(?<=[/@,])${host.replace(/[.-]/g, '\\\\$&')}:${port}(?!\\\\d)`,\n\t\t'g',\n\t);\n}\n\n/**\n * Each of the stack's services the environment reaches on the stack's\n * network, once: `postgres:5432`, `minio:9000`.\n */\nexport function stackAddresses(\n\tenv: Readonly<Record<string, string>>,\n\tstack: Pick<ComposeStack, 'infra'>,\n): StackAddress[] {\n\tconst found = new Map<string, StackAddress>();\n\tconst pattern = /(?<=[/@,])([a-z0-9-]+):(\\d+)(?!\\d)/g;\n\tfor (const value of Object.values(env)) {\n\t\tfor (const match of value.matchAll(pattern)) {\n\t\t\tconst service = match[1]!;\n\t\t\tif (!stack.infra.includes(service)) continue;\n\t\t\tconst port = Number(match[2]);\n\t\t\tfound.set(`${service}:${port}`, { service, port });\n\t\t}\n\t}\n\treturn [...found.values()];\n}\n\n/**\n * The environment as this machine reaches it: each service at its tunnel's\n * local port, and each sibling app at its public address. What has no\n * tunnel is left as the container has it.\n */\nexport function throughTunnels(\n\tenv: Readonly<Record<string, string>>,\n\tstack: Pick<ComposeStack, 'apps' | 'project'>,\n\ttunnels: ReadonlyMap<string, number>,\n): Record<string, string> {\n\tconst rewrites: [RegExp, string][] = [];\n\tfor (const [address, port] of tunnels) {\n\t\tconst [service, inside] = address.split(':');\n\t\trewrites.push([authority(service!, Number(inside)), `127.0.0.1:${port}`]);\n\t}\n\tfor (const app of stack.apps) {\n\t\tfor (const host of [app.name, edgeAlias(stack.project, app.name)]) {\n\t\t\trewrites.push([\n\t\t\t\tnew RegExp(\n\t\t\t\t\t`http://${host.replace(/[.-]/g, '\\\\$&')}:${app.port}(?!\\\\d)`,\n\t\t\t\t\t'g',\n\t\t\t\t),\n\t\t\t\tapp.url,\n\t\t\t]);\n\t\t}\n\t}\n\tconst out: Record<string, string> = {};\n\tfor (const [key, value] of Object.entries(env)) {\n\t\tout[key] = rewrites.reduce(\n\t\t\t(current, [pattern, replacement]) =>\n\t\t\t\tcurrent.replace(pattern, () => replacement),\n\t\t\tvalue,\n\t\t);\n\t}\n\treturn out;\n}\n\n/**\n * The preload that fills Credentials from the command's own environment: a\n * `data:` URL holding the keys, so no file — and no value — is written.\n */\nexport function credentialsImport(keys: readonly string[]): string {\n\tconst source =\n\t\t'globalThis.__gkm_credentials__=Object.fromEntries(' +\n\t\t`${JSON.stringify(keys)}.map((k)=>[k,process.env[k]]));`;\n\treturn `--import=data:text/javascript,${encodeURIComponent(source)}`;\n}\n\n/** A deployed compose stage, as its deploy has it, and the app here. */\nexport interface LoadedStage {\n\tworkspace: NormalizedWorkspace;\n\tmanifest: ConstructManifest;\n\tstack: ComposeStack;\n\t/** The stage's secrets, as its deploy reads them. */\n\tsecrets: StageSecrets;\n\t/** The app this directory is, if it is one. */\n\tappName: string | undefined;\n\tprotectedStage: boolean;\n}\n\n/**\n * The stage's stack, and the app this directory is, as the deploy has them:\n * its secrets read from its store, its values derived as the deploy derives\n * them. Nothing is reached but the store — no server, no SSH.\n *\n * @throws {UndeclaredStage}, {ExecStageNotComposed}, {ExecStageNotDeployed}\n */\nexport async function loadStage(\n\toptions: Pick<StageExecOptions, 'stage' | 'profile' | 'cwd'>,\n\tenv: NodeJS.ProcessEnv = process.env,\n): Promise<LoadedStage> {\n\tconst cwd = options.cwd ?? process.cwd();\n\tconst { stage } = options;\n\t// Outside an app the workspace is still loaded, so the stage is checked\n\t// before the directory is refused.\n\tconst info = await loadWorkspaceAppInfo(cwd).catch((error) => {\n\t\tif (error instanceof NotInAnApp) return undefined;\n\t\tthrow error;\n\t});\n\tconst workspace =\n\t\tinfo?.workspace ?? (await loadWorkspaceConfig(cwd)).workspace;\n\n\tassertDeployedStage(workspace.stages, stage);\n\tif (!deploysWithCompose(workspace)) throw new ExecStageNotComposed(stage);\n\n\tconst runnables: Record<string, string[]> = {};\n\tconst background: Record<string, string[]> = {};\n\tconst manifest: ConstructManifest = await discover({\n\t\tpatterns: constructGlobs(workspace),\n\t\tcwd: workspace.root,\n\t\trunnables,\n\t\tbackground,\n\t});\n\n\tconst store = await secretsStoreFor(workspace, stage, {\n\t\t...(options.profile ? { profile: options.profile } : {}),\n\t});\n\tconst { secrets, generated } = deployedStackSecrets(\n\t\tworkspace,\n\t\tstage,\n\t\tawait store.read(stage),\n\t\tmanifest,\n\t);\n\tif (generated.length > 0) throw new ExecStageNotDeployed(stage, generated);\n\n\tconst stack = composeStack({\n\t\tworkspace,\n\t\tmanifest,\n\t\trunnables,\n\t\tbackground,\n\t\tstage,\n\t\tidentity: deployIdentity(workspace, stage),\n\t\t// Built, so a site's public values are resolved as its build has them.\n\t\timages: { mode: 'build', tag: 'exec' },\n\t\tsecrets,\n\t\tports: edgePorts(env),\n\t});\n\treturn {\n\t\tworkspace,\n\t\tmanifest,\n\t\tstack,\n\t\tsecrets,\n\t\tappName: info?.appName,\n\t\tprotectedStage: workspace.stages.protected?.includes(stage) ?? false,\n\t};\n}\n\n/** The stage's server, as SSH reaches it: `--ssh-user` over the config's. */\nfunction stageServer(\n\tloaded: LoadedStage,\n\toptions: StageExecOptions,\n): ComposeServer {\n\tconst server = composeServer({\n\t\tstage: options.stage,\n\t\tlocal: false,\n\t\tconfig: loaded.workspace.deploy?.compose?.server,\n\t\tcustom: loaded.secrets.custom,\n\t})!;\n\tif (options.sshUser) server.user = options.sshUser;\n\treturn server;\n}\n\n/**\n * Run `commandArgs` as the app this directory is, on the stage — through a\n * tunnel to each service it reaches — and return its exit code. Every tunnel\n * is closed once it exits, whatever it exits with.\n *\n * @throws {ExecStageNotComposed}, {ExecStageNotDeployed}, {ExecAppNotInStack},\n * {ExecNotConfirmed}, {ComposeServerUnreachable}\n */\nexport async function execOnStage(\n\tcommandArgs: readonly [string, ...string[]],\n\toptions: StageExecOptions,\n\tdeps: StageExecDeps = defaultStageExecDeps,\n): Promise<number> {\n\tconst { stage } = options;\n\tconst loaded = await loadStage(options, deps.env);\n\tconst { stack, appName, protectedStage } = loaded;\n\tconst env = appEnvironment(stack, appName);\n\n\tif (protectedStage && !options.yes) {\n\t\tconst question = `'${stage}' is a protected stage. Run ${commandArgs.join(' ')} against it?`;\n\t\tif (!(await deps.confirm(question))) throw new ExecNotConfirmed(stage);\n\t}\n\n\t// Only what reaches a service on the stack needs the server: a site's or\n\t// a mobile app's values are public addresses, and need no SSH at all.\n\tconst addresses = stackAddresses(env, stack);\n\tif (addresses.length === 0) {\n\t\tlogger.log(`📦 App: ${appName} on '${stage}'`);\n\t\tlogger.log(`🔐 Loaded ${Object.keys(env).length} value(s) for '${stage}'`);\n\t\treturn run(commandArgs, env, options.cwd);\n\t}\n\n\tconst server = stageServer(loaded, options);\n\tlogger.log(`📦 App: ${appName} on '${stage}' (${server.host})`);\n\n\t// The deploy's own check: SSH logs in and Docker answers, before any\n\t// tunnel is asked for — so a key the server refuses is said as that.\n\tawait deps.server.check(server);\n\n\tconst tunnels: Tunnel[] = [];\n\tconst ports = new Map<string, number>();\n\ttry {\n\t\tfor (const address of addresses) {\n\t\t\tconst published = await deps.docker.published(\n\t\t\t\t{ host: dockerHost(server) },\n\t\t\t\tstack.project,\n\t\t\t\taddress.service,\n\t\t\t\taddress.port,\n\t\t\t);\n\t\t\tif (published === undefined) {\n\t\t\t\tlogger.log(\n\t\t\t\t\t`⚠️ ${address.service} is not published on the server (no running ` +\n\t\t\t\t\t\t`${stack.project} ${address.service} publishes ${address.port}), so ` +\n\t\t\t\t\t\t'it is unreachable from here; its address is passed on unchanged.',\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst tunnel = await deps.server.tunnel(server, published);\n\t\t\ttunnels.push(tunnel);\n\t\t\tports.set(`${address.service}:${address.port}`, tunnel.port);\n\t\t\tlogger.log(`🚇 ${address.service} → 127.0.0.1:${tunnel.port} (over SSH)`);\n\t\t}\n\n\t\tconst credentials = throughTunnels(env, stack, ports);\n\t\tlogger.log(\n\t\t\t`🔐 Loaded ${Object.keys(credentials).length} value(s) for '${stage}'`,\n\t\t);\n\t\treturn await run(commandArgs, credentials, options.cwd);\n\t} finally {\n\t\tawait Promise.all(tunnels.map((tunnel) => tunnel.close()));\n\t}\n}\n\n/**\n * The command, with the values in its environment and its Credentials. A\n * Ctrl-C reaches it — it is in the terminal's process group — and this\n * process waits for it to exit rather than leaving before the tunnels close.\n */\nfunction run(\n\tcommandArgs: readonly [string, ...string[]],\n\tcredentials: Record<string, string>,\n\tcwd: string | undefined,\n): Promise<number> {\n\tconst [cmd, ...args] = commandArgs;\n\tlogger.log(`🚀 Running: ${commandArgs.join(' ')}`);\n\n\tconst inherited = (process.env.NODE_OPTIONS ?? '')\n\t\t.replace(/--import[= ]\\S+/g, '')\n\t\t.trim();\n\tconst nodeOptions = [inherited, credentialsImport(Object.keys(credentials))]\n\t\t.filter(Boolean)\n\t\t.join(' ');\n\n\tconst wait = () => {};\n\tprocess.on('SIGINT', wait);\n\tprocess.on('SIGTERM', wait);\n\treturn new Promise<number>((resolve) => {\n\t\tconst child = spawn(cmd, args, {\n\t\t\t...(cwd ? { cwd } : {}),\n\t\t\tstdio: 'inherit',\n\t\t\tenv: { ...process.env, ...credentials, NODE_OPTIONS: nodeOptions },\n\t\t});\n\t\tchild.on('close', (code, signal) => {\n\t\t\tresolve(code ?? 128 + (signal ? constants.signals[signal] : 0));\n\t\t});\n\t\tchild.on('error', (error) => {\n\t\t\tlogger.error(`Failed to run command: ${error.message}`);\n\t\t\tresolve(1);\n\t\t});\n\t}).finally(() => {\n\t\tprocess.off('SIGINT', wait);\n\t\tprocess.off('SIGTERM', wait);\n\t});\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"stage-mta618Oy.mjs","names":[],"sources":["../src/exec/stage.ts"],"sourcesContent":["/**\n * `gkm exec --stage <stage> -- <command>`: a command run on this machine as\n * a deployed compose stage's app — a data import, a backfill, a report.\n *\n * The app's environment is the one its container runs with, derived the way\n * the deploy derives it (`composeStack`) from the stage's secrets, read from\n * the stage's store the way the deploy reads them. Only what the container\n * reaches on the stack's network is moved: each service it names that the\n * server publishes on its loopback — Postgres, MinIO — is reached through an\n * SSH tunnel on the deploy's own login (`ServerAccess.tunnel`), and each\n * sibling app through its public address.\n *\n * Nothing is written to disk. The values exist only in the command's\n * environment, and its Credentials are filled from there by a preload that\n * is a `data:` URL naming the keys — never their values.\n */\n\nimport { spawn } from 'node:child_process';\nimport { constants } from 'node:os';\nimport type { ConstructManifest } from '@geekmidas/manifest';\nimport { type ComposeDocker, dockerCompose } from '../compose/docker.js';\nimport { deployedStackSecrets } from '../compose/secrets.js';\nimport {\n\ttype ComposeServer,\n\tcomposeServer,\n\tdockerHost,\n\ttype ServerAccess,\n\tsshAccess,\n\ttype Tunnel,\n} from '../compose/server.js';\nimport { type ComposeStack, composeStack } from '../compose/stack.js';\nimport { edgeAlias } from '../compose/traefik.js';\nimport {\n\tloadWorkspaceAppInfo,\n\tloadWorkspaceConfig,\n\tNotInAnApp,\n} from '../config.js';\nimport { deployIdentity } from '../deploy/identity.js';\nimport { GkmError } from '../errors';\nimport { prompt } from '../prompt.js';\nimport { deploysWithCompose } from '../providers/dns.js';\nimport { discover } from '../reconcile/discover.js';\nimport { constructGlobs } from '../reconcile/workspace.js';\nimport { secretsStoreFor } from '../secrets/store.js';\nimport type { StageSecrets } from '../secrets/types.js';\nimport { edgePorts } from '../target/compose/phases.js';\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\n\nconst logger = console;\n\n/** The stage is not one the compose target deploys. */\nexport class ExecStageNotComposed extends GkmError {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`gkm exec --stage runs a command against a compose stage's server, and ` +\n\t\t\t\t`'${stage}' is not deployed with compose. Set deploy.default: 'compose' ` +\n\t\t\t\t'in gkm.config.ts, or run the command where the stage runs.',\n\t\t);\n\t\tthis.name = 'ExecStageNotComposed';\n\t}\n}\n\n/** The stage's secrets lack what its deploy generates: it was never deployed. */\nexport class ExecStageNotDeployed extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly missing: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`'${stage}' has no ${missing.join(', ')} in its secrets yet, so what its ` +\n\t\t\t\t'apps run with is not decided. Deploy it first: ' +\n\t\t\t\t`gkm deploy --stage ${stage}.`,\n\t\t);\n\t\tthis.name = 'ExecStageNotDeployed';\n\t}\n}\n\n/** The directory is not one of the stack's apps. */\nexport class ExecAppNotInStack extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly app: string | undefined,\n\t\treadonly apps: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`${app ? `'${app}' is not` : 'This directory is not in'} one of the apps ` +\n\t\t\t\t`'${stage}' runs (${apps.join(', ')}), so there is no environment to run ` +\n\t\t\t\t`as. Run from an app's directory: cd apps/${apps[0] ?? '<app>'} && ` +\n\t\t\t\t`gkm exec --stage ${stage} -- <command>.`,\n\t\t);\n\t\tthis.name = 'ExecAppNotInStack';\n\t}\n}\n\n/** A protected stage, and nobody said yes. */\nexport class ExecNotConfirmed extends GkmError {\n\tconstructor(readonly stage: string) {\n\t\tsuper(`Nothing was run against '${stage}'.`);\n\t\tthis.name = 'ExecNotConfirmed';\n\t}\n}\n\nexport interface StageExecOptions {\n\tstage: string;\n\t/** The AWS profile for the stage's account, where its secrets are kept. */\n\tprofile?: string;\n\t/**\n\t * The SSH login to reach the server as, in place of\n\t * `deploy.compose.server.<stage>.user` — a person's own, where the\n\t * deploy's key lives only in CI.\n\t */\n\tsshUser?: string;\n\t/** Run against a protected stage without asking. */\n\tyes?: boolean;\n\tcwd?: string;\n}\n\n/** What the command needs from outside the process, so tests can stand in. */\nexport interface StageExecDeps {\n\t/** The server's Docker, asked which port each service publishes. */\n\tdocker: ComposeDocker;\n\t/** The tunnels to the server's loopback. */\n\tserver: ServerAccess;\n\t/** Asked before running against a protected stage. */\n\tconfirm: (question: string) => Promise<boolean>;\n\t/** Where the edge's ports are read from, as the deploy reads them. */\n\tenv: NodeJS.ProcessEnv;\n}\n\nexport const defaultStageExecDeps: StageExecDeps = {\n\tdocker: dockerCompose,\n\tserver: sshAccess,\n\tconfirm: async (question) => {\n\t\tconst answer = await prompt(`${question} [y/N] `, {\n\t\t\tinstead: 'Pass --yes to run without asking.',\n\t\t});\n\t\treturn /^y(es)?$/i.test(answer.trim());\n\t},\n\tenv: process.env,\n};\n\n/** A service on the stack's network the app's environment names. */\nexport interface StackAddress {\n\tservice: string;\n\tport: number;\n}\n\n/**\n * The environment an app runs with on the stage: a backend's container's,\n * over the keys of each worker built from it; a site's or a mobile app's,\n * the public values its build reads.\n *\n * @throws {ExecAppNotInStack}\n */\nexport function appEnvironment(\n\tstack: Pick<ComposeStack, 'stage' | 'apps' | 'workers' | 'mobileApps'>,\n\tappName: string | undefined,\n): Record<string, string> {\n\tconst mobile = stack.mobileApps.find(\n\t\t(candidate) => candidate.name === appName,\n\t);\n\tif (mobile) return { ...mobile.values };\n\tconst app = stack.apps.find((candidate) => candidate.name === appName);\n\tif (!app) {\n\t\tthrow new ExecAppNotInStack(stack.stage, appName, [\n\t\t\t...stack.apps.map((candidate) => candidate.name),\n\t\t\t...stack.mobileApps.map((candidate) => candidate.name),\n\t\t]);\n\t}\n\tif (app.kind === 'site') return { ...app.build?.args };\n\tconst workers = stack.workers.filter((worker) => worker.host === app.name);\n\treturn Object.assign(\n\t\t{},\n\t\t...workers.map((worker) => worker.env ?? {}),\n\t\tapp.env ?? {},\n\t);\n}\n\n/** `host:port`, at a URL's authority — after `//` or a login's `@`. */\nfunction authority(host: string, port: number): RegExp {\n\treturn new RegExp(\n\t\t`(?<=[/@,])${host.replace(/[.-]/g, '\\\\$&')}:${port}(?!\\\\d)`,\n\t\t'g',\n\t);\n}\n\n/**\n * Each of the stack's services the environment reaches on the stack's\n * network, once: `postgres:5432`, `minio:9000`.\n */\nexport function stackAddresses(\n\tenv: Readonly<Record<string, string>>,\n\tstack: Pick<ComposeStack, 'infra'>,\n): StackAddress[] {\n\tconst found = new Map<string, StackAddress>();\n\tconst pattern = /(?<=[/@,])([a-z0-9-]+):(\\d+)(?!\\d)/g;\n\tfor (const value of Object.values(env)) {\n\t\tfor (const match of value.matchAll(pattern)) {\n\t\t\tconst service = match[1]!;\n\t\t\tif (!stack.infra.includes(service)) continue;\n\t\t\tconst port = Number(match[2]);\n\t\t\tfound.set(`${service}:${port}`, { service, port });\n\t\t}\n\t}\n\treturn [...found.values()];\n}\n\n/**\n * The environment as this machine reaches it: each service at its tunnel's\n * local port, and each sibling app at its public address. What has no\n * tunnel is left as the container has it.\n */\nexport function throughTunnels(\n\tenv: Readonly<Record<string, string>>,\n\tstack: Pick<ComposeStack, 'apps' | 'project'>,\n\ttunnels: ReadonlyMap<string, number>,\n): Record<string, string> {\n\tconst rewrites: [RegExp, string][] = [];\n\tfor (const [address, port] of tunnels) {\n\t\tconst [service, inside] = address.split(':');\n\t\trewrites.push([authority(service!, Number(inside)), `127.0.0.1:${port}`]);\n\t}\n\tfor (const app of stack.apps) {\n\t\tfor (const host of [app.name, edgeAlias(stack.project, app.name)]) {\n\t\t\trewrites.push([\n\t\t\t\tnew RegExp(\n\t\t\t\t\t`http://${host.replace(/[.-]/g, '\\\\$&')}:${app.port}(?!\\\\d)`,\n\t\t\t\t\t'g',\n\t\t\t\t),\n\t\t\t\tapp.url,\n\t\t\t]);\n\t\t}\n\t}\n\tconst out: Record<string, string> = {};\n\tfor (const [key, value] of Object.entries(env)) {\n\t\tout[key] = rewrites.reduce(\n\t\t\t(current, [pattern, replacement]) =>\n\t\t\t\tcurrent.replace(pattern, () => replacement),\n\t\t\tvalue,\n\t\t);\n\t}\n\treturn out;\n}\n\n/**\n * The preload that fills Credentials from the command's own environment: a\n * `data:` URL holding the keys, so no file — and no value — is written.\n */\nexport function credentialsImport(keys: readonly string[]): string {\n\tconst source =\n\t\t'globalThis.__gkm_credentials__=Object.fromEntries(' +\n\t\t`${JSON.stringify(keys)}.map((k)=>[k,process.env[k]]));`;\n\treturn `--import=data:text/javascript,${encodeURIComponent(source)}`;\n}\n\n/** A deployed compose stage, as its deploy has it, and the app here. */\nexport interface LoadedStage {\n\tworkspace: NormalizedWorkspace;\n\tmanifest: ConstructManifest;\n\tstack: ComposeStack;\n\t/** The stage's secrets, as its deploy reads them. */\n\tsecrets: StageSecrets;\n\t/** The app this directory is, if it is one. */\n\tappName: string | undefined;\n\tprotectedStage: boolean;\n}\n\n/**\n * The stage's stack, and the app this directory is, as the deploy has them:\n * its secrets read from its store, its values derived as the deploy derives\n * them. Nothing is reached but the store — no server, no SSH.\n *\n * @throws {UndeclaredStage}, {ExecStageNotComposed}, {ExecStageNotDeployed}\n */\nexport async function loadStage(\n\toptions: Pick<StageExecOptions, 'stage' | 'profile' | 'cwd'>,\n\tenv: NodeJS.ProcessEnv = process.env,\n): Promise<LoadedStage> {\n\tconst cwd = options.cwd ?? process.cwd();\n\tconst { stage } = options;\n\t// Outside an app the workspace is still loaded, so the stage is checked\n\t// before the directory is refused.\n\tconst info = await loadWorkspaceAppInfo(cwd).catch((error) => {\n\t\tif (error instanceof NotInAnApp) return undefined;\n\t\tthrow error;\n\t});\n\tconst workspace =\n\t\tinfo?.workspace ?? (await loadWorkspaceConfig(cwd)).workspace;\n\n\tassertDeployedStage(workspace.stages, stage);\n\tif (!deploysWithCompose(workspace)) throw new ExecStageNotComposed(stage);\n\n\tconst runnables: Record<string, string[]> = {};\n\tconst background: Record<string, string[]> = {};\n\tconst manifest: ConstructManifest = await discover({\n\t\tpatterns: constructGlobs(workspace),\n\t\tcwd: workspace.root,\n\t\trunnables,\n\t\tbackground,\n\t});\n\n\tconst store = await secretsStoreFor(workspace, stage, {\n\t\t...(options.profile ? { profile: options.profile } : {}),\n\t});\n\tconst { secrets, generated } = deployedStackSecrets(\n\t\tworkspace,\n\t\tstage,\n\t\tawait store.read(stage),\n\t\tmanifest,\n\t);\n\tif (generated.length > 0) throw new ExecStageNotDeployed(stage, generated);\n\n\tconst stack = composeStack({\n\t\tworkspace,\n\t\tmanifest,\n\t\trunnables,\n\t\tbackground,\n\t\tstage,\n\t\tidentity: deployIdentity(workspace, stage),\n\t\t// Built, so a site's public values are resolved as its build has them.\n\t\timages: { mode: 'build', tag: 'exec' },\n\t\tsecrets,\n\t\tports: edgePorts(env),\n\t});\n\treturn {\n\t\tworkspace,\n\t\tmanifest,\n\t\tstack,\n\t\tsecrets,\n\t\tappName: info?.appName,\n\t\tprotectedStage: workspace.stages.protected?.includes(stage) ?? false,\n\t};\n}\n\n/** The stage's server, as SSH reaches it: `--ssh-user` over the config's. */\nfunction stageServer(\n\tloaded: LoadedStage,\n\toptions: StageExecOptions,\n): ComposeServer {\n\tconst server = composeServer({\n\t\tstage: options.stage,\n\t\tlocal: false,\n\t\tconfig: loaded.workspace.deploy?.compose?.server,\n\t\tcustom: loaded.secrets.custom,\n\t})!;\n\tif (options.sshUser) server.user = options.sshUser;\n\treturn server;\n}\n\n/**\n * Run `commandArgs` as the app this directory is, on the stage — through a\n * tunnel to each service it reaches — and return its exit code. Every tunnel\n * is closed once it exits, whatever it exits with.\n *\n * @throws {ExecStageNotComposed}, {ExecStageNotDeployed}, {ExecAppNotInStack},\n * {ExecNotConfirmed}, {ComposeServerUnreachable}\n */\nexport async function execOnStage(\n\tcommandArgs: readonly [string, ...string[]],\n\toptions: StageExecOptions,\n\tdeps: StageExecDeps = defaultStageExecDeps,\n): Promise<number> {\n\tconst { stage } = options;\n\tconst loaded = await loadStage(options, deps.env);\n\tconst { stack, appName, protectedStage } = loaded;\n\tconst env = appEnvironment(stack, appName);\n\n\tif (protectedStage && !options.yes) {\n\t\tconst question = `'${stage}' is a protected stage. Run ${commandArgs.join(' ')} against it?`;\n\t\tif (!(await deps.confirm(question))) throw new ExecNotConfirmed(stage);\n\t}\n\n\t// Only what reaches a service on the stack needs the server: a site's or\n\t// a mobile app's values are public addresses, and need no SSH at all.\n\tconst addresses = stackAddresses(env, stack);\n\tif (addresses.length === 0) {\n\t\tlogger.log(`📦 App: ${appName} on '${stage}'`);\n\t\tlogger.log(`🔐 Loaded ${Object.keys(env).length} value(s) for '${stage}'`);\n\t\treturn run(commandArgs, env, options.cwd);\n\t}\n\n\tconst server = stageServer(loaded, options);\n\tlogger.log(`📦 App: ${appName} on '${stage}' (${server.host})`);\n\n\t// The deploy's own check: SSH logs in and Docker answers, before any\n\t// tunnel is asked for — so a key the server refuses is said as that.\n\tawait deps.server.check(server);\n\n\tconst tunnels: Tunnel[] = [];\n\tconst ports = new Map<string, number>();\n\ttry {\n\t\tfor (const address of addresses) {\n\t\t\tconst published = await deps.docker.published(\n\t\t\t\t{ host: dockerHost(server) },\n\t\t\t\tstack.project,\n\t\t\t\taddress.service,\n\t\t\t\taddress.port,\n\t\t\t);\n\t\t\tif (published === undefined) {\n\t\t\t\tlogger.log(\n\t\t\t\t\t`⚠️ ${address.service} is not published on the server (no running ` +\n\t\t\t\t\t\t`${stack.project} ${address.service} publishes ${address.port}), so ` +\n\t\t\t\t\t\t'it is unreachable from here; its address is passed on unchanged.',\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst tunnel = await deps.server.tunnel(server, published);\n\t\t\ttunnels.push(tunnel);\n\t\t\tports.set(`${address.service}:${address.port}`, tunnel.port);\n\t\t\tlogger.log(`🚇 ${address.service} → 127.0.0.1:${tunnel.port} (over SSH)`);\n\t\t}\n\n\t\tconst credentials = throughTunnels(env, stack, ports);\n\t\tlogger.log(\n\t\t\t`🔐 Loaded ${Object.keys(credentials).length} value(s) for '${stage}'`,\n\t\t);\n\t\treturn await run(commandArgs, credentials, options.cwd);\n\t} finally {\n\t\tawait Promise.all(tunnels.map((tunnel) => tunnel.close()));\n\t}\n}\n\n/**\n * The command, with the values in its environment and its Credentials. A\n * Ctrl-C reaches it — it is in the terminal's process group — and this\n * process waits for it to exit rather than leaving before the tunnels close.\n */\nfunction run(\n\tcommandArgs: readonly [string, ...string[]],\n\tcredentials: Record<string, string>,\n\tcwd: string | undefined,\n): Promise<number> {\n\tconst [cmd, ...args] = commandArgs;\n\tlogger.log(`🚀 Running: ${commandArgs.join(' ')}`);\n\n\tconst inherited = (process.env.NODE_OPTIONS ?? '')\n\t\t.replace(/--import[= ]\\S+/g, '')\n\t\t.trim();\n\tconst nodeOptions = [inherited, credentialsImport(Object.keys(credentials))]\n\t\t.filter(Boolean)\n\t\t.join(' ');\n\n\tconst wait = () => {};\n\tprocess.on('SIGINT', wait);\n\tprocess.on('SIGTERM', wait);\n\treturn new Promise<number>((resolve) => {\n\t\tconst child = spawn(cmd, args, {\n\t\t\t...(cwd ? { cwd } : {}),\n\t\t\tstdio: 'inherit',\n\t\t\tenv: { ...process.env, ...credentials, NODE_OPTIONS: nodeOptions },\n\t\t});\n\t\tchild.on('close', (code, signal) => {\n\t\t\tresolve(code ?? 128 + (signal ? constants.signals[signal] : 0));\n\t\t});\n\t\tchild.on('error', (error) => {\n\t\t\tlogger.error(`Failed to run command: ${error.message}`);\n\t\t\tresolve(1);\n\t\t});\n\t}).finally(() => {\n\t\tprocess.off('SIGINT', wait);\n\t\tprocess.off('SIGTERM', wait);\n\t});\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiDA,MAAM,SAAS;;AAGf,IAAa,uBAAb,cAA0C,SAAS;CAC7B;CAArB,YAAY,OAAwB;EACnC,MACC,0EACK,MAAM,yHAEZ;EALoB,KAAA,QAAA;EAMpB,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,uBAAb,cAA0C,SAAS;CAExC;CACA;CAFV,YACC,OACA,SACC;EACD,MACC,IAAI,MAAM,WAAW,QAAQ,KAAK,IAAI,EAAE,qGAEjB,MAAM,EAC9B;EAPS,KAAA,QAAA;EACA,KAAA,UAAA;EAOT,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,oBAAb,cAAuC,SAAS;CAErC;CACA;CACA;CAHV,YACC,OACA,KACA,MACC;EACD,MACC,GAAG,MAAM,IAAI,IAAI,YAAY,2BAA2B,oBACnD,MAAM,UAAU,KAAK,KAAK,IAAI,EAAE,gFACQ,KAAK,MAAM,QAAQ,uBAC3C,MAAM,eAC5B;EATS,KAAA,QAAA;EACA,KAAA,MAAA;EACA,KAAA,OAAA;EAQT,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,mBAAb,cAAsC,SAAS;CACzB;CAArB,YAAY,OAAwB;EACnC,MAAM,4BAA4B,MAAM,GAAG;EADvB,KAAA,QAAA;EAEpB,KAAK,OAAO;CACb;AACD;AA6BA,MAAa,uBAAsC;CAClD,QAAQ;CACR,QAAQ;CACR,SAAS,OAAO,aAAa;EAC5B,MAAM,SAAS,MAAM,OAAO,GAAG,SAAS,UAAU,EACjD,SAAS,oCACV,CAAC;EACD,OAAO,YAAY,KAAK,OAAO,KAAK,CAAC;CACtC;CACA,KAAK,QAAQ;AACd;;;;;;;;AAeA,SAAgB,eACf,OACA,SACyB;CACzB,MAAM,SAAS,MAAM,WAAW,MAC9B,cAAc,UAAU,SAAS,OACnC;CACA,IAAI,QAAQ,OAAO,EAAE,GAAG,OAAO,OAAO;CACtC,MAAM,MAAM,MAAM,KAAK,MAAM,cAAc,UAAU,SAAS,OAAO;CACrE,IAAI,CAAC,KACJ,MAAM,IAAI,kBAAkB,MAAM,OAAO,SAAS,CACjD,GAAG,MAAM,KAAK,KAAK,cAAc,UAAU,IAAI,GAC/C,GAAG,MAAM,WAAW,KAAK,cAAc,UAAU,IAAI,CACtD,CAAC;CAEF,IAAI,IAAI,SAAS,QAAQ,OAAO,EAAE,GAAG,IAAI,OAAO,KAAK;CACrD,MAAM,UAAU,MAAM,QAAQ,QAAQ,WAAW,OAAO,SAAS,IAAI,IAAI;CACzE,OAAO,OAAO,OACb,CAAC,GACD,GAAG,QAAQ,KAAK,WAAW,OAAO,OAAO,CAAC,CAAC,GAC3C,IAAI,OAAO,CAAC,CACb;AACD;;AAGA,SAAS,UAAU,MAAc,MAAsB;CACtD,OAAO,IAAI,OACV,aAAa,KAAK,QAAQ,SAAS,MAAM,EAAE,GAAG,KAAK,UACnD,GACD;AACD;;;;;AAMA,SAAgB,eACf,KACA,OACiB;CACjB,MAAM,wBAAQ,IAAI,IAA0B;CAC5C,MAAM,UAAU;CAChB,KAAK,MAAM,SAAS,OAAO,OAAO,GAAG,GACpC,KAAK,MAAM,SAAS,MAAM,SAAS,OAAO,GAAG;EAC5C,MAAM,UAAU,MAAM;EACtB,IAAI,CAAC,MAAM,MAAM,SAAS,OAAO,GAAG;EACpC,MAAM,OAAO,OAAO,MAAM,EAAE;EAC5B,MAAM,IAAI,GAAG,QAAQ,GAAG,QAAQ;GAAE;GAAS;EAAK,CAAC;CAClD;CAED,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC;AAC1B;;;;;;AAOA,SAAgB,eACf,KACA,OACA,SACyB;CACzB,MAAM,WAA+B,CAAC;CACtC,KAAK,MAAM,CAAC,SAAS,SAAS,SAAS;EACtC,MAAM,CAAC,SAAS,UAAU,QAAQ,MAAM,GAAG;EAC3C,SAAS,KAAK,CAAC,UAAU,SAAU,OAAO,MAAM,CAAC,GAAG,aAAa,MAAM,CAAC;CACzE;CACA,KAAK,MAAM,OAAO,MAAM,MACvB,KAAK,MAAM,QAAQ,CAAC,IAAI,MAAM,UAAU,MAAM,SAAS,IAAI,IAAI,CAAC,GAC/D,SAAS,KAAK,CACb,IAAI,OACH,UAAU,KAAK,QAAQ,SAAS,MAAM,EAAE,GAAG,IAAI,KAAK,UACpD,GACD,GACA,IAAI,GACL,CAAC;CAGH,MAAM,MAA8B,CAAC;CACrC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,GAAG,GAC5C,IAAI,OAAO,SAAS,QAClB,SAAS,CAAC,SAAS,iBACnB,QAAQ,QAAQ,eAAe,WAAW,GAC3C,KACD;CAED,OAAO;AACR;;;;;AAMA,SAAgB,kBAAkB,MAAiC;CAClE,MAAM,SACL,qDACG,KAAK,UAAU,IAAI,EAAE;CACzB,OAAO,iCAAiC,mBAAmB,MAAM;AAClE;;;;;;;;AAqBA,eAAsB,UACrB,SACA,MAAyB,QAAQ,KACV;CACvB,MAAM,MAAM,QAAQ,OAAO,QAAQ,IAAI;CACvC,MAAM,EAAE,UAAU;CAGlB,MAAM,OAAO,MAAM,qBAAqB,GAAG,CAAC,CAAC,OAAO,UAAU;EAC7D,IAAI,iBAAiB,YAAY,OAAO,KAAA;EACxC,MAAM;CACP,CAAC;CACD,MAAM,YACL,MAAM,cAAc,MAAM,oBAAoB,GAAG,EAAA,CAAG;CAErD,oBAAoB,UAAU,QAAQ,KAAK;CAC3C,IAAI,CAAC,mBAAmB,SAAS,GAAG,MAAM,IAAI,qBAAqB,KAAK;CAExE,MAAM,YAAsC,CAAC;CAC7C,MAAM,aAAuC,CAAC;CAC9C,MAAM,WAA8B,MAAM,SAAS;EAClD,UAAU,eAAe,SAAS;EAClC,KAAK,UAAU;EACf;EACA;CACD,CAAC;CAED,MAAM,QAAQ,MAAM,gBAAgB,WAAW,OAAO,EACrD,GAAI,QAAQ,UAAU,EAAE,SAAS,QAAQ,QAAQ,IAAI,CAAC,EACvD,CAAC;CACD,MAAM,EAAE,SAAS,cAAc,qBAC9B,WACA,OACA,MAAM,MAAM,KAAK,KAAK,GACtB,QACD;CACA,IAAI,UAAU,SAAS,GAAG,MAAM,IAAI,qBAAqB,OAAO,SAAS;CAczE,OAAO;EACN;EACA;EACA,OAfa,aAAa;GAC1B;GACA;GACA;GACA;GACA;GACA,UAAU,eAAe,WAAW,KAAK;GAEzC,QAAQ;IAAE,MAAM;IAAS,KAAK;GAAO;GACrC;GACA,OAAO,UAAU,GAAG;EACrB,CAIK;EACJ;EACA,SAAS,MAAM;EACf,gBAAgB,UAAU,OAAO,WAAW,SAAS,KAAK,KAAK;CAChE;AACD;;AAGA,SAAS,YACR,QACA,SACgB;CAChB,MAAM,SAAS,cAAc;EAC5B,OAAO,QAAQ;EACf,OAAO;EACP,QAAQ,OAAO,UAAU,QAAQ,SAAS;EAC1C,QAAQ,OAAO,QAAQ;CACxB,CAAC;CACD,IAAI,QAAQ,SAAS,OAAO,OAAO,QAAQ;CAC3C,OAAO;AACR;;;;;;;;;AAUA,eAAsB,YACrB,aACA,SACA,OAAsB,sBACJ;CAClB,MAAM,EAAE,UAAU;CAClB,MAAM,SAAS,MAAM,UAAU,SAAS,KAAK,GAAG;CAChD,MAAM,EAAE,OAAO,SAAS,mBAAmB;CAC3C,MAAM,MAAM,eAAe,OAAO,OAAO;CAEzC,IAAI,kBAAkB,CAAC,QAAQ,KAAK;EACnC,MAAM,WAAW,IAAI,MAAM,8BAA8B,YAAY,KAAK,GAAG,EAAE;EAC/E,IAAI,CAAE,MAAM,KAAK,QAAQ,QAAQ,GAAI,MAAM,IAAI,iBAAiB,KAAK;CACtE;CAIA,MAAM,YAAY,eAAe,KAAK,KAAK;CAC3C,IAAI,UAAU,WAAW,GAAG;EAC3B,OAAO,IAAI,WAAW,QAAQ,OAAO,MAAM,EAAE;EAC7C,OAAO,IAAI,aAAa,OAAO,KAAK,GAAG,CAAC,CAAC,OAAO,iBAAiB,MAAM,EAAE;EACzE,OAAO,IAAI,aAAa,KAAK,QAAQ,GAAG;CACzC;CAEA,MAAM,SAAS,YAAY,QAAQ,OAAO;CAC1C,OAAO,IAAI,WAAW,QAAQ,OAAO,MAAM,KAAK,OAAO,KAAK,EAAE;CAI9D,MAAM,KAAK,OAAO,MAAM,MAAM;CAE9B,MAAM,UAAoB,CAAC;CAC3B,MAAM,wBAAQ,IAAI,IAAoB;CACtC,IAAI;EACH,KAAK,MAAM,WAAW,WAAW;GAChC,MAAM,YAAY,MAAM,KAAK,OAAO,UACnC,EAAE,MAAM,WAAW,MAAM,EAAE,GAC3B,MAAM,SACN,QAAQ,SACR,QAAQ,IACT;GACA,IAAI,cAAc,KAAA,GAAW;IAC5B,OAAO,IACN,OAAO,QAAQ,QAAQ,8CACnB,MAAM,QAAQ,GAAG,QAAQ,QAAQ,aAAa,QAAQ,KAAK,uEAEhE;IACA;GACD;GACA,MAAM,SAAS,MAAM,KAAK,OAAO,OAAO,QAAQ,SAAS;GACzD,QAAQ,KAAK,MAAM;GACnB,MAAM,IAAI,GAAG,QAAQ,QAAQ,GAAG,QAAQ,QAAQ,OAAO,IAAI;GAC3D,OAAO,IAAI,MAAM,QAAQ,QAAQ,eAAe,OAAO,KAAK,YAAY;EACzE;EAEA,MAAM,cAAc,eAAe,KAAK,OAAO,KAAK;EACpD,OAAO,IACN,aAAa,OAAO,KAAK,WAAW,CAAC,CAAC,OAAO,iBAAiB,MAAM,EACrE;EACA,OAAO,MAAM,IAAI,aAAa,aAAa,QAAQ,GAAG;CACvD,UAAU;EACT,MAAM,QAAQ,IAAI,QAAQ,KAAK,WAAW,OAAO,MAAM,CAAC,CAAC;CAC1D;AACD;;;;;;AAOA,SAAS,IACR,aACA,aACA,KACkB;CAClB,MAAM,CAAC,KAAK,GAAG,QAAQ;CACvB,OAAO,IAAI,eAAe,YAAY,KAAK,GAAG,GAAG;CAKjD,MAAM,cAAc,EAHD,QAAQ,IAAI,gBAAgB,GAAA,CAC7C,QAAQ,oBAAoB,EAAE,CAAC,CAC/B,KAC2B,GAAG,kBAAkB,OAAO,KAAK,WAAW,CAAC,CAAC,CAAC,CAC1E,OAAO,OAAO,CAAC,CACf,KAAK,GAAG;CAEV,MAAM,aAAa,CAAC;CACpB,QAAQ,GAAG,UAAU,IAAI;CACzB,QAAQ,GAAG,WAAW,IAAI;CAC1B,OAAO,IAAI,SAAiB,YAAY;EACvC,MAAM,QAAQ,MAAM,KAAK,MAAM;GAC9B,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC;GACrB,OAAO;GACP,KAAK;IAAE,GAAG,QAAQ;IAAK,GAAG;IAAa,cAAc;GAAY;EAClE,CAAC;EACD,MAAM,GAAG,UAAU,MAAM,WAAW;GACnC,QAAQ,QAAQ,OAAO,SAAS,UAAU,QAAQ,UAAU,EAAE;EAC/D,CAAC;EACD,MAAM,GAAG,UAAU,UAAU;GAC5B,OAAO,MAAM,0BAA0B,MAAM,SAAS;GACtD,QAAQ,CAAC;EACV,CAAC;CACF,CAAC,CAAC,CAAC,cAAc;EAChB,QAAQ,IAAI,UAAU,IAAI;EAC1B,QAAQ,IAAI,WAAW,IAAI;CAC5B,CAAC;AACF"}