@geekmidas/cli 10.0.0-alpha.123 → 10.0.0-alpha.125

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 (265) hide show
  1. package/dist/{EndpointGenerator-BDa6pPgE.cjs → EndpointGenerator-BGxf9jWE.cjs} +8 -4
  2. package/dist/{EndpointGenerator-BDa6pPgE.cjs.map → EndpointGenerator-BGxf9jWE.cjs.map} +1 -1
  3. package/dist/{EndpointGenerator-CP_z_E3e.mjs → EndpointGenerator-errMeldx.mjs} +8 -4
  4. package/dist/{EndpointGenerator-CP_z_E3e.mjs.map → EndpointGenerator-errMeldx.mjs.map} +1 -1
  5. package/dist/{GoDaddyProvider-BlB5nJky.mjs → GoDaddyProvider-BN1dODm9.mjs} +3 -3
  6. package/dist/{GoDaddyProvider-BlB5nJky.mjs.map → GoDaddyProvider-BN1dODm9.mjs.map} +1 -1
  7. package/dist/{GoDaddyProvider-Dubq-pCc.cjs → GoDaddyProvider-BTdtqhI_.cjs} +3 -3
  8. package/dist/{GoDaddyProvider-Dubq-pCc.cjs.map → GoDaddyProvider-BTdtqhI_.cjs.map} +1 -1
  9. package/dist/{HostingerProvider-BNpS9aRJ.mjs → HostingerProvider-Cjh_2Ml6.mjs} +2 -2
  10. package/dist/{HostingerProvider-BNpS9aRJ.mjs.map → HostingerProvider-Cjh_2Ml6.mjs.map} +1 -1
  11. package/dist/{HostingerProvider-DR1iws1P.cjs → HostingerProvider-OdQmKXE3.cjs} +2 -2
  12. package/dist/{HostingerProvider-DR1iws1P.cjs.map → HostingerProvider-OdQmKXE3.cjs.map} +1 -1
  13. package/dist/{LegacyStateStore-ClvUC8qy.cjs → LegacyStateStore-C2S8TGwx.cjs} +3 -3
  14. package/dist/{LegacyStateStore-ClvUC8qy.cjs.map → LegacyStateStore-C2S8TGwx.cjs.map} +1 -1
  15. package/dist/{LegacyStateStore-DGALQGZu.mjs → LegacyStateStore-DgXmuG87.mjs} +3 -3
  16. package/dist/{LegacyStateStore-DGALQGZu.mjs.map → LegacyStateStore-DgXmuG87.mjs.map} +1 -1
  17. package/dist/{LocalStateStore-43kSYNpw.cjs → LocalStateStore-CaJw9H-3.cjs} +2 -2
  18. package/dist/{LocalStateStore-43kSYNpw.cjs.map → LocalStateStore-CaJw9H-3.cjs.map} +1 -1
  19. package/dist/{LocalStateStore-CBYUzVR5.mjs → LocalStateStore-ojY_BiMK.mjs} +2 -2
  20. package/dist/{LocalStateStore-CBYUzVR5.mjs.map → LocalStateStore-ojY_BiMK.mjs.map} +1 -1
  21. package/dist/{OpenApiTsGenerator-CsrukCE-.mjs → OpenApiTsGenerator-W_HRXH1X.mjs} +2 -2
  22. package/dist/{OpenApiTsGenerator-CsrukCE-.mjs.map → OpenApiTsGenerator-W_HRXH1X.mjs.map} +1 -1
  23. package/dist/{OpenApiTsGenerator-FNhY7Ssz.cjs → OpenApiTsGenerator-cQgtxWQo.cjs} +2 -2
  24. package/dist/{OpenApiTsGenerator-FNhY7Ssz.cjs.map → OpenApiTsGenerator-cQgtxWQo.cjs.map} +1 -1
  25. package/dist/{S3StateStore-DLHjDYYP.mjs → S3StateStore-BSPkxhrx.mjs} +2 -2
  26. package/dist/{S3StateStore-DLHjDYYP.mjs.map → S3StateStore-BSPkxhrx.mjs.map} +1 -1
  27. package/dist/{S3StateStore-ahE7hSNK.cjs → S3StateStore-BqugXIcb.cjs} +2 -2
  28. package/dist/{S3StateStore-ahE7hSNK.cjs.map → S3StateStore-BqugXIcb.cjs.map} +1 -1
  29. package/dist/{SSMStateStore-qIzIuazA.cjs → SSMStateStore-B0OsuT_k.cjs} +2 -2
  30. package/dist/{SSMStateStore-qIzIuazA.cjs.map → SSMStateStore-B0OsuT_k.cjs.map} +1 -1
  31. package/dist/{SSMStateStore-j5RuMkch.mjs → SSMStateStore-DM85OiH0.mjs} +2 -2
  32. package/dist/{SSMStateStore-j5RuMkch.mjs.map → SSMStateStore-DM85OiH0.mjs.map} +1 -1
  33. package/dist/{SubscriberGenerator-C8CPxrCK.cjs → SubscriberGenerator-BDUp7OeY.cjs} +237 -54
  34. package/dist/SubscriberGenerator-BDUp7OeY.cjs.map +1 -0
  35. package/dist/{SubscriberGenerator-y3pncae_.mjs → SubscriberGenerator-ocQ2yj0K.mjs} +227 -56
  36. package/dist/SubscriberGenerator-ocQ2yj0K.mjs.map +1 -0
  37. package/dist/{add-Bm-C56lH.mjs → add-C7ZBXIzM.mjs} +6 -6
  38. package/dist/{add-Bm-C56lH.mjs.map → add-C7ZBXIzM.mjs.map} +1 -1
  39. package/dist/{add-C2CvXrYw.cjs → add-CrxvmSpq.cjs} +7 -7
  40. package/dist/{add-C2CvXrYw.cjs.map → add-CrxvmSpq.cjs.map} +1 -1
  41. package/dist/{bundler-Dk7tAvAX.cjs → bundler-DVUXW95p.cjs} +5 -5
  42. package/dist/{bundler-Dk7tAvAX.cjs.map → bundler-DVUXW95p.cjs.map} +1 -1
  43. package/dist/{bundler-DnkFt4mn.mjs → bundler-DpZ9uBGN.mjs} +5 -5
  44. package/dist/{bundler-DnkFt4mn.mjs.map → bundler-DpZ9uBGN.mjs.map} +1 -1
  45. package/dist/{cli-Di464j1O.cjs → cli-BMRhE6cZ.cjs} +4 -4
  46. package/dist/{cli-Di464j1O.cjs.map → cli-BMRhE6cZ.cjs.map} +1 -1
  47. package/dist/{cli-CVeHFXNE.mjs → cli-BWfYyy1k.mjs} +4 -4
  48. package/dist/{cli-CVeHFXNE.mjs.map → cli-BWfYyy1k.mjs.map} +1 -1
  49. package/dist/{compose-cHkF---f.mjs → compose-BFmdLig3.mjs} +525 -55
  50. package/dist/compose-BFmdLig3.mjs.map +1 -0
  51. package/dist/{compose-ChVvb3xM.cjs → compose-Bqcy0lqo.cjs} +554 -84
  52. package/dist/compose-Bqcy0lqo.cjs.map +1 -0
  53. package/dist/{config-DrOX29sT.d.mts → config-CdytFgSR.d.cts} +3 -4
  54. package/dist/config-CdytFgSR.d.cts.map +1 -0
  55. package/dist/{config-B3iz9BtK.d.cts → config-_y1FGBWR.d.mts} +3 -4
  56. package/dist/config-_y1FGBWR.d.mts.map +1 -0
  57. package/dist/config.cjs +2 -2
  58. package/dist/config.d.cts +3 -3
  59. package/dist/config.d.mts +3 -3
  60. package/dist/config.mjs +2 -2
  61. package/dist/{credentialSchemas-B67WJOOm.cjs → credentialSchemas-DFxvGEwa.cjs} +2 -2
  62. package/dist/{credentialSchemas-B67WJOOm.cjs.map → credentialSchemas-DFxvGEwa.cjs.map} +1 -1
  63. package/dist/{credentialSchemas-DDOLRCLF.mjs → credentialSchemas-DaMpt-Dx.mjs} +2 -2
  64. package/dist/{credentialSchemas-DDOLRCLF.mjs.map → credentialSchemas-DaMpt-Dx.mjs.map} +1 -1
  65. package/dist/{credentials-Zqr3QQYB.cjs → credentials-D7XJypNd.cjs} +173 -3
  66. package/dist/credentials-D7XJypNd.cjs.map +1 -0
  67. package/dist/{credentials-B5N5yKk6.mjs → credentials-DpfnUvIR.mjs} +132 -4
  68. package/dist/credentials-DpfnUvIR.mjs.map +1 -0
  69. package/dist/deploy/public.cjs +16 -17
  70. package/dist/deploy/public.cjs.map +1 -1
  71. package/dist/deploy/public.d.cts +3 -5
  72. package/dist/deploy/public.d.cts.map +1 -1
  73. package/dist/deploy/public.d.mts +3 -5
  74. package/dist/deploy/public.d.mts.map +1 -1
  75. package/dist/deploy/public.mjs +11 -12
  76. package/dist/deploy/public.mjs.map +1 -1
  77. package/dist/{deploy-DO4djCHb.mjs → deploy-D8pUA9Y0.mjs} +1240 -130
  78. package/dist/deploy-D8pUA9Y0.mjs.map +1 -0
  79. package/dist/{deploy-BffR_uEN.cjs → deploy-DVrNjX1X.cjs} +1261 -169
  80. package/dist/deploy-DVrNjX1X.cjs.map +1 -0
  81. package/dist/{discover-C5roJuc9.mjs → discover-CzGM5P3p.mjs} +136 -14
  82. package/dist/discover-CzGM5P3p.mjs.map +1 -0
  83. package/dist/{discover-DozUKs8f.cjs → discover-DL6h-J97.cjs} +158 -12
  84. package/dist/discover-DL6h-J97.cjs.map +1 -0
  85. package/dist/{dokploy-BcZm93Cc.mjs → dokploy-CzDGNB6A.mjs} +2 -2
  86. package/dist/{dokploy-BcZm93Cc.mjs.map → dokploy-CzDGNB6A.mjs.map} +1 -1
  87. package/dist/{dokploy-CsdvfH3t.cjs → dokploy-TtK6Zyl3.cjs} +2 -2
  88. package/dist/{dokploy-CsdvfH3t.cjs.map → dokploy-TtK6Zyl3.cjs.map} +1 -1
  89. package/dist/{drivers-BjGDYfnC.mjs → drivers-C-VqMtld.mjs} +2 -2
  90. package/dist/{drivers-BjGDYfnC.mjs.map → drivers-C-VqMtld.mjs.map} +1 -1
  91. package/dist/{drivers-DbjiYpW6.cjs → drivers-L29WvE95.cjs} +2 -2
  92. package/dist/{drivers-DbjiYpW6.cjs.map → drivers-L29WvE95.cjs.map} +1 -1
  93. package/dist/{eas-CN_cUXth.cjs → eas-D3clzkni.cjs} +2 -2
  94. package/dist/{eas-CN_cUXth.cjs.map → eas-D3clzkni.cjs.map} +1 -1
  95. package/dist/{eas-CBShP20v.mjs → eas-DaobzDks.mjs} +2 -2
  96. package/dist/{eas-CBShP20v.mjs.map → eas-DaobzDks.mjs.map} +1 -1
  97. package/dist/{emit-Dmc1nvdS.cjs → emit-CYloptRS.cjs} +34 -1
  98. package/dist/emit-CYloptRS.cjs.map +1 -0
  99. package/dist/{emit-C1e8qD08.mjs → emit-CiJGdy_f.mjs} +34 -1
  100. package/dist/emit-CiJGdy_f.mjs.map +1 -0
  101. package/dist/{encryption-CPDM49Hi.cjs → encryption-DBkIFiR2.cjs} +3 -3
  102. package/dist/{encryption-CPDM49Hi.cjs.map → encryption-DBkIFiR2.cjs.map} +1 -1
  103. package/dist/{encryption-CITa6dXL.mjs → encryption-DV31gEY8.mjs} +3 -3
  104. package/dist/{encryption-CITa6dXL.mjs.map → encryption-DV31gEY8.mjs.map} +1 -1
  105. package/dist/{engine-CTnHmHjz.mjs → engine-CFOR9h6X.mjs} +140 -16
  106. package/dist/engine-CFOR9h6X.mjs.map +1 -0
  107. package/dist/{engine-D-jfMVj-.cjs → engine-CuZUGbEh.cjs} +143 -19
  108. package/dist/engine-CuZUGbEh.cjs.map +1 -0
  109. package/dist/{github-D56PSK39.cjs → github-CsvtWSBW.cjs} +115 -47
  110. package/dist/github-CsvtWSBW.cjs.map +1 -0
  111. package/dist/{github-9RlA7EjN.mjs → github-Nep8pI4_.mjs} +113 -45
  112. package/dist/github-Nep8pI4_.mjs.map +1 -0
  113. package/dist/{googlePlay-SX_jQeJf.cjs → googlePlay-C-UfBXJU.cjs} +5 -5
  114. package/dist/{googlePlay-SX_jQeJf.cjs.map → googlePlay-C-UfBXJU.cjs.map} +1 -1
  115. package/dist/{googlePlay-COi5LXON.mjs → googlePlay-lF7Atm_N.mjs} +4 -4
  116. package/dist/{googlePlay-COi5LXON.mjs.map → googlePlay-lF7Atm_N.mjs.map} +1 -1
  117. package/dist/{index-DcyRA5Rf.d.mts → index-DrkV7N5w.d.mts} +2 -2
  118. package/dist/{index-DcyRA5Rf.d.mts.map → index-DrkV7N5w.d.mts.map} +1 -1
  119. package/dist/{index-DjLSMgAL.d.cts → index-yaXZ9m-J.d.cts} +2 -2
  120. package/dist/{index-DjLSMgAL.d.cts.map → index-yaXZ9m-J.d.cts.map} +1 -1
  121. package/dist/index.cjs +166 -85
  122. package/dist/index.cjs.map +1 -1
  123. package/dist/index.mjs +149 -68
  124. package/dist/index.mjs.map +1 -1
  125. package/dist/{local-BwC2v2Y8.mjs → local-B7Zv1NoA.mjs} +2 -2
  126. package/dist/{local-BwC2v2Y8.mjs.map → local-B7Zv1NoA.mjs.map} +1 -1
  127. package/dist/{local-DNhnkugT.cjs → local-OPC9BCZt.cjs} +3 -3
  128. package/dist/{local-DNhnkugT.cjs.map → local-OPC9BCZt.cjs.map} +1 -1
  129. package/dist/{migrate-CrtxXeGY.cjs → migrate-Dp3GhFnf.cjs} +4 -4
  130. package/dist/{migrate-CrtxXeGY.cjs.map → migrate-Dp3GhFnf.cjs.map} +1 -1
  131. package/dist/{migrate-0J6gsfrQ.mjs → migrate-NfbPVhi9.mjs} +4 -4
  132. package/dist/{migrate-0J6gsfrQ.mjs.map → migrate-NfbPVhi9.mjs.map} +1 -1
  133. package/dist/{migrate-DJa_Dlsl.mjs → migrate-_sBX0pve.mjs} +3 -3
  134. package/dist/{migrate-DJa_Dlsl.mjs.map → migrate-_sBX0pve.mjs.map} +1 -1
  135. package/dist/{migrate-BCz1lkh_.cjs → migrate-hlNKNWmg.cjs} +3 -3
  136. package/dist/{migrate-BCz1lkh_.cjs.map → migrate-hlNKNWmg.cjs.map} +1 -1
  137. package/dist/openapi.cjs +3 -3
  138. package/dist/openapi.d.cts +1 -1
  139. package/dist/openapi.d.mts +1 -1
  140. package/dist/openapi.mjs +3 -3
  141. package/dist/{publish-CuQlOW5p.mjs → publish-BuUVaZVy.mjs} +2 -2
  142. package/dist/{publish-CuQlOW5p.mjs.map → publish-BuUVaZVy.mjs.map} +1 -1
  143. package/dist/{publish-qSpnvmdX.cjs → publish-ZOo4xEuZ.cjs} +2 -2
  144. package/dist/{publish-qSpnvmdX.cjs.map → publish-ZOo4xEuZ.cjs.map} +1 -1
  145. package/dist/{ready-BOfogqzr.mjs → ready-Ca883OvY.mjs} +6 -6
  146. package/dist/{ready-BOfogqzr.mjs.map → ready-Ca883OvY.mjs.map} +1 -1
  147. package/dist/{ready-BFFP838V.cjs → ready-qnqp6JAY.cjs} +6 -6
  148. package/dist/{ready-BFFP838V.cjs.map → ready-qnqp6JAY.cjs.map} +1 -1
  149. package/dist/reconcile/public.cjs +2 -2
  150. package/dist/reconcile/public.d.cts +1 -2
  151. package/dist/reconcile/public.d.cts.map +1 -1
  152. package/dist/reconcile/public.d.mts +1 -2
  153. package/dist/reconcile/public.d.mts.map +1 -1
  154. package/dist/reconcile/public.mjs +2 -2
  155. package/dist/{resolve-j8hUcbGz.mjs → resolve-BCU9pYDx.mjs} +4 -4
  156. package/dist/{resolve-j8hUcbGz.mjs.map → resolve-BCU9pYDx.mjs.map} +1 -1
  157. package/dist/{resolve-AsfohXd1.cjs → resolve-C9QqOl5t.cjs} +4 -4
  158. package/dist/{resolve-AsfohXd1.cjs.map → resolve-C9QqOl5t.cjs.map} +1 -1
  159. package/dist/{rollback-Bxq_URgL.cjs → rollback-CTWdDBgB.cjs} +7 -7
  160. package/dist/{rollback-Bxq_URgL.cjs.map → rollback-CTWdDBgB.cjs.map} +1 -1
  161. package/dist/{rollback-JgbAY43C.mjs → rollback-CWyyTQ0B.mjs} +7 -7
  162. package/dist/{rollback-JgbAY43C.mjs.map → rollback-CWyyTQ0B.mjs.map} +1 -1
  163. package/dist/{runtime-DIZTeGJV.d.cts → runtime-DCkfI2E7.d.mts} +2 -2
  164. package/dist/{runtime-DIZTeGJV.d.cts.map → runtime-DCkfI2E7.d.mts.map} +1 -1
  165. package/dist/{runtime-WMLJY-db.d.mts → runtime-Ie4znWIB.d.cts} +2 -2
  166. package/dist/{runtime-WMLJY-db.d.mts.map → runtime-Ie4znWIB.d.cts.map} +1 -1
  167. package/dist/{s3-B4FTQQkV.mjs → s3-BZzSnker.mjs} +2 -2
  168. package/dist/{s3-B4FTQQkV.mjs.map → s3-BZzSnker.mjs.map} +1 -1
  169. package/dist/{s3-DwKLXwUT.cjs → s3-pqgmCTOq.cjs} +2 -2
  170. package/dist/{s3-DwKLXwUT.cjs.map → s3-pqgmCTOq.cjs.map} +1 -1
  171. package/dist/sandbox/credentials-worker.cjs +2 -2
  172. package/dist/sandbox/credentials-worker.mjs +2 -2
  173. package/dist/sandbox/discover-worker.cjs +1 -1
  174. package/dist/sandbox/discover-worker.mjs +1 -1
  175. package/dist/sandbox/migrate-worker.cjs +1 -1
  176. package/dist/sandbox/migrate-worker.mjs +1 -1
  177. package/dist/{seeds-Dv78P9LS.mjs → seeds-CK7w79VC.mjs} +848 -61
  178. package/dist/seeds-CK7w79VC.mjs.map +1 -0
  179. package/dist/{seeds-BWC-qjSI.cjs → seeds-CPEcmoPn.cjs} +953 -64
  180. package/dist/seeds-CPEcmoPn.cjs.map +1 -0
  181. package/dist/{serverSetup-DB6sKwAy.mjs → serverSetup-BAPNuNEX.mjs} +6 -6
  182. package/dist/{serverSetup-DB6sKwAy.mjs.map → serverSetup-BAPNuNEX.mjs.map} +1 -1
  183. package/dist/{serverSetup-CtV80a5M.cjs → serverSetup-C6t6Vtt7.cjs} +6 -6
  184. package/dist/{serverSetup-CtV80a5M.cjs.map → serverSetup-C6t6Vtt7.cjs.map} +1 -1
  185. package/dist/{sniffer-ctpwBBkT.cjs → sniffer-DdX_WN_-.cjs} +9 -9
  186. package/dist/{sniffer-ctpwBBkT.cjs.map → sniffer-DdX_WN_-.cjs.map} +1 -1
  187. package/dist/{sniffer-qDFUQmWP.mjs → sniffer-g9v7cqDN.mjs} +9 -9
  188. package/dist/{sniffer-qDFUQmWP.mjs.map → sniffer-g9v7cqDN.mjs.map} +1 -1
  189. package/dist/{dns-Bmnuttja.cjs → snsSubscriptions-5Cqww8S7.cjs} +351 -3
  190. package/dist/snsSubscriptions-5Cqww8S7.cjs.map +1 -0
  191. package/dist/{dns-DFvO5a53.mjs → snsSubscriptions-BmYeeTH8.mjs} +280 -4
  192. package/dist/snsSubscriptions-BmYeeTH8.mjs.map +1 -0
  193. package/dist/{sst-C1pxbFEd.mjs → sst-CTqZZl1_.mjs} +3 -4
  194. package/dist/{sst-C1pxbFEd.mjs.map → sst-CTqZZl1_.mjs.map} +1 -1
  195. package/dist/{sst-B-Uz4cvm.cjs → sst-lEkBwzGc.cjs} +4 -5
  196. package/dist/{sst-B-Uz4cvm.cjs.map → sst-lEkBwzGc.cjs.map} +1 -1
  197. package/dist/{stage-C7lf36az.mjs → stage-C-I2dVw1.mjs} +6 -6
  198. package/dist/{stage-C7lf36az.mjs.map → stage-C-I2dVw1.mjs.map} +1 -1
  199. package/dist/{stage-DzALYUwY.cjs → stage-CUE5pMmw.cjs} +7 -7
  200. package/dist/{stage-DzALYUwY.cjs.map → stage-CUE5pMmw.cjs.map} +1 -1
  201. package/dist/{stages-B7mCWI_R.cjs → stages-3yUZaZeA.cjs} +2 -2
  202. package/dist/{stages-B7mCWI_R.cjs.map → stages-3yUZaZeA.cjs.map} +1 -1
  203. package/dist/{stages-DSltTW2d.mjs → stages-DTTv0NZW.mjs} +2 -2
  204. package/dist/{stages-DSltTW2d.mjs.map → stages-DTTv0NZW.mjs.map} +1 -1
  205. package/dist/target/public.cjs +1 -1
  206. package/dist/target/public.d.cts +2 -2
  207. package/dist/target/public.d.mts +2 -2
  208. package/dist/target/public.mjs +1 -1
  209. package/dist/{terminal-DJlqcW_U.cjs → terminal-PKhPAajN.cjs} +3 -3
  210. package/dist/{terminal-DJlqcW_U.cjs.map → terminal-PKhPAajN.cjs.map} +1 -1
  211. package/dist/{terminal-CW_dWeTe.mjs → terminal-bqY33lpN.mjs} +3 -3
  212. package/dist/{terminal-CW_dWeTe.mjs.map → terminal-bqY33lpN.mjs.map} +1 -1
  213. package/dist/{types-Q2SC_ly2.d.cts → types-Bmjs4jZV.d.mts} +248 -4
  214. package/dist/types-Bmjs4jZV.d.mts.map +1 -0
  215. package/dist/{types-C8gXOLWL.d.mts → types-D0IsQxES.d.cts} +249 -3
  216. package/dist/types-D0IsQxES.d.cts.map +1 -0
  217. package/dist/vitest.cjs +1 -1
  218. package/dist/vitest.mjs +1 -1
  219. package/dist/workspace/index.cjs +1 -1
  220. package/dist/workspace/index.d.cts +2 -2
  221. package/dist/workspace/index.d.mts +2 -2
  222. package/dist/workspace/index.mjs +1 -1
  223. package/dist/{workspace-DUnnVjqC.mjs → workspace-DBHF9Gba.mjs} +5 -3
  224. package/dist/workspace-DBHF9Gba.mjs.map +1 -0
  225. package/dist/{workspace-C82GLPnf.cjs → workspace-Du4Afb03.cjs} +5 -3
  226. package/dist/workspace-Du4Afb03.cjs.map +1 -0
  227. package/package.json +17 -15
  228. package/dist/SubscriberGenerator-C8CPxrCK.cjs.map +0 -1
  229. package/dist/SubscriberGenerator-y3pncae_.mjs.map +0 -1
  230. package/dist/compose-ChVvb3xM.cjs.map +0 -1
  231. package/dist/compose-cHkF---f.mjs.map +0 -1
  232. package/dist/config-B3iz9BtK.d.cts.map +0 -1
  233. package/dist/config-DrOX29sT.d.mts.map +0 -1
  234. package/dist/credentials-B5N5yKk6.mjs.map +0 -1
  235. package/dist/credentials-Zqr3QQYB.cjs.map +0 -1
  236. package/dist/deploy-BffR_uEN.cjs.map +0 -1
  237. package/dist/deploy-DO4djCHb.mjs.map +0 -1
  238. package/dist/discover-C08meHhu.d.mts +0 -125
  239. package/dist/discover-C08meHhu.d.mts.map +0 -1
  240. package/dist/discover-C5roJuc9.mjs.map +0 -1
  241. package/dist/discover-DipvfWpK.d.cts +0 -125
  242. package/dist/discover-DipvfWpK.d.cts.map +0 -1
  243. package/dist/discover-DozUKs8f.cjs.map +0 -1
  244. package/dist/dns-Bmnuttja.cjs.map +0 -1
  245. package/dist/dns-DFvO5a53.mjs.map +0 -1
  246. package/dist/emit-C1e8qD08.mjs.map +0 -1
  247. package/dist/emit-Dmc1nvdS.cjs.map +0 -1
  248. package/dist/engine-CTnHmHjz.mjs.map +0 -1
  249. package/dist/engine-D-jfMVj-.cjs.map +0 -1
  250. package/dist/github-9RlA7EjN.mjs.map +0 -1
  251. package/dist/github-D56PSK39.cjs.map +0 -1
  252. package/dist/run-BJLIerMM.mjs +0 -133
  253. package/dist/run-BJLIerMM.mjs.map +0 -1
  254. package/dist/run-fVZ-rLnc.cjs +0 -174
  255. package/dist/run-fVZ-rLnc.cjs.map +0 -1
  256. package/dist/sandbox-Co1y2CmF.d.mts +0 -104
  257. package/dist/sandbox-Co1y2CmF.d.mts.map +0 -1
  258. package/dist/sandbox-DvNqr6od.d.cts +0 -104
  259. package/dist/sandbox-DvNqr6od.d.cts.map +0 -1
  260. package/dist/seeds-BWC-qjSI.cjs.map +0 -1
  261. package/dist/seeds-Dv78P9LS.mjs.map +0 -1
  262. package/dist/types-C8gXOLWL.d.mts.map +0 -1
  263. package/dist/types-Q2SC_ly2.d.cts.map +0 -1
  264. package/dist/workspace-C82GLPnf.cjs.map +0 -1
  265. package/dist/workspace-DUnnVjqC.mjs.map +0 -1
@@ -1,15 +1,15 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-BhDjJH2R.mjs";
2
2
  import { t as GkmError } from "./errors-D8Gwp5kU.mjs";
3
- import { a as runOutput, i as run } from "./run-BJLIerMM.mjs";
4
- import { B as LOGS_PORT, I as otlpTelemetryEnv, L as resolveStageTelemetry, at as certificateFor, ft as SERVER_IPV4_KEY, ht as isReservedStageKey, it as ComposeTlsFileMissing, ot as proxyFor, z as telemetryEnv } from "./workspace-DUnnVjqC.mjs";
5
- import { E as stageBackups, a as serverAddressHint, b as stackBackups, d as passwordSites, f as siteAuth, h as BACKUPS_SERVICE, i as requiredServerAddress, l as systemLookup, m as withSitePasswords, o as stackHosts, p as siteNames, r as isLocalHost, s as stageDns, t as HostNotPointingAtServer, v as backupsRoleStatements, y as backupsService } from "./dns-DFvO5a53.mjs";
3
+ import { c as runOutput, s as run } from "./credentials-DpfnUvIR.mjs";
4
+ import { B as LOGS_PORT, I as otlpTelemetryEnv, L as resolveStageTelemetry, at as certificateFor, ft as SERVER_IPV4_KEY, ht as isReservedStageKey, it as ComposeTlsFileMissing, ot as proxyFor, z as telemetryEnv } from "./workspace-DBHF9Gba.mjs";
5
+ import { A as backupsRoleStatements, C as passwordSites, D as BACKUPS_SERVICE, E as withSitePasswords, M as stackBackups, R as stageBackups, T as siteNames, a as subscriptionKey, c as HostNotPointingAtServer, f as requiredServerAddress, g as stageDns, h as stackHosts, i as recordedPushSubscriptions, j as backupsService, n as SUBSCRIPTION_TYPE, o as subscriptionResource, p as serverAddressHint, r as confirmPendingSubscriptions, s as topicConnection, t as PushSubscriptionsUnconfirmed, u as isLocalHost, v as systemLookup, w as siteAuth } from "./snsSubscriptions-BmYeeTH8.mjs";
6
6
  import { t as defineTarget } from "./define-6poNqm5O.mjs";
7
- import { $n as stackLogs, $t as publicBuildValues, An as localRolePassword, B as workerDockerfile, Bn as EMULATOR_ACCESS_KEY_ID, Bt as initStageSecrets, Cn as REDIS_SERVICE, Ct as composeFor, Dn as stackRedis, En as runsRedis, Fn as isTelemetryKey, Gn as portKeys, Gt as composeBuildPaths, Hn as DEFAULT_IMAGES, In as scopeTelemetryEnv, Jn as primaryPortKey, Jt as WORKER_PORT, Kt as imageLayout, L as appDockerfile, Mn as planFor, N as stageProviderNotes, On as withRedisPassword, Qn as logsService, Rn as telemetryOf, S as recordRelease, Sn as withGeneratedSecrets, Tn as redisService, Vn as hostFor, Xn as LOGS_SERVICE, Xt as findBuildRoot, Zn as logsAccess, a as dnsResources, an as applyPostgres, at as suppliedOnly, bt as bucketClient, c as currentActor, ct as networkEnv, d as DeployJournal, er as withLogsPassword, et as devServicesUsed, fn as LOCAL_RABBITMQ_USER, ft as constructGlobs, hn as postgresSuperuser, in as applyPolicies, it as stageServiceDeclarations, kn as envFor, ln as postgresStatements, lt as workerEnvKeys, n as reportDatabaseRuns, nn as applyBuckets, nr as isMainFrontendApp, o as forgetDnsResource, on as bucketLifecycles, ot as appEnvKeys, pn as loadLocalCredentials, q as validateImageRef, qt as TURBO_VERSION, r as reportPlannedSeeds, rn as applyLifecycles, rr as resolveHost, s as recordDnsChanges, sn as bucketPolicies, t as DeploySeedsFailed, tn as workerUnits, tr as NoDomainForStage, tt as externalServices, v as createComposeState, vt as LEGACY_LOCAL_LOGIN, wn as STACK_CACHE, xt as pgClient, yt as ensurePostgresLogin, z as ensureDockerignore, zn as usesTelemetry } from "./seeds-Dv78P9LS.mjs";
7
+ import { $ as ORIGIN_VERIFY_HEADER, $n as telemetryOf, An as postgresSuperuser, At as LEGACY_LOCAL_LOGIN, B as processorDockerfile, Bn as redisService, Dn as LOCAL_RABBITMQ_USER, Ft as composeFor, Gn as localRolePassword, H as workerDockerfile, Hn as stackRedis, L as appDockerfile, Ln as withGeneratedSecrets, Mt as bucketClient, N as stageProviderNotes, Nt as pgClient, On as loadLocalCredentials, Qt as initStageSecrets, Rn as REDIS_SERVICE, S as recordRelease, Sn as bucketPolicies, Un as withRedisPassword, V as processorNative, Vn as runsRedis, Wn as envFor, Xn as isTelemetryKey, Zn as scopeTelemetryEnv, _n as applyLifecycles, _r as withLogsPassword, _t as appEnvKeys, a as dnsResources, an as PROCESSOR_PORT, ar as DEFAULT_IMAGES, bn as applyPostgres, br as resolveHost, bt as processorEnvKeys, c as currentActor, cr as portKeys, d as DeployJournal, dr as primaryPortKey, dt as devServicesUsed, er as usesTelemetry, fn as publicBuildValues, ft as externalServices, gn as applyBuckets, gr as stackLogs, gt as suppliedOnly, hn as workerUnits, hr as logsService, ht as stageServiceDeclarations, in as imageLayout, ir as hostFor, it as validateImageRef, jt as ensurePostgresLogin, ln as findBuildRoot, mn as processorUnits, mr as logsAccess, n as reportDatabaseRuns, nr as watchedBuckets, nt as originSecretKey, o as forgetDnsResource, on as TURBO_VERSION, pr as LOGS_SERVICE, qn as planFor, r as reportPlannedSeeds, rn as composeBuildPaths, rr as EMULATOR_ACCESS_KEY_ID, rt as processorOriginHost, s as recordDnsChanges, sn as WORKER_PORT, t as DeploySeedsFailed, tr as minioWebhookEnv, v as createComposeState, vn as applyNotifications, vr as NoDomainForStage, wn as postgresStatements, wt as constructGlobs, xn as bucketLifecycles, xt as workerEnvKeys, yn as applyPolicies, yr as isMainFrontendApp, yt as networkEnv, z as ensureDockerignore, zn as STACK_CACHE } from "./seeds-CK7w79VC.mjs";
8
8
  import { t as appKey } from "./derive-B4vDpzgP.mjs";
9
- import { n as subscriberRoute, s as DEFAULT_EVENTS } from "./SubscriberGenerator-y3pncae_.mjs";
9
+ import { a as SNS_SUBSCRIPTIONS_KEY, l as DEFAULT_EVENTS, o as objectsRoute, s as subscriberRoute } from "./SubscriberGenerator-ocQ2yj0K.mjs";
10
10
  import { a as imageRef } from "./identity-CBXr4wNi.mjs";
11
- import { i as discover } from "./discover-C5roJuc9.mjs";
12
- import { i as installedFrom } from "./drivers-BjGDYfnC.mjs";
11
+ import { i as discover } from "./discover-CzGM5P3p.mjs";
12
+ import { i as installedFrom } from "./drivers-C-VqMtld.mjs";
13
13
  import { f as plannedSeeds, o as SeedFailed, p as seedDatabases, u as migrateDatabases } from "./databases-BPOgTn1u.mjs";
14
14
  import { dirname, join } from "node:path";
15
15
  import { existsSync } from "node:fs";
@@ -649,6 +649,21 @@ const sshAccess = {
649
649
  }
650
650
  };
651
651
  //#endregion
652
+ //#region src/compose/routes.ts
653
+ /** A route with handlers and a check that would have to hold for each of them. */
654
+ var EdgeRouteConflict = class extends Error {
655
+ route;
656
+ constructor(route) {
657
+ super(`The edge route '${route}' sends some paths to other upstreams and also restricts who or what it answers. The two are not combined: give the restricted paths a host of their own.`);
658
+ this.route = route;
659
+ this.name = "EdgeRouteConflict";
660
+ }
661
+ };
662
+ /** @throws {EdgeRouteConflict} */
663
+ function assertRouteRenderable(route) {
664
+ if (route.handlers?.length && (route.allow || route.paths || route.prefixes || route.requireHeader)) throw new EdgeRouteConflict(route.name);
665
+ }
666
+ //#endregion
652
667
  //#region src/compose/caddyfile.ts
653
668
  /**
654
669
  * The stack's own edge, rendered for Caddy: one site block per route.
@@ -684,26 +699,55 @@ function edgeCaddyfile(routes, options) {
684
699
  ` : tls.kind === "files" ? `
685
700
  tls ${tls.certFile} ${tls.keyFile}
686
701
  ` : "";
687
- const blocks = routes.map(({ host, upstreams, prefix, allow, auth, paths, streaming }) => `https://${host} {${certificate}
688
- # gkm's own header, which only a service of this stack may send: the auth
689
- # server rate-limits by the client address it carries.
690
- request_header -${CLIENT_IP_HEADER}
691
- ${allow ? `
702
+ const blocks = routes.map((route) => siteBlock(route, certificate));
703
+ return `${HEADER}${global ? `${global}\n\n` : ""}${blocks.join("\n\n")}\n`;
704
+ }
705
+ /** One route's site block. */
706
+ function siteBlock(route, certificate) {
707
+ assertRouteRenderable(route);
708
+ const { host, allow, auth, paths, prefixes, requireHeader, handlers } = route;
709
+ const allowed = [...paths ?? [], ...(prefixes ?? []).map((prefix) => `${prefix}*`)];
710
+ const checks = `${allow ? `
692
711
  # Only these addresses — the peer Caddy sees, never a header a client
693
712
  # could set. Everything else is refused before it reaches the upstream.
694
713
  @denied not remote_ip ${allow.join(" ")}
695
714
  respond @denied 403
715
+ ` : ""}${requireHeader ? `
716
+ # Only what the CDN sends: it adds this header to every request, and this
717
+ # host would be reached around the CDN otherwise.
718
+ @forged not header ${requireHeader.name} "${requireHeader.value}"
719
+ respond @forged 403
696
720
  ` : ""}${auth ? `
697
721
  # The stage's login, asked for before anything reaches the upstream.
698
722
  basic_auth {
699
723
  ${auth.user} ${auth.hash}
700
724
  }
701
- ` : ""}${paths ? `
725
+ ` : ""}${allowed.length ? `
702
726
  # Only these paths answer. Everything else on this host is refused before
703
727
  # it reaches the upstream.
704
- @elsewhere not path ${paths.join(" ")}
728
+ @elsewhere not path ${allowed.join(" ")}
705
729
  respond @elsewhere 404
706
- ` : ""}${prefix ? `
730
+ ` : ""}`;
731
+ const proxy = proxyBlock(route);
732
+ const body = handlers?.length ? `${handlers.map((handler, index) => `
733
+ # Answered by another service: ${handler.name}.
734
+ @handler${index} path ${handler.prefixes.map((prefix) => `${prefix}*`).join(" ")}
735
+ handle @handler${index} {
736
+ reverse_proxy ${handler.upstreams.map(address).join(" ")}
737
+ }
738
+ `).join("")}
739
+ handle {${proxy.replace(/\n/g, "\n ")}
740
+ }` : proxy;
741
+ return `https://${host} {${certificate}
742
+ # gkm's own header, which only a service of this stack may send: the auth
743
+ # server rate-limits by the client address it carries.
744
+ request_header -${CLIENT_IP_HEADER}
745
+ ${checks}${body}
746
+ }`;
747
+ }
748
+ /** Where a route's requests go, when no handler took them. */
749
+ function proxyBlock({ upstreams, prefix, streaming }) {
750
+ return prefix ? `
707
751
  # A bucket, served at a host of its own: the bucket is a prefix on MinIO,
708
752
  # which routes and signs on the Host header — so it is sent the upstream's.
709
753
  reverse_proxy ${upstreams.map(address).join(" ")} {
@@ -716,17 +760,16 @@ ${allow ? `
716
760
  # Never buffer: a streamed response — server-sent events, a Next.js
717
761
  # RSC payload — reaches the client as each chunk is written.
718
762
  flush_interval -1
719
- }` : ""}`}
720
- }`);
721
- return `${HEADER}${global ? `${global}\n\n` : ""}${blocks.join("\n\n")}\n`;
763
+ }` : ""}`;
722
764
  }
723
765
  const HEADER = `# Generated by gkm compose from the construct manifest — do not edit.
724
766
  #
725
767
  # One host per API and site, each in front of its own container, one per
726
- # file server in front of the stack's MinIO, one per worker SNS pushes to,
727
- # answering only those routes, and — when its logs are served publicly — one
728
- # for OpenObserve, to the addresses it allows. A site the stage puts behind a
729
- # password asks for its login first.
768
+ # file server in front of the stack's MinIO — its processors' paths sent to
769
+ # their own service — one per worker SNS pushes to and per processor unit a
770
+ # CDN reaches, answering only those paths, and — when its logs are served
771
+ # publicly — one for OpenObserve, to the addresses it allows. A site the
772
+ # stage puts behind a password asks for its login first.
730
773
  `;
731
774
  //#endregion
732
775
  //#region src/compose/images.ts
@@ -1029,9 +1072,24 @@ function traefikDynamic(routes, options) {
1029
1072
  middlewares[name(route, "prefix")] = { addPrefix: { prefix: route.prefix } };
1030
1073
  chain.push(name(route, "prefix"));
1031
1074
  }
1032
- const paths = route.paths ? ` && (${route.paths.map((path) => `Path(\`${path}\`)`).join(" || ")})` : "";
1075
+ assertRouteRenderable(route);
1076
+ const allowed = [...(route.paths ?? []).map((path) => `Path(\`${path}\`)`), ...(route.prefixes ?? []).map((prefix) => `PathPrefix(\`${prefix}\`)`)];
1077
+ const paths = allowed.length ? ` && (${allowed.join(" || ")})` : "";
1078
+ const header = route.requireHeader ? ` && Header(\`${route.requireHeader.name}\`, \`${route.requireHeader.value}\`)` : "";
1079
+ for (const handler of route.handlers ?? []) {
1080
+ const handlerName = name(route, handler.name);
1081
+ routers[handlerName] = {
1082
+ rule: `Host(\`${route.host}\`) && (${handler.prefixes.map((prefix) => `PathPrefix(\`${prefix}\`)`).join(" || ")})`,
1083
+ priority: 1e4,
1084
+ entryPoints: ["websecure"],
1085
+ service: handlerName,
1086
+ middlewares: [strip],
1087
+ tls: tls.kind === "acme" ? { certResolver: ACME_RESOLVER } : {}
1088
+ };
1089
+ services[handlerName] = { loadBalancer: { servers: handler.upstreams.map((upstream) => ({ url: url(upstream) })) } };
1090
+ }
1033
1091
  routers[name(route)] = {
1034
- rule: `Host(\`${route.host}\`)${paths}`,
1092
+ rule: `Host(\`${route.host}\`)${paths}${header}`,
1035
1093
  entryPoints: ["websecure"],
1036
1094
  service: name(route),
1037
1095
  middlewares: chain,
@@ -1173,6 +1231,19 @@ const LOG_ROTATION = {
1173
1231
  "max-file": "3"
1174
1232
  }
1175
1233
  };
1234
+ /** A CDN serves the bucket, and the secret it sends the processors is not in the stage. */
1235
+ var ProcessorOriginSecretMissing = class extends GkmError {
1236
+ fileServer;
1237
+ key;
1238
+ stage;
1239
+ constructor(fileServer, key, stage) {
1240
+ super(`'${fileServer}' has processors, and its bucket is not the stack's MinIO, so they are reached through the CDN in front of it — which sends ${key}, and stage '${stage}' has none. It is written by the deploy that creates the CDN (\`deploy.objects.${stage}\` on s3, cdn on): run gkm deploy --stage ${stage}.`);
1241
+ this.fileServer = fileServer;
1242
+ this.key = key;
1243
+ this.stage = stage;
1244
+ this.name = "ProcessorOriginSecretMissing";
1245
+ }
1246
+ };
1176
1247
  /**
1177
1248
  * The stack with Caddy's files inline: its Caddyfile and, where the stage has
1178
1249
  * one, its own certificate — `configs` with their `content`, which compose
@@ -1381,7 +1452,13 @@ function composeStack(input) {
1381
1452
  identity,
1382
1453
  images: input.images
1383
1454
  });
1384
- if (apps.length === 0 && workers.length === 0) throw new NothingToCompose(workspace.root);
1455
+ const processors = stackProcessors(processorUnits(workspace, manifest), {
1456
+ manifest,
1457
+ stage,
1458
+ identity,
1459
+ images: input.images
1460
+ });
1461
+ if (apps.length === 0 && workers.length === 0 && processors.length === 0) throw new NothingToCompose(workspace.root);
1385
1462
  const owners = /* @__PURE__ */ new Map();
1386
1463
  for (const resource of plan.resources) {
1387
1464
  owners.set(resource.envKey, resource);
@@ -1443,9 +1520,20 @@ function composeStack(input) {
1443
1520
  ...credential.containers.minio,
1444
1521
  buckets,
1445
1522
  policies: bucketPolicies(plan).filter((p) => buckets.includes(p.bucket)),
1523
+ objects: services.minio.map((id) => ({
1524
+ id,
1525
+ name: byId.get(id)?.name
1526
+ })).filter((b) => Boolean(b.name)).sort((a, b) => a.name.localeCompare(b.name)),
1527
+ webhooks: [],
1446
1528
  lifecycles: bucketLifecycles(plan).filter((l) => buckets.includes(l.bucket))
1447
1529
  };
1448
1530
  })() : void 0;
1531
+ const bucketEventKeys = /* @__PURE__ */ new Map();
1532
+ for (const resource of plan.resources) {
1533
+ if (resource.kind !== "objects") continue;
1534
+ bucketEventKeys.set(provideKey(resource.id, "eventsConnectionString"), resource.id);
1535
+ bucketEventKeys.set(provideKey(resource.id, "eventsToken"), resource.id);
1536
+ }
1449
1537
  const fileServers = /* @__PURE__ */ new Map();
1450
1538
  if (storage) for (const resource of plan.resources) {
1451
1539
  if (resource.kind !== "file-server" || !resource.of) continue;
@@ -1490,7 +1578,8 @@ function composeStack(input) {
1490
1578
  project: workspace.name,
1491
1579
  credentials: credential.containers,
1492
1580
  seed: credential.seed,
1493
- ...!local && domain ? { mailFrom: `noreply@${domain}` } : {}
1581
+ ...!local && domain ? { mailFrom: `noreply@${domain}` } : {},
1582
+ relayedBuckets: [...watchedBuckets(manifest, input.subscribers ?? {}).keys()].filter((bucket) => services.minio.includes(bucket))
1494
1583
  };
1495
1584
  const outside = networkEnv(plan, {
1496
1585
  ...derivation,
@@ -1508,6 +1597,11 @@ function composeStack(input) {
1508
1597
  */
1509
1598
  const valueFor = (app, key, owner) => {
1510
1599
  if (isReservedStageKey(key)) return void 0;
1600
+ const watching = bucketEventKeys.get(key);
1601
+ if (watching) {
1602
+ if (custom[key] !== void 0) return custom[key];
1603
+ return services.minio.includes(watching) ? outside[key] : void 0;
1604
+ }
1511
1605
  if (!local && owner?.kind === "email" && key.endsWith("_INBOX_URL")) return;
1512
1606
  if (owner && SERVICE_KINDS[owner.kind]) {
1513
1607
  if (custom[key] !== void 0) return custom[key];
@@ -1609,13 +1703,70 @@ function composeStack(input) {
1609
1703
  serviceName: worker.name,
1610
1704
  ...telemetryValues ? { telemetry: telemetryValues } : {}
1611
1705
  }),
1612
- ...push ? { [SUBSCRIBER_PUSH_URL_KEY]: push.url } : {},
1706
+ ...push && !input.recordingSubscriptions ? { [SNS_SUBSCRIPTIONS_KEY]: recordedSubscriptions(worker, push, input.pushSubscriptions?.[worker.id]) } : {},
1613
1707
  NODE_ENV: "production",
1614
1708
  PORT: String(worker.port),
1615
1709
  STAGE: stage
1616
1710
  };
1617
1711
  for (const [key, value] of Object.entries(worker.env)) if (/[\r\n]/.test(value)) throw new EnvValueMultiline(worker.name, key);
1618
1712
  }
1713
+ for (const unit of processors) {
1714
+ const served = fileServers.get(unit.id);
1715
+ if (served) served.route = {
1716
+ ...served.route,
1717
+ handlers: [{
1718
+ name: unit.name,
1719
+ prefixes: unit.prefixes,
1720
+ upstreams: [{
1721
+ service: unit.name,
1722
+ port: unit.port
1723
+ }]
1724
+ }]
1725
+ };
1726
+ else if (!local && !input.buildOnly) {
1727
+ if (!domain) throw new NoDomainForStage(stage);
1728
+ const key = originSecretKey(unit.id);
1729
+ const secret = custom[key];
1730
+ if (!secret) throw new ProcessorOriginSecretMissing(unit.id, key, stage);
1731
+ unit.origin = {
1732
+ host: processorOriginHost(unit.id, domain),
1733
+ secret
1734
+ };
1735
+ }
1736
+ if (proxy === "traefik" && (served || unit.origin)) edgeServices.add(unit.name);
1737
+ if (input.buildOnly) continue;
1738
+ const keys = processorEnvKeys(manifest, unit.id, input.runnables) ?? /* @__PURE__ */ new Set();
1739
+ unit.env = {
1740
+ ...scopeTelemetryEnv(valuesFor({
1741
+ name: unit.name,
1742
+ id: unit.id,
1743
+ kind: "rest-api"
1744
+ }, keys), {
1745
+ uses: telemetryOf(manifest, unit.id) !== void 0,
1746
+ serviceName: unit.name,
1747
+ ...telemetryValues ? { telemetry: telemetryValues } : {}
1748
+ }),
1749
+ NODE_ENV: "production",
1750
+ PORT: String(unit.port),
1751
+ STAGE: stage
1752
+ };
1753
+ for (const [key, value] of Object.entries(unit.env)) if (/[\r\n]/.test(value)) throw new EnvValueMultiline(unit.name, key);
1754
+ }
1755
+ if (storage && !input.buildOnly) {
1756
+ const watched = watchedBuckets(manifest, input.subscribers ?? {});
1757
+ for (const [bucket, workerId] of watched) {
1758
+ if (!services.minio.includes(bucket)) continue;
1759
+ const worker = workers.find((w) => w.id === workerId);
1760
+ const relay = worker?.env?.[provideKey(bucket, "eventsConnectionString")];
1761
+ const token = worker?.env?.[provideKey(bucket, "eventsToken")];
1762
+ if (!worker || !relay || relay.startsWith("sns:") || !token) continue;
1763
+ storage.webhooks.push({
1764
+ bucket,
1765
+ endpoint: `http://${hostOf(worker.name)}:${worker.port}${objectsRoute(bucket)}`,
1766
+ token
1767
+ });
1768
+ }
1769
+ }
1619
1770
  const deployedTls = certificate ? {
1620
1771
  kind: "files",
1621
1772
  ...proxy === "caddy" ? {
@@ -1643,6 +1794,7 @@ function composeStack(input) {
1643
1794
  infra,
1644
1795
  apps,
1645
1796
  workers,
1797
+ processors,
1646
1798
  credentials: credential.containers,
1647
1799
  ...storage ? { storage } : {},
1648
1800
  ...logs ? { logs } : {},
@@ -1678,6 +1830,21 @@ function composeStack(input) {
1678
1830
  health: app.kind === "site" ? "/" : "/health"
1679
1831
  })),
1680
1832
  ...[...fileServers.values()].map(({ route }) => route),
1833
+ ...processors.flatMap(({ name, port, prefixes, origin }) => origin ? [{
1834
+ name,
1835
+ host: origin.host,
1836
+ upstreams: [{
1837
+ service: name,
1838
+ port
1839
+ }],
1840
+ streaming: false,
1841
+ prefixes,
1842
+ requireHeader: {
1843
+ name: ORIGIN_VERIFY_HEADER,
1844
+ value: origin.secret
1845
+ },
1846
+ health: "/health"
1847
+ }] : []),
1681
1848
  ...workers.flatMap(({ name, port, push }) => push ? [{
1682
1849
  name,
1683
1850
  host: push.host,
@@ -1727,12 +1894,26 @@ function composeStack(input) {
1727
1894
  cache: STACK_CACHE
1728
1895
  });
1729
1896
  }
1897
+ for (const unit of processors) {
1898
+ if (!unit.build) continue;
1899
+ dockerfiles[unit.build.dockerfile] = processorDockerfile({
1900
+ id: unit.id,
1901
+ name: unit.name,
1902
+ app: unit.host
1903
+ }, workspace.apps[unit.host], {
1904
+ layout,
1905
+ workspaceRoot: workspace.root,
1906
+ native: unit.native,
1907
+ cache: STACK_CACHE
1908
+ });
1909
+ }
1730
1910
  return {
1731
1911
  project,
1732
1912
  stage,
1733
1913
  local,
1734
1914
  apps,
1735
1915
  workers,
1916
+ processors,
1736
1917
  mobileApps,
1737
1918
  infra,
1738
1919
  compose,
@@ -1795,29 +1976,61 @@ function stackApps(workspace, manifest, plan, options) {
1795
1976
  }
1796
1977
  return apps.sort((a, b) => a.name.localeCompare(b.name));
1797
1978
  }
1798
- /** What the worker reads the public base of its push routes from. */
1799
- const SUBSCRIBER_PUSH_URL_KEY = "GKM_SUBSCRIBER_PUSH_URL";
1800
1979
  /**
1801
1980
  * Where SNS pushes to a worker on a deployed stage, or nothing when it pushes
1802
1981
  * to none of its subscribers.
1803
1982
  *
1804
- * A subscriber is pushed to when its topic's connection string, as this stage
1805
- * resolved it for the worker, is an SNS one — the same test the worker's own
1983
+ * A subscriber is pushed to when the connection string its deliveries come
1984
+ * from — its topic's, or its bucket's events — as this stage resolved it for
1985
+ * the worker, is an SNS one — the same test the worker's own
1806
1986
  * startup makes, so the edge exposes a route exactly when the worker mounts
1807
1987
  * it. Each such route, and nothing else the worker serves, answers on
1808
1988
  * `<worker>.<domain>`.
1809
1989
  */
1810
1990
  function workerPush(worker, subscribers, env, options) {
1811
- const paths = subscribers.filter(({ topic }) => env[provideKey(topic, "publisherConnectionString")]?.startsWith("sns:")).map(({ name }) => subscriberRoute(name)).sort();
1812
- if (paths.length === 0) return void 0;
1991
+ const pushed = subscribers.filter(({ connectionKey }) => env[connectionKey]?.startsWith("sns:")).filter((subscriber, index, all) => all.findIndex((s) => s.name === subscriber.name) === index).sort((a, b) => a.name.localeCompare(b.name));
1992
+ if (pushed.length === 0) return void 0;
1813
1993
  if (!options.domain) throw new NoDomainForStage(options.stage);
1814
1994
  const host = `${worker.name}.${options.domain}`;
1995
+ const url = `https://${host}`;
1815
1996
  return {
1816
1997
  host,
1817
- url: `https://${host}`,
1818
- paths: [...new Set(paths)]
1998
+ url,
1999
+ paths: pushed.map(({ name }) => subscriberRoute(name)),
2000
+ subscribers: pushed.map(({ name, connectionKey, events }) => ({
2001
+ name,
2002
+ connectionKey,
2003
+ endpoint: `${url}${subscriberRoute(name)}`,
2004
+ ...events ? { events } : {}
2005
+ }))
1819
2006
  };
1820
2007
  }
2008
+ /** A pushed subscriber the deploy recorded no SNS subscription for. */
2009
+ var PushSubscriptionMissing = class extends GkmError {
2010
+ worker;
2011
+ subscriber;
2012
+ constructor(worker, subscriber) {
2013
+ super(`SNS pushes to '${subscriber}' on the worker '${worker}', and the deploy recorded no subscription for it, so the worker would mount no route and SNS would deliver nowhere. Deploy the stage, which subscribes each pushed subscriber and records its ARN.`);
2014
+ this.worker = worker;
2015
+ this.subscriber = subscriber;
2016
+ this.name = "PushSubscriptionMissing";
2017
+ }
2018
+ };
2019
+ /**
2020
+ * The worker's `GKM_SNS_SUBSCRIPTIONS`: each pushed subscriber's recorded
2021
+ * subscription ARN, and nothing for a subscriber it does not run.
2022
+ *
2023
+ * @throws {PushSubscriptionMissing} when one has none
2024
+ */
2025
+ function recordedSubscriptions(worker, push, recorded = {}) {
2026
+ const arns = {};
2027
+ for (const { name } of push.subscribers) {
2028
+ const arn = recorded[name];
2029
+ if (!arn) throw new PushSubscriptionMissing(worker.name, name);
2030
+ arns[name] = arn;
2031
+ }
2032
+ return JSON.stringify(arns);
2033
+ }
1821
2034
  /** Two constructs the edge would answer on one host. */
1822
2035
  var EdgeHostTaken = class extends GkmError {
1823
2036
  host;
@@ -1840,6 +2053,25 @@ function assertHostsUnique(routes) {
1840
2053
  claimed.set(host, name);
1841
2054
  }
1842
2055
  }
2056
+ /** Each processor unit's image, and where it is built from. */
2057
+ function stackProcessors(units, options) {
2058
+ return units.map((unit) => {
2059
+ const server = options.manifest[unit.id];
2060
+ const processors = server?.kind === "file-server" ? server.processors ?? [] : [];
2061
+ return {
2062
+ app: unit.name,
2063
+ name: unit.name,
2064
+ id: unit.id,
2065
+ host: unit.app,
2066
+ port: PROCESSOR_PORT,
2067
+ prefixes: [...new Set(processors.map((p) => p.prefix))].sort(),
2068
+ native: processorNative(options.manifest, unit.id),
2069
+ ref: validateImageRef(imageRef(options.identity, unit.name, options.images.registry, options.images.tag)),
2070
+ tag: options.images.tag,
2071
+ ...options.images.mode === "build" ? { build: { dockerfile: `${stackDir(options.stage)}/Dockerfile.${unit.name}` } } : {}
2072
+ };
2073
+ });
2074
+ }
1843
2075
  /** Each worker's image, and where it is built from. */
1844
2076
  function stackWorkers(units, options) {
1845
2077
  return units.map((unit) => ({
@@ -1897,7 +2129,8 @@ function stackFile(options) {
1897
2129
  minio.environment = {
1898
2130
  ...minio.environment,
1899
2131
  MINIO_ROOT_USER: options.storage.user,
1900
- MINIO_ROOT_PASSWORD: options.storage.password
2132
+ MINIO_ROOT_PASSWORD: options.storage.password,
2133
+ ...minioWebhookEnv(options.storage.webhooks)
1901
2134
  };
1902
2135
  minio.ports = ["127.0.0.1::9000"];
1903
2136
  }
@@ -1925,7 +2158,7 @@ function stackFile(options) {
1925
2158
  } : {},
1926
2159
  healthcheck: probe(app.port, app.kind === "site" ? "/" : "/health")
1927
2160
  };
1928
- for (const worker of options.workers) services[worker.name] = {
2161
+ for (const worker of [...options.workers, ...options.processors]) services[worker.name] = {
1929
2162
  image: worker.ref,
1930
2163
  ...worker.build ? { build: composeBuildPaths({
1931
2164
  composeDir: options.composeDir,
@@ -1946,7 +2179,11 @@ function stackFile(options) {
1946
2179
  restart: "unless-stopped",
1947
2180
  ports: [`${options.https}:443`, `${options.http}:80`],
1948
2181
  volumes: ["caddy-data:/data", "caddy-config:/config"],
1949
- depends_on: Object.fromEntries([...apps.map((app) => app.name), ...options.workers.filter((w) => w.push).map((w) => w.name)].map((name) => [name, HEALTHY])),
2182
+ depends_on: Object.fromEntries([
2183
+ ...apps.map((app) => app.name),
2184
+ ...options.workers.filter((w) => w.push).map((w) => w.name),
2185
+ ...options.processors.map((p) => p.name)
2186
+ ].map((name) => [name, HEALTHY])),
1950
2187
  healthcheck: {
1951
2188
  test: [
1952
2189
  "CMD",
@@ -2265,6 +2502,119 @@ var ComposeAppsUnhealthy = class extends GkmError {
2265
2502
  }
2266
2503
  };
2267
2504
  //#endregion
2505
+ //#region src/target/compose/topicSubscriptions.ts
2506
+ /**
2507
+ * Every topic subscriber the stack pushes to: a subscriber whose connection
2508
+ * key is a topic's publisher string — a bucket's events key is not — and
2509
+ * whose value on this stage is an SNS one.
2510
+ */
2511
+ function topicPushes(stack, manifest) {
2512
+ const topicKeys = new Set(Object.values(manifest).filter((declaration) => declaration.kind === "topic").map((topic) => provideKey(topic.id, "publisherConnectionString")));
2513
+ const pushes = [];
2514
+ for (const worker of stack.workers) for (const subscriber of worker.push?.subscribers ?? []) {
2515
+ if (!topicKeys.has(subscriber.connectionKey)) continue;
2516
+ const connectionString = worker.env?.[subscriber.connectionKey];
2517
+ if (!connectionString?.startsWith("sns:")) continue;
2518
+ pushes.push({
2519
+ worker: worker.id,
2520
+ name: subscriber.name,
2521
+ endpoint: subscriber.endpoint,
2522
+ connectionKey: subscriber.connectionKey,
2523
+ connectionString,
2524
+ ...subscriber.events ? { events: subscriber.events } : {}
2525
+ });
2526
+ }
2527
+ return pushes;
2528
+ }
2529
+ /** The subscriptions recorded for hand-set topics: those with a key. */
2530
+ function recordedTopicSubscriptions(resources) {
2531
+ return Object.values(resources ?? {}).filter((record) => record.type === "sns-subscription" && typeof record.data?.connectionKey === "string");
2532
+ }
2533
+ /**
2534
+ * Converge each hand-set topic's subscriptions on the subscribers pushed to:
2535
+ * subscribe, bring filters up to date, unsubscribe a route on the worker's
2536
+ * host no subscriber wants any more — and record each ARN, forgetting what
2537
+ * was removed.
2538
+ *
2539
+ * @returns each worker's subscriptions, by export — what it is handed
2540
+ */
2541
+ async function ensureTopicSubscriptions(input) {
2542
+ const { snsUrl, reconcileHttpSubscriptions } = await import("@geekmidas/events/sns");
2543
+ const groups = /* @__PURE__ */ new Map();
2544
+ const group = (topicArn, host, connectionKey) => {
2545
+ const id = `${topicArn}|${host}`;
2546
+ const found = groups.get(id);
2547
+ if (found) return found;
2548
+ const created = {
2549
+ topicArn,
2550
+ host,
2551
+ connectionKey,
2552
+ pushes: [],
2553
+ recorded: []
2554
+ };
2555
+ groups.set(id, created);
2556
+ return created;
2557
+ };
2558
+ for (const push of input.pushes) {
2559
+ const { topicArn } = snsUrl.parse(push.connectionString);
2560
+ const into = group(topicArn, new URL(push.endpoint).host, push.connectionKey);
2561
+ into.connectionString = push.connectionString;
2562
+ into.pushes.push(push);
2563
+ }
2564
+ for (const record of recordedTopicSubscriptions(input.recorded)) group(String(record.data?.topicArn), new URL(String(record.data?.endpoint)).host, String(record.data?.connectionKey)).recorded.push(record);
2565
+ const wanted = new Set(input.pushes.map(({ worker, name }) => subscriptionKey(worker, name)));
2566
+ const arns = {};
2567
+ for (const { topicArn, host, connectionKey, ...members } of groups.values()) {
2568
+ const stale = members.recorded.filter((r) => !wanted.has(r.key));
2569
+ const named = members.connectionString ?? input.secrets[connectionKey];
2570
+ if (!named?.startsWith("sns:")) {
2571
+ for (const record of stale) {
2572
+ input.log(` – forgot ${record.data?.endpoint}: '${connectionKey}' no longer names an SNS topic to unsubscribe it from`);
2573
+ await input.forget(record.key);
2574
+ }
2575
+ continue;
2576
+ }
2577
+ const address = new URL(named);
2578
+ address.searchParams.set("topicArn", topicArn);
2579
+ const connection = await topicConnection({
2580
+ connectionKey,
2581
+ connectionString: address.toString(),
2582
+ stage: input.stage,
2583
+ env: input.env
2584
+ });
2585
+ const result = await reconcileHttpSubscriptions(connection, {
2586
+ endpoints: members.pushes.map((push) => ({
2587
+ name: push.name,
2588
+ endpoint: push.endpoint,
2589
+ ...push.events ? { events: push.events } : {}
2590
+ })),
2591
+ ownedHost: host
2592
+ });
2593
+ await connection.close();
2594
+ for (const push of members.pushes) {
2595
+ const arn = result.subscriptions[push.name];
2596
+ const key = subscriptionKey(push.worker, push.name);
2597
+ if (input.recorded?.[key]?.id !== arn) input.log(` ✓ '${push.name}' subscribed to ${topicArn.split(":").pop()} at ${push.endpoint}`);
2598
+ await input.record(subscriptionResource({
2599
+ worker: push.worker,
2600
+ name: push.name,
2601
+ topicArn,
2602
+ endpoint: push.endpoint,
2603
+ connectionKey: push.connectionKey,
2604
+ pending: result.pending.includes(push.name),
2605
+ ...push.events ? { events: push.events } : {}
2606
+ }, arn));
2607
+ arns[push.worker] = {
2608
+ ...arns[push.worker],
2609
+ [push.name]: arn
2610
+ };
2611
+ }
2612
+ for (const arn of result.removed) input.log(` – unsubscribed ${arn}: no subscriber wants it any more`);
2613
+ for (const record of stale) await input.forget(record.key);
2614
+ }
2615
+ return arns;
2616
+ }
2617
+ //#endregion
2268
2618
  //#region src/target/compose/phases.ts
2269
2619
  /**
2270
2620
  * The compose target's phases: a stage as one Docker Compose stack behind
@@ -2415,8 +2765,9 @@ async function validateCompose(ctx, deps, options = {}) {
2415
2765
  subscribers
2416
2766
  });
2417
2767
  const { secrets, generated } = await stageSecrets(ctx, manifest);
2768
+ const recorded = push ? void 0 : await ctx.state.read(stage);
2418
2769
  const layout = mode === "build" ? imageLayout(workspace) : void 0;
2419
- const composed = composeStack({
2770
+ const input = {
2420
2771
  workspace,
2421
2772
  manifest,
2422
2773
  runnables,
@@ -2435,10 +2786,29 @@ async function validateCompose(ctx, deps, options = {}) {
2435
2786
  ports: edgePorts(deps.env),
2436
2787
  ...layout ? { layout } : {},
2437
2788
  ...push ? { buildOnly: true } : {}
2789
+ };
2790
+ const recordedArns = recordedPushSubscriptions(recorded?.resources);
2791
+ const planned = push ? void 0 : composeStack({
2792
+ ...input,
2793
+ recordingSubscriptions: true
2794
+ });
2795
+ const pushes = planned ? topicPushes(planned, manifest) : [];
2796
+ const topics = planned && (pushes.length > 0 || recordedTopicSubscriptions(recorded?.resources).length > 0) ? {
2797
+ pushes,
2798
+ recorded: recorded?.resources
2799
+ } : void 0;
2800
+ const composed = topics && planned ? planned : composeStack({
2801
+ ...input,
2802
+ pushSubscriptions: recordedArns
2438
2803
  });
2439
2804
  if (mode === "build") assertRedisClient(workspace, composed, manifest, runnables);
2440
- const stack = options.pin ? withPinnedImages(composed, options.pin.file, options.pin.digests) : composed;
2441
- if (mode === "pull" && !ctx.dryRun) await assertImagesExist(deps.docker, tag, [...stack.apps, ...stack.workers]);
2805
+ const pinned = (unpinned) => options.pin ? withPinnedImages(unpinned, options.pin.file, options.pin.digests) : unpinned;
2806
+ const stack = pinned(composed);
2807
+ if (mode === "pull" && !ctx.dryRun) await assertImagesExist(deps.docker, tag, [
2808
+ ...stack.apps,
2809
+ ...stack.workers,
2810
+ ...stack.processors
2811
+ ]);
2442
2812
  if (stack.redis && !stack.local) ctx.secrets.mask(stack.redis.password);
2443
2813
  if (stack.logs && (!stack.local || stack.logs.passwordFromSecrets)) {
2444
2814
  ctx.secrets.mask(stack.logs.password);
@@ -2455,7 +2825,6 @@ async function validateCompose(ctx, deps, options = {}) {
2455
2825
  const override = stackOverrideFile(root, stage);
2456
2826
  const overrides = existsSync(override) ? [override] : [];
2457
2827
  if (overrides.length > 0) ctx.logger.info(`🧩 Merging docker-compose.${stage}.yml over the generated stack`);
2458
- const recorded = push ? void 0 : await ctx.state.read(stage);
2459
2828
  const previous = Object.fromEntries(Object.entries(recorded?.state.releases ?? {}).map(([name, releases]) => {
2460
2829
  const { ref, tag, digest } = releases.current;
2461
2830
  return [name, {
@@ -2468,6 +2837,20 @@ async function validateCompose(ctx, deps, options = {}) {
2468
2837
  mode,
2469
2838
  push,
2470
2839
  stack,
2840
+ ...topics ? { topicSubscriptions: {
2841
+ ...topics,
2842
+ restack: (arns) => {
2843
+ const merged = { ...recordedArns };
2844
+ for (const [worker, own] of Object.entries(arns)) merged[worker] = {
2845
+ ...merged[worker],
2846
+ ...own
2847
+ };
2848
+ return pinned(composeStack({
2849
+ ...input,
2850
+ pushSubscriptions: merged
2851
+ }));
2852
+ }
2853
+ } } : {},
2471
2854
  ...server ? { server } : {},
2472
2855
  dir,
2473
2856
  ref: {
@@ -2631,8 +3014,13 @@ async function stageSecrets(ctx, manifest) {
2631
3014
  function withPinnedImages(stack, file, digests) {
2632
3015
  const apps = pinImages(file, digests, stack.apps);
2633
3016
  const workers = pinImages(file, digests, stack.workers);
3017
+ const processors = pinImages(file, digests, stack.processors);
2634
3018
  const services = { ...stack.compose.services };
2635
- for (const image of [...apps, ...workers]) services[image.name] = {
3019
+ for (const image of [
3020
+ ...apps,
3021
+ ...workers,
3022
+ ...processors
3023
+ ]) services[image.name] = {
2636
3024
  ...services[image.name],
2637
3025
  image: image.ref
2638
3026
  };
@@ -2640,6 +3028,7 @@ function withPinnedImages(stack, file, digests) {
2640
3028
  ...stack,
2641
3029
  apps,
2642
3030
  workers,
3031
+ processors,
2643
3032
  compose: {
2644
3033
  ...stack.compose,
2645
3034
  services
@@ -2688,7 +3077,7 @@ async function planCompose(ctx, run) {
2688
3077
  action: run.previous[app.name] ? "reuse" : "create"
2689
3078
  });
2690
3079
  }
2691
- for (const worker of run.stack.workers) {
3080
+ for (const worker of [...run.stack.workers, ...run.stack.processors]) {
2692
3081
  planned({
2693
3082
  key: `image:${worker.name}`,
2694
3083
  resourceType: "image",
@@ -2702,6 +3091,16 @@ async function planCompose(ctx, run) {
2702
3091
  action: run.previous[worker.name] ? "reuse" : "create"
2703
3092
  });
2704
3093
  }
3094
+ for (const push of run.topicSubscriptions?.pushes ?? []) {
3095
+ const key = subscriptionKey(push.worker, push.name);
3096
+ const id = run.topicSubscriptions?.recorded?.[key]?.id;
3097
+ planned({
3098
+ key,
3099
+ resourceType: SUBSCRIPTION_TYPE,
3100
+ action: id ? "reuse" : "create",
3101
+ ...id ? { id } : {}
3102
+ });
3103
+ }
2705
3104
  if (!run.push) planned({
2706
3105
  key: run.stack.proxy === "caddy" ? "service:caddy" : `service:${EDGE_PROJECT}/${EDGE_SERVICE}`,
2707
3106
  resourceType: "service",
@@ -2714,7 +3113,7 @@ async function planCompose(ctx, run) {
2714
3113
  * migrations applied.
2715
3114
  */
2716
3115
  async function provisionCompose(ctx, run, deps) {
2717
- const { stack, ref } = run;
3116
+ const { ref } = run;
2718
3117
  if (run.push) return;
2719
3118
  if (run.generated.length > 0 && run.secrets) {
2720
3119
  await ctx.secrets.write(run.secrets);
@@ -2722,11 +3121,13 @@ async function provisionCompose(ctx, run, deps) {
2722
3121
  }
2723
3122
  const ports = edgePorts(deps.env);
2724
3123
  await assertEdgePorts(deps.docker, {
2725
- project: stack.project,
2726
- proxy: stack.proxy,
3124
+ project: run.stack.project,
3125
+ proxy: run.stack.proxy,
2727
3126
  ports,
2728
3127
  engine: ref
2729
3128
  });
3129
+ if (run.topicSubscriptions) await subscribeTopics(ctx, run, deps);
3130
+ const { stack } = run;
2730
3131
  run.files = await writeStack(ctx.cwd, run.dir, stack);
2731
3132
  printPlan(ctx, run);
2732
3133
  if (stack.proxy === "traefik") {
@@ -2765,6 +3166,36 @@ async function provisionCompose(ctx, run, deps) {
2765
3166
  if (stack.storage && stack.infra.includes("minio")) await prepareBuckets(ctx, run, deps);
2766
3167
  }
2767
3168
  /**
3169
+ * Subscribe each topic subscriber SNS pushes to — for a topic the stage set
3170
+ * to SNS by hand — record the ARNs, and compose the stack again with them,
3171
+ * before a file is written.
3172
+ */
3173
+ async function subscribeTopics(ctx, run, deps) {
3174
+ const { pushes, recorded, restack } = run.topicSubscriptions;
3175
+ if (pushes.length > 0) ctx.logger.info("\n📮 SNS subscriptions for the stage's own topics");
3176
+ run.stack = restack(await ensureTopicSubscriptions({
3177
+ pushes,
3178
+ recorded,
3179
+ secrets: run.secrets?.custom ?? {},
3180
+ stage: ctx.stage,
3181
+ env: deps.env,
3182
+ record: async (resource) => {
3183
+ await ctx.state.putResource(ctx.stage, resource);
3184
+ applied(ctx, run, {
3185
+ key: resource.key,
3186
+ resourceType: SUBSCRIPTION_TYPE,
3187
+ action: "ensure",
3188
+ id: resource.id,
3189
+ via: recorded?.[resource.key]?.id === resource.id ? "recorded" : "created"
3190
+ });
3191
+ },
3192
+ forget: async (key) => {
3193
+ await ctx.state.deleteResource(ctx.stage, key);
3194
+ },
3195
+ log: (line) => ctx.logger.info(line)
3196
+ }));
3197
+ }
3198
+ /**
2768
3199
  * Create each bucket the stack's MinIO serves, and the open paths of each
2769
3200
  * one a file server fronts — from this machine, on MinIO's loopback port,
2770
3201
  * the way `gkm dev` creates them.
@@ -2779,7 +3210,8 @@ async function prepareBuckets(ctx, run, deps) {
2779
3210
  done = [
2780
3211
  ...await applyBuckets(client, storage.buckets),
2781
3212
  ...await applyPolicies(client, storage.policies),
2782
- ...await applyLifecycles(client, storage.lifecycles)
3213
+ ...await applyLifecycles(client, storage.lifecycles),
3214
+ ...await applyNotifications(client, storage.objects, new Set(storage.webhooks.map((hook) => hook.bucket)))
2783
3215
  ];
2784
3216
  } finally {
2785
3217
  await tunnel.close();
@@ -2881,7 +3313,11 @@ async function migrateAndSeed(ctx, run, deps, port) {
2881
3313
  */
2882
3314
  async function buildCompose(ctx, run, deps) {
2883
3315
  const { stack, ref } = run;
2884
- const images = [...stack.apps, ...stack.workers];
3316
+ const images = [
3317
+ ...stack.apps,
3318
+ ...stack.workers,
3319
+ ...stack.processors
3320
+ ];
2885
3321
  const apps = images.map((image) => image.app);
2886
3322
  if (run.push) {
2887
3323
  run.files = await writeStack(ctx.cwd, run.dir, stack);
@@ -2953,7 +3389,7 @@ async function releaseCompose(ctx, run, deps) {
2953
3389
  url: app.url
2954
3390
  });
2955
3391
  }
2956
- for (const worker of stack.workers) applied(ctx, run, {
3392
+ for (const worker of [...stack.workers, ...stack.processors]) applied(ctx, run, {
2957
3393
  key: `service:${worker.name}`,
2958
3394
  resourceType: "service",
2959
3395
  action: "ensure",
@@ -2963,6 +3399,7 @@ async function releaseCompose(ctx, run, deps) {
2963
3399
  ctx.logger.info(`\n✅ ${stack.project} is running:`);
2964
3400
  for (const app of stack.apps) ctx.logger.info(` ${app.name.padEnd(12)} ${app.url}${app.auth ? ` 🔒 user: ${app.auth.user}, password: gkm secrets:show --stage ${ctx.stage} --reveal → ${app.auth.key}` : ""}`);
2965
3401
  for (const worker of stack.workers) ctx.logger.info(` ${worker.name.padEnd(12)} worker ${worker.id}`);
3402
+ for (const unit of stack.processors) ctx.logger.info(` ${unit.name.padEnd(12)} processors of ${unit.id}`);
2966
3403
  if (stack.local) ctx.logger.info(`\n🔐 Certificates are from Caddy's local CA: NODE_EXTRA_CA_CERTS=${join(stackDir(ctx.stage), "caddy-root.crt")}`);
2967
3404
  }
2968
3405
  /**
@@ -2973,7 +3410,11 @@ async function releaseCompose(ctx, run, deps) {
2973
3410
  async function recordImages(ctx, run) {
2974
3411
  const journal = await DeployJournal.open(ctx.state, ctx.stage, () => createComposeState(ctx.stage));
2975
3412
  const by = currentActor();
2976
- for (const app of [...run.stack.apps, ...run.stack.workers]) {
3413
+ for (const app of [
3414
+ ...run.stack.apps,
3415
+ ...run.stack.workers,
3416
+ ...run.stack.processors
3417
+ ]) {
2977
3418
  const image = run.images[app.app] ?? {
2978
3419
  ref: app.ref,
2979
3420
  tag: app.tag
@@ -3002,7 +3443,7 @@ async function verifyCompose(ctx, run, deps) {
3002
3443
  ca,
3003
3444
  edge
3004
3445
  })));
3005
- results.push(...await Promise.all(stack.workers.map((worker) => checkContainer(ctx, run, deps, worker.name))));
3446
+ results.push(...await Promise.all([...stack.workers, ...stack.processors].map((worker) => checkContainer(ctx, run, deps, worker.name))));
3006
3447
  if (stack.logs) results.push(await checkContainer(ctx, run, deps, LOGS_SERVICE));
3007
3448
  const down = results.filter((result) => !result.healthy).map(({ app, url, last }) => ({
3008
3449
  app,
@@ -3011,9 +3452,33 @@ async function verifyCompose(ctx, run, deps) {
3011
3452
  }));
3012
3453
  if (down.length > 0) throw new ComposeAppsUnhealthy(stack.project, down, stack.proxy);
3013
3454
  await pruneStageDns(ctx, run);
3455
+ await confirmSubscriptions(ctx, run, deps);
3014
3456
  if (stack.logs) reportLogs(ctx, stack.logs, stack.local, run.server);
3015
3457
  }
3016
3458
  /**
3459
+ * The SNS subscriptions the stage's providers made before the workers were
3460
+ * up: each still pending is subscribed again, so SNS resends its
3461
+ * confirmation to a worker now there to answer it, and waited on. One still
3462
+ * pending after that is said, not failed on — every app is healthy, and
3463
+ * rolling them back would not confirm it.
3464
+ */
3465
+ async function confirmSubscriptions(ctx, run, deps) {
3466
+ const stored = await ctx.state.read(ctx.stage);
3467
+ const left = await (deps.confirmSubscriptions ?? confirmPendingSubscriptions)({
3468
+ workspace: ctx.workspace,
3469
+ stage: ctx.stage,
3470
+ secrets: run.secrets?.custom ?? {},
3471
+ resources: stored?.resources,
3472
+ env: deps.env,
3473
+ log: (line) => ctx.logger.info(line),
3474
+ record: async (record) => {
3475
+ const { updatedAt: _, updatedBy: __, ...input } = record;
3476
+ await ctx.state.putResource(ctx.stage, input);
3477
+ }
3478
+ });
3479
+ if (left.length > 0) ctx.logger.warn(`⚠ ${new PushSubscriptionsUnconfirmed(left).message}`);
3480
+ }
3481
+ /**
3017
3482
  * A container's own health check, as Docker reports it: a worker's, which
3018
3483
  * has no route through the edge, and OpenObserve's — by default it has no
3019
3484
  * host there, and served publicly it answers only the addresses it allows,
@@ -3143,7 +3608,7 @@ function composeResult(ctx, run) {
3143
3608
  ...image?.digest ? { digest: image.digest } : {},
3144
3609
  url: app.url
3145
3610
  };
3146
- }), ...run.stack.workers.map((worker) => {
3611
+ }), ...[...run.stack.workers, ...run.stack.processors].map((worker) => {
3147
3612
  const image = run.images[worker.name];
3148
3613
  return {
3149
3614
  appName: worker.name,
@@ -3217,7 +3682,11 @@ async function writeStack(root, dir, stack) {
3217
3682
  recursive: true,
3218
3683
  force: true
3219
3684
  });
3220
- for (const app of [...stack.apps, ...stack.workers]) if (app.env) await write(join(dir, `${app.name}.env`), envFile(app.env), 384);
3685
+ for (const app of [
3686
+ ...stack.apps,
3687
+ ...stack.workers,
3688
+ ...stack.processors
3689
+ ]) if (app.env) await write(join(dir, `${app.name}.env`), envFile(app.env), 384);
3221
3690
  if (stack.logs && stack.compose.services["openobserve"]?.env_file) await write(join(dir, `${LOGS_SERVICE}.env`), envFile(stack.logs.env), 384);
3222
3691
  if (stack.redis && stack.compose.services["redis"]?.env_file) await write(join(dir, `${REDIS_SERVICE}.env`), envFile(stack.redis.env), 384);
3223
3692
  if (stack.backups) {
@@ -3255,6 +3724,7 @@ function printPlan(ctx, run) {
3255
3724
  if (run.server) ctx.logger.info(` → ${sshTarget(run.server)} (docker over ssh)`);
3256
3725
  for (const app of stack.apps) ctx.logger.info(` ${app.name.padEnd(12)} ${app.url.padEnd(40)} ${mode} ${app.ref}`);
3257
3726
  for (const worker of stack.workers) ctx.logger.info(` ${worker.name.padEnd(12)} ${`worker ${worker.id} (no route)`.padEnd(40)} ${mode} ${worker.ref}`);
3727
+ for (const unit of stack.processors) ctx.logger.info(` ${unit.name.padEnd(12)} ${`processors of ${unit.id}`.padEnd(40)} ${mode} ${unit.ref}`);
3258
3728
  if (stack.infra.length > 0) ctx.logger.info(` infrastructure: ${stack.infra.join(", ")}`);
3259
3729
  if (stack.backups) ctx.logger.info(` ${BACKUPS_SERVICE.padEnd(12)} ${stack.backups.databases.map((d) => d.file).join(", ")} — ${stack.backups.backups.describe}, to ${stack.backups.prefix}/`);
3260
3730
  ctx.logger.info(`\n📝 Wrote ${files.length} file(s) under ${stackDir(stack.stage)}/`);
@@ -3333,4 +3803,4 @@ function composeTarget(options = {}) {
3333
3803
  //#endregion
3334
3804
  export { dockerCompose as C, sshTarget as S, parseDigests as _, edgePorts as a, dockerHost as b, edgeRef as c, composeStack as d, derivedCredentials as f, edgeAlias as g, EDGE_PROJECT as h, ComposePushNeedsBuild as i, removeEdgeRoutes as l, stackPlan as m, compose_exports as n, stackOverrideFile as o, stackDir as p, ComposePinNeedsPull as r, deployedStackSecrets as s, composeTarget as t, composeProject as u, pinnedRef as v, projectNetwork as w, sshAccess as x, composeServer as y };
3335
3805
 
3336
- //# sourceMappingURL=compose-cHkF---f.mjs.map
3806
+ //# sourceMappingURL=compose-BFmdLig3.mjs.map