@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.
- package/dist/{EndpointGenerator-BDa6pPgE.cjs → EndpointGenerator-BGxf9jWE.cjs} +8 -4
- package/dist/{EndpointGenerator-BDa6pPgE.cjs.map → EndpointGenerator-BGxf9jWE.cjs.map} +1 -1
- package/dist/{EndpointGenerator-CP_z_E3e.mjs → EndpointGenerator-errMeldx.mjs} +8 -4
- package/dist/{EndpointGenerator-CP_z_E3e.mjs.map → EndpointGenerator-errMeldx.mjs.map} +1 -1
- package/dist/{GoDaddyProvider-BlB5nJky.mjs → GoDaddyProvider-BN1dODm9.mjs} +3 -3
- package/dist/{GoDaddyProvider-BlB5nJky.mjs.map → GoDaddyProvider-BN1dODm9.mjs.map} +1 -1
- package/dist/{GoDaddyProvider-Dubq-pCc.cjs → GoDaddyProvider-BTdtqhI_.cjs} +3 -3
- package/dist/{GoDaddyProvider-Dubq-pCc.cjs.map → GoDaddyProvider-BTdtqhI_.cjs.map} +1 -1
- package/dist/{HostingerProvider-BNpS9aRJ.mjs → HostingerProvider-Cjh_2Ml6.mjs} +2 -2
- package/dist/{HostingerProvider-BNpS9aRJ.mjs.map → HostingerProvider-Cjh_2Ml6.mjs.map} +1 -1
- package/dist/{HostingerProvider-DR1iws1P.cjs → HostingerProvider-OdQmKXE3.cjs} +2 -2
- package/dist/{HostingerProvider-DR1iws1P.cjs.map → HostingerProvider-OdQmKXE3.cjs.map} +1 -1
- package/dist/{LegacyStateStore-ClvUC8qy.cjs → LegacyStateStore-C2S8TGwx.cjs} +3 -3
- package/dist/{LegacyStateStore-ClvUC8qy.cjs.map → LegacyStateStore-C2S8TGwx.cjs.map} +1 -1
- package/dist/{LegacyStateStore-DGALQGZu.mjs → LegacyStateStore-DgXmuG87.mjs} +3 -3
- package/dist/{LegacyStateStore-DGALQGZu.mjs.map → LegacyStateStore-DgXmuG87.mjs.map} +1 -1
- package/dist/{LocalStateStore-43kSYNpw.cjs → LocalStateStore-CaJw9H-3.cjs} +2 -2
- package/dist/{LocalStateStore-43kSYNpw.cjs.map → LocalStateStore-CaJw9H-3.cjs.map} +1 -1
- package/dist/{LocalStateStore-CBYUzVR5.mjs → LocalStateStore-ojY_BiMK.mjs} +2 -2
- package/dist/{LocalStateStore-CBYUzVR5.mjs.map → LocalStateStore-ojY_BiMK.mjs.map} +1 -1
- package/dist/{OpenApiTsGenerator-CsrukCE-.mjs → OpenApiTsGenerator-W_HRXH1X.mjs} +2 -2
- package/dist/{OpenApiTsGenerator-CsrukCE-.mjs.map → OpenApiTsGenerator-W_HRXH1X.mjs.map} +1 -1
- package/dist/{OpenApiTsGenerator-FNhY7Ssz.cjs → OpenApiTsGenerator-cQgtxWQo.cjs} +2 -2
- package/dist/{OpenApiTsGenerator-FNhY7Ssz.cjs.map → OpenApiTsGenerator-cQgtxWQo.cjs.map} +1 -1
- package/dist/{S3StateStore-DLHjDYYP.mjs → S3StateStore-BSPkxhrx.mjs} +2 -2
- package/dist/{S3StateStore-DLHjDYYP.mjs.map → S3StateStore-BSPkxhrx.mjs.map} +1 -1
- package/dist/{S3StateStore-ahE7hSNK.cjs → S3StateStore-BqugXIcb.cjs} +2 -2
- package/dist/{S3StateStore-ahE7hSNK.cjs.map → S3StateStore-BqugXIcb.cjs.map} +1 -1
- package/dist/{SSMStateStore-qIzIuazA.cjs → SSMStateStore-B0OsuT_k.cjs} +2 -2
- package/dist/{SSMStateStore-qIzIuazA.cjs.map → SSMStateStore-B0OsuT_k.cjs.map} +1 -1
- package/dist/{SSMStateStore-j5RuMkch.mjs → SSMStateStore-DM85OiH0.mjs} +2 -2
- package/dist/{SSMStateStore-j5RuMkch.mjs.map → SSMStateStore-DM85OiH0.mjs.map} +1 -1
- package/dist/{SubscriberGenerator-C8CPxrCK.cjs → SubscriberGenerator-BDUp7OeY.cjs} +237 -54
- package/dist/SubscriberGenerator-BDUp7OeY.cjs.map +1 -0
- package/dist/{SubscriberGenerator-y3pncae_.mjs → SubscriberGenerator-ocQ2yj0K.mjs} +227 -56
- package/dist/SubscriberGenerator-ocQ2yj0K.mjs.map +1 -0
- package/dist/{add-Bm-C56lH.mjs → add-C7ZBXIzM.mjs} +6 -6
- package/dist/{add-Bm-C56lH.mjs.map → add-C7ZBXIzM.mjs.map} +1 -1
- package/dist/{add-C2CvXrYw.cjs → add-CrxvmSpq.cjs} +7 -7
- package/dist/{add-C2CvXrYw.cjs.map → add-CrxvmSpq.cjs.map} +1 -1
- package/dist/{bundler-Dk7tAvAX.cjs → bundler-DVUXW95p.cjs} +5 -5
- package/dist/{bundler-Dk7tAvAX.cjs.map → bundler-DVUXW95p.cjs.map} +1 -1
- package/dist/{bundler-DnkFt4mn.mjs → bundler-DpZ9uBGN.mjs} +5 -5
- package/dist/{bundler-DnkFt4mn.mjs.map → bundler-DpZ9uBGN.mjs.map} +1 -1
- package/dist/{cli-Di464j1O.cjs → cli-BMRhE6cZ.cjs} +4 -4
- package/dist/{cli-Di464j1O.cjs.map → cli-BMRhE6cZ.cjs.map} +1 -1
- package/dist/{cli-CVeHFXNE.mjs → cli-BWfYyy1k.mjs} +4 -4
- package/dist/{cli-CVeHFXNE.mjs.map → cli-BWfYyy1k.mjs.map} +1 -1
- package/dist/{compose-cHkF---f.mjs → compose-BFmdLig3.mjs} +525 -55
- package/dist/compose-BFmdLig3.mjs.map +1 -0
- package/dist/{compose-ChVvb3xM.cjs → compose-Bqcy0lqo.cjs} +554 -84
- package/dist/compose-Bqcy0lqo.cjs.map +1 -0
- package/dist/{config-DrOX29sT.d.mts → config-CdytFgSR.d.cts} +3 -4
- package/dist/config-CdytFgSR.d.cts.map +1 -0
- package/dist/{config-B3iz9BtK.d.cts → config-_y1FGBWR.d.mts} +3 -4
- package/dist/config-_y1FGBWR.d.mts.map +1 -0
- package/dist/config.cjs +2 -2
- package/dist/config.d.cts +3 -3
- package/dist/config.d.mts +3 -3
- package/dist/config.mjs +2 -2
- package/dist/{credentialSchemas-B67WJOOm.cjs → credentialSchemas-DFxvGEwa.cjs} +2 -2
- package/dist/{credentialSchemas-B67WJOOm.cjs.map → credentialSchemas-DFxvGEwa.cjs.map} +1 -1
- package/dist/{credentialSchemas-DDOLRCLF.mjs → credentialSchemas-DaMpt-Dx.mjs} +2 -2
- package/dist/{credentialSchemas-DDOLRCLF.mjs.map → credentialSchemas-DaMpt-Dx.mjs.map} +1 -1
- package/dist/{credentials-Zqr3QQYB.cjs → credentials-D7XJypNd.cjs} +173 -3
- package/dist/credentials-D7XJypNd.cjs.map +1 -0
- package/dist/{credentials-B5N5yKk6.mjs → credentials-DpfnUvIR.mjs} +132 -4
- package/dist/credentials-DpfnUvIR.mjs.map +1 -0
- package/dist/deploy/public.cjs +16 -17
- package/dist/deploy/public.cjs.map +1 -1
- package/dist/deploy/public.d.cts +3 -5
- package/dist/deploy/public.d.cts.map +1 -1
- package/dist/deploy/public.d.mts +3 -5
- package/dist/deploy/public.d.mts.map +1 -1
- package/dist/deploy/public.mjs +11 -12
- package/dist/deploy/public.mjs.map +1 -1
- package/dist/{deploy-DO4djCHb.mjs → deploy-D8pUA9Y0.mjs} +1240 -130
- package/dist/deploy-D8pUA9Y0.mjs.map +1 -0
- package/dist/{deploy-BffR_uEN.cjs → deploy-DVrNjX1X.cjs} +1261 -169
- package/dist/deploy-DVrNjX1X.cjs.map +1 -0
- package/dist/{discover-C5roJuc9.mjs → discover-CzGM5P3p.mjs} +136 -14
- package/dist/discover-CzGM5P3p.mjs.map +1 -0
- package/dist/{discover-DozUKs8f.cjs → discover-DL6h-J97.cjs} +158 -12
- package/dist/discover-DL6h-J97.cjs.map +1 -0
- package/dist/{dokploy-BcZm93Cc.mjs → dokploy-CzDGNB6A.mjs} +2 -2
- package/dist/{dokploy-BcZm93Cc.mjs.map → dokploy-CzDGNB6A.mjs.map} +1 -1
- package/dist/{dokploy-CsdvfH3t.cjs → dokploy-TtK6Zyl3.cjs} +2 -2
- package/dist/{dokploy-CsdvfH3t.cjs.map → dokploy-TtK6Zyl3.cjs.map} +1 -1
- package/dist/{drivers-BjGDYfnC.mjs → drivers-C-VqMtld.mjs} +2 -2
- package/dist/{drivers-BjGDYfnC.mjs.map → drivers-C-VqMtld.mjs.map} +1 -1
- package/dist/{drivers-DbjiYpW6.cjs → drivers-L29WvE95.cjs} +2 -2
- package/dist/{drivers-DbjiYpW6.cjs.map → drivers-L29WvE95.cjs.map} +1 -1
- package/dist/{eas-CN_cUXth.cjs → eas-D3clzkni.cjs} +2 -2
- package/dist/{eas-CN_cUXth.cjs.map → eas-D3clzkni.cjs.map} +1 -1
- package/dist/{eas-CBShP20v.mjs → eas-DaobzDks.mjs} +2 -2
- package/dist/{eas-CBShP20v.mjs.map → eas-DaobzDks.mjs.map} +1 -1
- package/dist/{emit-Dmc1nvdS.cjs → emit-CYloptRS.cjs} +34 -1
- package/dist/emit-CYloptRS.cjs.map +1 -0
- package/dist/{emit-C1e8qD08.mjs → emit-CiJGdy_f.mjs} +34 -1
- package/dist/emit-CiJGdy_f.mjs.map +1 -0
- package/dist/{encryption-CPDM49Hi.cjs → encryption-DBkIFiR2.cjs} +3 -3
- package/dist/{encryption-CPDM49Hi.cjs.map → encryption-DBkIFiR2.cjs.map} +1 -1
- package/dist/{encryption-CITa6dXL.mjs → encryption-DV31gEY8.mjs} +3 -3
- package/dist/{encryption-CITa6dXL.mjs.map → encryption-DV31gEY8.mjs.map} +1 -1
- package/dist/{engine-CTnHmHjz.mjs → engine-CFOR9h6X.mjs} +140 -16
- package/dist/engine-CFOR9h6X.mjs.map +1 -0
- package/dist/{engine-D-jfMVj-.cjs → engine-CuZUGbEh.cjs} +143 -19
- package/dist/engine-CuZUGbEh.cjs.map +1 -0
- package/dist/{github-D56PSK39.cjs → github-CsvtWSBW.cjs} +115 -47
- package/dist/github-CsvtWSBW.cjs.map +1 -0
- package/dist/{github-9RlA7EjN.mjs → github-Nep8pI4_.mjs} +113 -45
- package/dist/github-Nep8pI4_.mjs.map +1 -0
- package/dist/{googlePlay-SX_jQeJf.cjs → googlePlay-C-UfBXJU.cjs} +5 -5
- package/dist/{googlePlay-SX_jQeJf.cjs.map → googlePlay-C-UfBXJU.cjs.map} +1 -1
- package/dist/{googlePlay-COi5LXON.mjs → googlePlay-lF7Atm_N.mjs} +4 -4
- package/dist/{googlePlay-COi5LXON.mjs.map → googlePlay-lF7Atm_N.mjs.map} +1 -1
- package/dist/{index-DcyRA5Rf.d.mts → index-DrkV7N5w.d.mts} +2 -2
- package/dist/{index-DcyRA5Rf.d.mts.map → index-DrkV7N5w.d.mts.map} +1 -1
- package/dist/{index-DjLSMgAL.d.cts → index-yaXZ9m-J.d.cts} +2 -2
- package/dist/{index-DjLSMgAL.d.cts.map → index-yaXZ9m-J.d.cts.map} +1 -1
- package/dist/index.cjs +166 -85
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +149 -68
- package/dist/index.mjs.map +1 -1
- package/dist/{local-BwC2v2Y8.mjs → local-B7Zv1NoA.mjs} +2 -2
- package/dist/{local-BwC2v2Y8.mjs.map → local-B7Zv1NoA.mjs.map} +1 -1
- package/dist/{local-DNhnkugT.cjs → local-OPC9BCZt.cjs} +3 -3
- package/dist/{local-DNhnkugT.cjs.map → local-OPC9BCZt.cjs.map} +1 -1
- package/dist/{migrate-CrtxXeGY.cjs → migrate-Dp3GhFnf.cjs} +4 -4
- package/dist/{migrate-CrtxXeGY.cjs.map → migrate-Dp3GhFnf.cjs.map} +1 -1
- package/dist/{migrate-0J6gsfrQ.mjs → migrate-NfbPVhi9.mjs} +4 -4
- package/dist/{migrate-0J6gsfrQ.mjs.map → migrate-NfbPVhi9.mjs.map} +1 -1
- package/dist/{migrate-DJa_Dlsl.mjs → migrate-_sBX0pve.mjs} +3 -3
- package/dist/{migrate-DJa_Dlsl.mjs.map → migrate-_sBX0pve.mjs.map} +1 -1
- package/dist/{migrate-BCz1lkh_.cjs → migrate-hlNKNWmg.cjs} +3 -3
- package/dist/{migrate-BCz1lkh_.cjs.map → migrate-hlNKNWmg.cjs.map} +1 -1
- package/dist/openapi.cjs +3 -3
- package/dist/openapi.d.cts +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/openapi.mjs +3 -3
- package/dist/{publish-CuQlOW5p.mjs → publish-BuUVaZVy.mjs} +2 -2
- package/dist/{publish-CuQlOW5p.mjs.map → publish-BuUVaZVy.mjs.map} +1 -1
- package/dist/{publish-qSpnvmdX.cjs → publish-ZOo4xEuZ.cjs} +2 -2
- package/dist/{publish-qSpnvmdX.cjs.map → publish-ZOo4xEuZ.cjs.map} +1 -1
- package/dist/{ready-BOfogqzr.mjs → ready-Ca883OvY.mjs} +6 -6
- package/dist/{ready-BOfogqzr.mjs.map → ready-Ca883OvY.mjs.map} +1 -1
- package/dist/{ready-BFFP838V.cjs → ready-qnqp6JAY.cjs} +6 -6
- package/dist/{ready-BFFP838V.cjs.map → ready-qnqp6JAY.cjs.map} +1 -1
- package/dist/reconcile/public.cjs +2 -2
- package/dist/reconcile/public.d.cts +1 -2
- package/dist/reconcile/public.d.cts.map +1 -1
- package/dist/reconcile/public.d.mts +1 -2
- package/dist/reconcile/public.d.mts.map +1 -1
- package/dist/reconcile/public.mjs +2 -2
- package/dist/{resolve-j8hUcbGz.mjs → resolve-BCU9pYDx.mjs} +4 -4
- package/dist/{resolve-j8hUcbGz.mjs.map → resolve-BCU9pYDx.mjs.map} +1 -1
- package/dist/{resolve-AsfohXd1.cjs → resolve-C9QqOl5t.cjs} +4 -4
- package/dist/{resolve-AsfohXd1.cjs.map → resolve-C9QqOl5t.cjs.map} +1 -1
- package/dist/{rollback-Bxq_URgL.cjs → rollback-CTWdDBgB.cjs} +7 -7
- package/dist/{rollback-Bxq_URgL.cjs.map → rollback-CTWdDBgB.cjs.map} +1 -1
- package/dist/{rollback-JgbAY43C.mjs → rollback-CWyyTQ0B.mjs} +7 -7
- package/dist/{rollback-JgbAY43C.mjs.map → rollback-CWyyTQ0B.mjs.map} +1 -1
- package/dist/{runtime-DIZTeGJV.d.cts → runtime-DCkfI2E7.d.mts} +2 -2
- package/dist/{runtime-DIZTeGJV.d.cts.map → runtime-DCkfI2E7.d.mts.map} +1 -1
- package/dist/{runtime-WMLJY-db.d.mts → runtime-Ie4znWIB.d.cts} +2 -2
- package/dist/{runtime-WMLJY-db.d.mts.map → runtime-Ie4znWIB.d.cts.map} +1 -1
- package/dist/{s3-B4FTQQkV.mjs → s3-BZzSnker.mjs} +2 -2
- package/dist/{s3-B4FTQQkV.mjs.map → s3-BZzSnker.mjs.map} +1 -1
- package/dist/{s3-DwKLXwUT.cjs → s3-pqgmCTOq.cjs} +2 -2
- package/dist/{s3-DwKLXwUT.cjs.map → s3-pqgmCTOq.cjs.map} +1 -1
- package/dist/sandbox/credentials-worker.cjs +2 -2
- package/dist/sandbox/credentials-worker.mjs +2 -2
- package/dist/sandbox/discover-worker.cjs +1 -1
- package/dist/sandbox/discover-worker.mjs +1 -1
- package/dist/sandbox/migrate-worker.cjs +1 -1
- package/dist/sandbox/migrate-worker.mjs +1 -1
- package/dist/{seeds-Dv78P9LS.mjs → seeds-CK7w79VC.mjs} +848 -61
- package/dist/seeds-CK7w79VC.mjs.map +1 -0
- package/dist/{seeds-BWC-qjSI.cjs → seeds-CPEcmoPn.cjs} +953 -64
- package/dist/seeds-CPEcmoPn.cjs.map +1 -0
- package/dist/{serverSetup-DB6sKwAy.mjs → serverSetup-BAPNuNEX.mjs} +6 -6
- package/dist/{serverSetup-DB6sKwAy.mjs.map → serverSetup-BAPNuNEX.mjs.map} +1 -1
- package/dist/{serverSetup-CtV80a5M.cjs → serverSetup-C6t6Vtt7.cjs} +6 -6
- package/dist/{serverSetup-CtV80a5M.cjs.map → serverSetup-C6t6Vtt7.cjs.map} +1 -1
- package/dist/{sniffer-ctpwBBkT.cjs → sniffer-DdX_WN_-.cjs} +9 -9
- package/dist/{sniffer-ctpwBBkT.cjs.map → sniffer-DdX_WN_-.cjs.map} +1 -1
- package/dist/{sniffer-qDFUQmWP.mjs → sniffer-g9v7cqDN.mjs} +9 -9
- package/dist/{sniffer-qDFUQmWP.mjs.map → sniffer-g9v7cqDN.mjs.map} +1 -1
- package/dist/{dns-Bmnuttja.cjs → snsSubscriptions-5Cqww8S7.cjs} +351 -3
- package/dist/snsSubscriptions-5Cqww8S7.cjs.map +1 -0
- package/dist/{dns-DFvO5a53.mjs → snsSubscriptions-BmYeeTH8.mjs} +280 -4
- package/dist/snsSubscriptions-BmYeeTH8.mjs.map +1 -0
- package/dist/{sst-C1pxbFEd.mjs → sst-CTqZZl1_.mjs} +3 -4
- package/dist/{sst-C1pxbFEd.mjs.map → sst-CTqZZl1_.mjs.map} +1 -1
- package/dist/{sst-B-Uz4cvm.cjs → sst-lEkBwzGc.cjs} +4 -5
- package/dist/{sst-B-Uz4cvm.cjs.map → sst-lEkBwzGc.cjs.map} +1 -1
- package/dist/{stage-C7lf36az.mjs → stage-C-I2dVw1.mjs} +6 -6
- package/dist/{stage-C7lf36az.mjs.map → stage-C-I2dVw1.mjs.map} +1 -1
- package/dist/{stage-DzALYUwY.cjs → stage-CUE5pMmw.cjs} +7 -7
- package/dist/{stage-DzALYUwY.cjs.map → stage-CUE5pMmw.cjs.map} +1 -1
- package/dist/{stages-B7mCWI_R.cjs → stages-3yUZaZeA.cjs} +2 -2
- package/dist/{stages-B7mCWI_R.cjs.map → stages-3yUZaZeA.cjs.map} +1 -1
- package/dist/{stages-DSltTW2d.mjs → stages-DTTv0NZW.mjs} +2 -2
- package/dist/{stages-DSltTW2d.mjs.map → stages-DTTv0NZW.mjs.map} +1 -1
- package/dist/target/public.cjs +1 -1
- package/dist/target/public.d.cts +2 -2
- package/dist/target/public.d.mts +2 -2
- package/dist/target/public.mjs +1 -1
- package/dist/{terminal-DJlqcW_U.cjs → terminal-PKhPAajN.cjs} +3 -3
- package/dist/{terminal-DJlqcW_U.cjs.map → terminal-PKhPAajN.cjs.map} +1 -1
- package/dist/{terminal-CW_dWeTe.mjs → terminal-bqY33lpN.mjs} +3 -3
- package/dist/{terminal-CW_dWeTe.mjs.map → terminal-bqY33lpN.mjs.map} +1 -1
- package/dist/{types-Q2SC_ly2.d.cts → types-Bmjs4jZV.d.mts} +248 -4
- package/dist/types-Bmjs4jZV.d.mts.map +1 -0
- package/dist/{types-C8gXOLWL.d.mts → types-D0IsQxES.d.cts} +249 -3
- package/dist/types-D0IsQxES.d.cts.map +1 -0
- package/dist/vitest.cjs +1 -1
- package/dist/vitest.mjs +1 -1
- package/dist/workspace/index.cjs +1 -1
- package/dist/workspace/index.d.cts +2 -2
- package/dist/workspace/index.d.mts +2 -2
- package/dist/workspace/index.mjs +1 -1
- package/dist/{workspace-DUnnVjqC.mjs → workspace-DBHF9Gba.mjs} +5 -3
- package/dist/workspace-DBHF9Gba.mjs.map +1 -0
- package/dist/{workspace-C82GLPnf.cjs → workspace-Du4Afb03.cjs} +5 -3
- package/dist/workspace-Du4Afb03.cjs.map +1 -0
- package/package.json +17 -15
- package/dist/SubscriberGenerator-C8CPxrCK.cjs.map +0 -1
- package/dist/SubscriberGenerator-y3pncae_.mjs.map +0 -1
- package/dist/compose-ChVvb3xM.cjs.map +0 -1
- package/dist/compose-cHkF---f.mjs.map +0 -1
- package/dist/config-B3iz9BtK.d.cts.map +0 -1
- package/dist/config-DrOX29sT.d.mts.map +0 -1
- package/dist/credentials-B5N5yKk6.mjs.map +0 -1
- package/dist/credentials-Zqr3QQYB.cjs.map +0 -1
- package/dist/deploy-BffR_uEN.cjs.map +0 -1
- package/dist/deploy-DO4djCHb.mjs.map +0 -1
- package/dist/discover-C08meHhu.d.mts +0 -125
- package/dist/discover-C08meHhu.d.mts.map +0 -1
- package/dist/discover-C5roJuc9.mjs.map +0 -1
- package/dist/discover-DipvfWpK.d.cts +0 -125
- package/dist/discover-DipvfWpK.d.cts.map +0 -1
- package/dist/discover-DozUKs8f.cjs.map +0 -1
- package/dist/dns-Bmnuttja.cjs.map +0 -1
- package/dist/dns-DFvO5a53.mjs.map +0 -1
- package/dist/emit-C1e8qD08.mjs.map +0 -1
- package/dist/emit-Dmc1nvdS.cjs.map +0 -1
- package/dist/engine-CTnHmHjz.mjs.map +0 -1
- package/dist/engine-D-jfMVj-.cjs.map +0 -1
- package/dist/github-9RlA7EjN.mjs.map +0 -1
- package/dist/github-D56PSK39.cjs.map +0 -1
- package/dist/run-BJLIerMM.mjs +0 -133
- package/dist/run-BJLIerMM.mjs.map +0 -1
- package/dist/run-fVZ-rLnc.cjs +0 -174
- package/dist/run-fVZ-rLnc.cjs.map +0 -1
- package/dist/sandbox-Co1y2CmF.d.mts +0 -104
- package/dist/sandbox-Co1y2CmF.d.mts.map +0 -1
- package/dist/sandbox-DvNqr6od.d.cts +0 -104
- package/dist/sandbox-DvNqr6od.d.cts.map +0 -1
- package/dist/seeds-BWC-qjSI.cjs.map +0 -1
- package/dist/seeds-Dv78P9LS.mjs.map +0 -1
- package/dist/types-C8gXOLWL.d.mts.map +0 -1
- package/dist/types-Q2SC_ly2.d.cts.map +0 -1
- package/dist/workspace-C82GLPnf.cjs.map +0 -1
- package/dist/workspace-DUnnVjqC.mjs.map +0 -1
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-BhDjJH2R.mjs";
|
|
2
2
|
import { t as GkmError } from "./errors-D8Gwp5kU.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import { B as LOGS_PORT, H as provisioningOf, R as stageSampleRate, U as stageProvider, _ as PUBLIC_ENV_PREFIXES, a as allConstructGlobs, ct as UnknownSecretsStoreProvider, ft as SERVER_IPV4_KEY, ht as isReservedStageKey, j as TEST_STAGE, pt as SERVER_IPV6_KEY, ut as s3SecretsLocation, v as getPublicEnvPrefix, z as telemetryEnv } from "./workspace-
|
|
3
|
+
import { s as run$1, x as gkmHome } from "./credentials-DpfnUvIR.mjs";
|
|
4
|
+
import { B as LOGS_PORT, H as provisioningOf, R as stageSampleRate, U as stageProvider, _ as PUBLIC_ENV_PREFIXES, a as allConstructGlobs, ct as UnknownSecretsStoreProvider, ft as SERVER_IPV4_KEY, ht as isReservedStageKey, j as TEST_STAGE, pt as SERVER_IPV6_KEY, ut as s3SecretsLocation, v as getPublicEnvPrefix, z as telemetryEnv } from "./workspace-DBHF9Gba.mjs";
|
|
5
5
|
import { t as appKey } from "./derive-B4vDpzgP.mjs";
|
|
6
|
-
import {
|
|
6
|
+
import { c as DEFAULT_CACHE, d as holdsEveryCache, i as readFakes, l as DEFAULT_EVENTS$1, o as objectsRoute, r as readCredentialFakes, t as SubscriberGenerator, u as DEFAULT_STORAGE } from "./SubscriberGenerator-ocQ2yj0K.mjs";
|
|
7
7
|
import { n as deployIdentity } from "./identity-CBXr4wNi.mjs";
|
|
8
|
-
import { a as startTelemetryCode, n as runtimeFor, s as writeTelemetryModule, u as telemetryArgs } from "./EndpointGenerator-
|
|
9
|
-
import { d as ConstructGenerator, f as output, i as discover } from "./discover-
|
|
8
|
+
import { a as startTelemetryCode, n as runtimeFor, s as writeTelemetryModule, u as telemetryArgs } from "./EndpointGenerator-errMeldx.mjs";
|
|
9
|
+
import { d as ConstructGenerator, f as output, h as eventsEdgeBucket, i as discover } from "./discover-CzGM5P3p.mjs";
|
|
10
10
|
import { ConfigNotFound, NotInAnApp, loadWorkspaceAppInfo, loadWorkspaceConfig } from "./config.mjs";
|
|
11
|
-
import { c as runtimeOf } from "./resolve-
|
|
11
|
+
import { c as runtimeOf } from "./resolve-BCU9pYDx.mjs";
|
|
12
12
|
import { basename, dirname, isAbsolute, join, parse, relative, resolve, sep } from "node:path";
|
|
13
13
|
import { constants, existsSync, readFileSync } from "node:fs";
|
|
14
14
|
import { appendFile, chmod, copyFile, mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
@@ -16,7 +16,7 @@ import { homedir, hostname, networkInterfaces, userInfo } from "node:os";
|
|
|
16
16
|
import { execFile, spawn } from "node:child_process";
|
|
17
17
|
import { createServer } from "node:net";
|
|
18
18
|
import { createCipheriv, createDecipheriv, createHash, randomBytes, randomInt } from "node:crypto";
|
|
19
|
-
import { CLIENT_IP_HEADER, DEFAULT_POSTGRES_VERSION, PUBLIC_PREFIX, TELEMETRY_KEYS, cacheTable, cookieDomain, dependenciesOf, dependentsOf, drawSecret, externalApiUrl, kebabCase, mobileOrigins, provideKey, providedKeyFor, provisionOrder, publicEnvFor, schemeBase } from "@geekmidas/manifest";
|
|
19
|
+
import { CLIENT_IP_HEADER, DEFAULT_POSTGRES_VERSION, PUBLIC_PREFIX, TELEMETRY_KEYS, bucketExpiry, cacheTable, cookieDomain, dependenciesOf, dependentsOf, drawSecret, externalApiUrl, kebabCase, mobileOrigins, provideKey, providedKeyFor, provisionOrder, publicEnvFor, schemeBase } from "@geekmidas/manifest";
|
|
20
20
|
import { formatKeyring, generateKeyring } from "@geekmidas/constructs/encryption";
|
|
21
21
|
import { ownerRole, readerRole, roleStatements } from "@geekmidas/db/pg/roles";
|
|
22
22
|
import * as snsUrl from "@geekmidas/events/sns/url";
|
|
@@ -25,8 +25,9 @@ import { cacheTableStatements } from "@geekmidas/cache/postgres";
|
|
|
25
25
|
import { config } from "dotenv";
|
|
26
26
|
import { promisify } from "node:util";
|
|
27
27
|
import { Cron } from "@geekmidas/constructs/crons";
|
|
28
|
+
import { Processor } from "@geekmidas/constructs/processors";
|
|
28
29
|
import { Queue } from "@geekmidas/constructs/queue";
|
|
29
|
-
import { CreateBucketCommand, DeleteBucketLifecycleCommand, GetBucketLifecycleConfigurationCommand, GetBucketPolicyCommand, HeadBucketCommand, ListBucketsCommand, PutBucketLifecycleConfigurationCommand, PutBucketPolicyCommand, S3Client } from "@aws-sdk/client-s3";
|
|
30
|
+
import { CreateBucketCommand, DeleteBucketLifecycleCommand, GetBucketLifecycleConfigurationCommand, GetBucketNotificationConfigurationCommand, GetBucketPolicyCommand, HeadBucketCommand, ListBucketsCommand, PutBucketLifecycleConfigurationCommand, PutBucketNotificationConfigurationCommand, PutBucketPolicyCommand, S3Client } from "@aws-sdk/client-s3";
|
|
30
31
|
import { CreateTopicCommand, GetTopicAttributesCommand, SNSClient } from "@aws-sdk/client-sns";
|
|
31
32
|
import { CreateQueueCommand, GetQueueUrlCommand, SQSClient } from "@aws-sdk/client-sqs";
|
|
32
33
|
import { Client } from "pg";
|
|
@@ -486,6 +487,21 @@ function volumeOf(container) {
|
|
|
486
487
|
}[container];
|
|
487
488
|
}
|
|
488
489
|
//#endregion
|
|
490
|
+
//#region src/generators/processorRoute.ts
|
|
491
|
+
/**
|
|
492
|
+
* Where `gkm dev` serves a file server's processors, on the dev server of the
|
|
493
|
+
* app holding them: named after the file server.
|
|
494
|
+
*
|
|
495
|
+
* One definition for both sides — the dev server mounts it, and the local
|
|
496
|
+
* edge rewrites each processor pattern on the file server's host onto it — so
|
|
497
|
+
* the path the edge sends and the path the server answers cannot drift.
|
|
498
|
+
*/
|
|
499
|
+
const PROCESSORS_DEV_PREFIX = "/__gkm/processors/";
|
|
500
|
+
/** The base a file server's processors answer under, in `gkm dev`. */
|
|
501
|
+
function processorsDevRoute(fileServerId) {
|
|
502
|
+
return `${PROCESSORS_DEV_PREFIX}${fileServerId}`;
|
|
503
|
+
}
|
|
504
|
+
//#endregion
|
|
489
505
|
//#region src/reconcile/caddyfile.ts
|
|
490
506
|
/**
|
|
491
507
|
* The Caddy configuration a plan implies.
|
|
@@ -535,7 +551,7 @@ const EDGE_KINDS = {
|
|
|
535
551
|
* Docker Desktop resolves this name already; on Linux the compose definition
|
|
536
552
|
* maps it to the host gateway.
|
|
537
553
|
*/
|
|
538
|
-
const HOST_GATEWAY = "host.docker.internal";
|
|
554
|
+
const HOST_GATEWAY$1 = "host.docker.internal";
|
|
539
555
|
/**
|
|
540
556
|
* The suffix every local host shares.
|
|
541
557
|
*
|
|
@@ -552,19 +568,26 @@ const LOCAL_TLD = "localhost";
|
|
|
552
568
|
* are a legitimate arrangement — two cache behaviours, one origin — and naming
|
|
553
569
|
* the host after the bucket would make them collide.
|
|
554
570
|
*/
|
|
555
|
-
function sitesFor(plan, project, addresses = {}) {
|
|
571
|
+
function sitesFor(plan, project, addresses = {}, processorAddresses = {}) {
|
|
556
572
|
const byId = new Map(plan.resources.map((r) => [r.id, r]));
|
|
557
573
|
const sites = [];
|
|
558
574
|
for (const resource of plan.resources) {
|
|
559
575
|
if (!EDGE_KINDS[resource.kind]) continue;
|
|
560
|
-
const host = hostFor(resource, project);
|
|
576
|
+
const host = hostFor$1(resource, project);
|
|
561
577
|
if (resource.kind === "file-server") {
|
|
562
578
|
const origin = resource.of ? byId.get(resource.of) : void 0;
|
|
563
579
|
if (!origin) continue;
|
|
580
|
+
const address = processorAddresses[resource.id];
|
|
581
|
+
const port = address ? portOf(address) : void 0;
|
|
564
582
|
sites.push({
|
|
565
583
|
host,
|
|
566
584
|
upstream: "http://minio:9000",
|
|
567
|
-
rewrite: `/${origin.name}{uri}
|
|
585
|
+
rewrite: `/${origin.name}{uri}`,
|
|
586
|
+
...resource.processors?.length && port ? { processors: {
|
|
587
|
+
prefixes: resource.processors,
|
|
588
|
+
upstream: `http://${HOST_GATEWAY$1}:${port}`,
|
|
589
|
+
under: processorsDevRoute(resource.id)
|
|
590
|
+
} } : {}
|
|
568
591
|
});
|
|
569
592
|
continue;
|
|
570
593
|
}
|
|
@@ -574,7 +597,7 @@ function sitesFor(plan, project, addresses = {}) {
|
|
|
574
597
|
if (!port) continue;
|
|
575
598
|
sites.push({
|
|
576
599
|
host,
|
|
577
|
-
upstream: `http://${HOST_GATEWAY}:${port}`
|
|
600
|
+
upstream: `http://${HOST_GATEWAY$1}:${port}`
|
|
578
601
|
});
|
|
579
602
|
}
|
|
580
603
|
return sites;
|
|
@@ -596,7 +619,7 @@ function portOf(address) {
|
|
|
596
619
|
* name, because an empty label makes `uploads..localhost` — a host nothing
|
|
597
620
|
* resolves and no certificate can be issued for.
|
|
598
621
|
*/
|
|
599
|
-
function hostFor(resource, project) {
|
|
622
|
+
function hostFor$1(resource, project) {
|
|
600
623
|
return [
|
|
601
624
|
resource.root && project ? stageOf(resource) : labelOf(resource),
|
|
602
625
|
project,
|
|
@@ -654,14 +677,24 @@ function toCaddyfile(sites) {
|
|
|
654
677
|
if (sites.length === 0) return `${GENERATED}
|
|
655
678
|
# No file server is declared in this stage, so it routes nothing.
|
|
656
679
|
`;
|
|
657
|
-
const blocks = sites.map(({ host, upstream, rewrite }) => `
|
|
680
|
+
const blocks = sites.map(({ host, upstream, rewrite, processors }) => `
|
|
658
681
|
https://${host} {
|
|
659
682
|
tls internal
|
|
660
683
|
|
|
661
684
|
# gkm's own header, which only a service of this stack may send: the auth
|
|
662
685
|
# server rate-limits by the client address it carries.
|
|
663
686
|
request_header -${CLIENT_IP_HEADER}
|
|
664
|
-
|
|
687
|
+
${processors ? `
|
|
688
|
+
# Its processors' patterns, to the dev server holding them — under the
|
|
689
|
+
# route it mounts them on. Answered there, so the bucket below never is.
|
|
690
|
+
@processors path ${processors.prefixes.map((prefix) => `${prefix}*`).join(" ")}
|
|
691
|
+
handle @processors {
|
|
692
|
+
reverse_proxy ${processors.upstream} {
|
|
693
|
+
rewrite ${processors.under}{uri}
|
|
694
|
+
header_up Host {host}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
` : ""}
|
|
665
698
|
reverse_proxy ${upstream} {${rewrite ? `
|
|
666
699
|
rewrite ${rewrite}
|
|
667
700
|
# MinIO routes and signs on the Host header, so forwarding the requested
|
|
@@ -720,6 +753,108 @@ function emulatorQueueUrl(name, port) {
|
|
|
720
753
|
return `${emulatorEndpoint(port)}/${EMULATOR_ACCOUNT}/${name}`;
|
|
721
754
|
}
|
|
722
755
|
//#endregion
|
|
756
|
+
//#region src/reconcile/objectEvents.ts
|
|
757
|
+
/**
|
|
758
|
+
* A bucket's object events, where the bucket is a local MinIO.
|
|
759
|
+
*
|
|
760
|
+
* S3 publishes a bucket's events to a topic the deploy creates; MinIO has no
|
|
761
|
+
* topic to publish to, but it can post each event to a webhook. So where MinIO
|
|
762
|
+
* holds a watched bucket, it posts the bucket's events to the process that
|
|
763
|
+
* runs the bucket's subscribers, on a route only it can use, and that process
|
|
764
|
+
* relays each one onto the events broker — under the bucket's name, as
|
|
765
|
+
* `object.created` or `object.deleted` — where every subscriber drains a queue
|
|
766
|
+
* of its own, as a topic's subscribers do.
|
|
767
|
+
*
|
|
768
|
+
* Declared to MinIO in two halves, both derived here so they cannot drift:
|
|
769
|
+
*
|
|
770
|
+
* - **a webhook target per bucket**, as container environment: where to post,
|
|
771
|
+
* the token to present, and a queue on disk, because MinIO starts before the
|
|
772
|
+
* process it posts to and must keep what it could not deliver;
|
|
773
|
+
* - **the bucket's notification**, written through the S3 API once the bucket
|
|
774
|
+
* exists: every created and removed object, to that target. Unfiltered —
|
|
775
|
+
* two subscribers watching overlapping prefixes are two rules S3 refuses on
|
|
776
|
+
* one bucket, and each subscriber filters what it is handed anyway.
|
|
777
|
+
*/
|
|
778
|
+
/**
|
|
779
|
+
* The token MinIO presents when it posts a bucket's events.
|
|
780
|
+
*
|
|
781
|
+
* Derived, never stored: the container and the process it posts to are handed
|
|
782
|
+
* the same value by the same derivation — with the stage's random seed where a
|
|
783
|
+
* deployed stage's stack has one, so the token is a secret there, and from the
|
|
784
|
+
* project alone locally, where everything it would protect is on loopback.
|
|
785
|
+
* Not salted by the stage: one MinIO serves `gkm dev` and `gkm test`, and a
|
|
786
|
+
* target is declared once per container.
|
|
787
|
+
*/
|
|
788
|
+
function objectsWebhookToken(project, bucket, seed) {
|
|
789
|
+
return createHash("sha256").update(`${seed ? `${seed}:` : ""}${project}:objects-webhook:${bucket}`).digest("base64url").slice(0, 32);
|
|
790
|
+
}
|
|
791
|
+
/**
|
|
792
|
+
* The target's id in MinIO's environment — `MINIO_NOTIFY_WEBHOOK_*_<ID>`.
|
|
793
|
+
*
|
|
794
|
+
* Upper-cased with nothing between words: a canonical id is letters and
|
|
795
|
+
* digits, and an underscore inside the id would read as part of the setting's
|
|
796
|
+
* name.
|
|
797
|
+
*/
|
|
798
|
+
function minioTargetId(bucket) {
|
|
799
|
+
return bucket.toUpperCase();
|
|
800
|
+
}
|
|
801
|
+
/** The target as a notification names it. */
|
|
802
|
+
function minioTargetArn(bucket) {
|
|
803
|
+
return `arn:minio:sqs::${minioTargetId(bucket)}:webhook`;
|
|
804
|
+
}
|
|
805
|
+
/** How a container reaches a process on the host. */
|
|
806
|
+
const HOST_GATEWAY = "host.docker.internal";
|
|
807
|
+
/** Where MinIO keeps what it could not deliver yet, inside its container. */
|
|
808
|
+
const QUEUE_ROOT = "/gkm-object-events";
|
|
809
|
+
/** The id of the notification rule gkm owns on a bucket. */
|
|
810
|
+
const NOTIFICATION_RULE_PREFIX = "gkm-";
|
|
811
|
+
/**
|
|
812
|
+
* MinIO's environment for each watched bucket's webhook target.
|
|
813
|
+
*
|
|
814
|
+
* Sorted, so the same buckets always render the same environment and a
|
|
815
|
+
* reconcile that changed nothing does not recreate the container.
|
|
816
|
+
*/
|
|
817
|
+
function minioWebhookEnv(hooks) {
|
|
818
|
+
const env = {};
|
|
819
|
+
for (const hook of [...hooks].sort((a, b) => a.bucket.localeCompare(b.bucket))) {
|
|
820
|
+
const id = minioTargetId(hook.bucket);
|
|
821
|
+
env[`MINIO_NOTIFY_WEBHOOK_ENABLE_${id}`] = "on";
|
|
822
|
+
env[`MINIO_NOTIFY_WEBHOOK_ENDPOINT_${id}`] = hook.endpoint;
|
|
823
|
+
env[`MINIO_NOTIFY_WEBHOOK_AUTH_TOKEN_${id}`] = hook.token;
|
|
824
|
+
env[`MINIO_NOTIFY_WEBHOOK_QUEUE_DIR_${id}`] = `${QUEUE_ROOT}/${id.toLowerCase()}`;
|
|
825
|
+
}
|
|
826
|
+
return env;
|
|
827
|
+
}
|
|
828
|
+
/** The rule that sends every created and removed object to the bucket's target. */
|
|
829
|
+
function minioNotificationRule(bucket) {
|
|
830
|
+
return {
|
|
831
|
+
Id: `${NOTIFICATION_RULE_PREFIX}${bucket}`,
|
|
832
|
+
QueueArn: minioTargetArn(bucket),
|
|
833
|
+
Events: ["s3:ObjectCreated:*", "s3:ObjectRemoved:*"]
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
/**
|
|
837
|
+
* Each watched bucket, and the workers that run one of its subscribers.
|
|
838
|
+
*
|
|
839
|
+
* A bucket is watched when a subscriber reads its events key. MinIO posts a
|
|
840
|
+
* bucket's events to one place, and the relay fans them out to every
|
|
841
|
+
* subscriber whichever worker received them — so one worker per bucket is
|
|
842
|
+
* enough, and the first, by id, is the one chosen, so the choice is stable.
|
|
843
|
+
*/
|
|
844
|
+
function watchedBuckets(manifest, subscribers) {
|
|
845
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
846
|
+
for (const declaration of Object.values(manifest)) {
|
|
847
|
+
if (declaration.kind !== "objects") continue;
|
|
848
|
+
byKey.set(provideKey(declaration.id, "eventsConnectionString"), declaration.id);
|
|
849
|
+
}
|
|
850
|
+
const watched = /* @__PURE__ */ new Map();
|
|
851
|
+
for (const worker of Object.keys(subscribers).sort()) for (const { connectionKey } of subscribers[worker] ?? []) {
|
|
852
|
+
const bucket = byKey.get(connectionKey);
|
|
853
|
+
if (bucket && !watched.has(bucket)) watched.set(bucket, worker);
|
|
854
|
+
}
|
|
855
|
+
return watched;
|
|
856
|
+
}
|
|
857
|
+
//#endregion
|
|
723
858
|
//#region src/telemetry/edges.ts
|
|
724
859
|
/**
|
|
725
860
|
* Which processes use a `Telemetry` node — read off the manifest's edges.
|
|
@@ -744,13 +879,14 @@ function nodeOf(manifest, id) {
|
|
|
744
879
|
return node?.kind === "telemetry" ? node : void 0;
|
|
745
880
|
}
|
|
746
881
|
/**
|
|
747
|
-
* The `Telemetry` node the process for `id` — a surface
|
|
748
|
-
* construct id — emits through, or undefined
|
|
882
|
+
* The `Telemetry` node the process for `id` — a surface, a worker, or a file
|
|
883
|
+
* server's processor unit, by construct id — emits through, or undefined
|
|
884
|
+
* when it has no edge to one.
|
|
749
885
|
*/
|
|
750
886
|
function telemetryOf(manifest, id) {
|
|
751
887
|
const declaration = manifest[id];
|
|
752
888
|
if (!declaration) return void 0;
|
|
753
|
-
if (declaration.kind === "worker") return nodeOf(manifest, declaration.telemetry);
|
|
889
|
+
if (declaration.kind === "worker" || declaration.kind === "file-server") return nodeOf(manifest, declaration.telemetry);
|
|
754
890
|
if (declaration.kind !== "rest-api") return void 0;
|
|
755
891
|
return nodeOf(manifest, declaration.telemetry);
|
|
756
892
|
}
|
|
@@ -764,7 +900,7 @@ function appTelemetry(manifest, appName) {
|
|
|
764
900
|
}
|
|
765
901
|
/** Whether any process in the manifest emits through a `Telemetry` node. */
|
|
766
902
|
function usesTelemetry(manifest) {
|
|
767
|
-
return Object.entries(manifest).some(([id, d]) => (d.kind === "rest-api" || d.kind === "worker") && telemetryOf(manifest, id) !== void 0);
|
|
903
|
+
return Object.entries(manifest).some(([id, d]) => (d.kind === "rest-api" || d.kind === "worker" || d.kind === "file-server") && telemetryOf(manifest, id) !== void 0);
|
|
768
904
|
}
|
|
769
905
|
/**
|
|
770
906
|
* One process's share of the telemetry keys a stage resolved: all of them,
|
|
@@ -1025,7 +1161,8 @@ function planFor(manifest, stage, order, options = {}) {
|
|
|
1025
1161
|
...declaration.scheme ? { scheme: declaration.scheme } : {}
|
|
1026
1162
|
} : {},
|
|
1027
1163
|
...declaration.kind === "file-server" && declaration.open?.length ? { open: declaration.open } : {},
|
|
1028
|
-
...declaration.kind === "
|
|
1164
|
+
...declaration.kind === "file-server" && declaration.processors?.length ? { processors: [...new Set(declaration.processors.map((p) => p.prefix))].sort() } : {},
|
|
1165
|
+
...declaration.kind === "objects" && bucketExpiry(manifest, id) ? { expire: bucketExpiry(manifest, id) } : {},
|
|
1029
1166
|
...(declaration.kind === "rest-api" || declaration.kind === "site") && declaration.subdomain ? { subdomain: declaration.subdomain } : {},
|
|
1030
1167
|
...fake ? { fake: {
|
|
1031
1168
|
key: fake.key,
|
|
@@ -1215,6 +1352,14 @@ function envFor(plan, options) {
|
|
|
1215
1352
|
}
|
|
1216
1353
|
Object.assign(env, publicEnv(plan, env, options.addresses));
|
|
1217
1354
|
Object.assign(env, brokerEnv(plan, options.ports, credential));
|
|
1355
|
+
const relay = env.EVENT_PUBLISHER_CONNECTION_STRING;
|
|
1356
|
+
if (relay && options.relayedBuckets?.length) for (const resource of plan.resources) {
|
|
1357
|
+
if (resource.kind !== "objects" || resource.container !== "minio") continue;
|
|
1358
|
+
if (!options.relayedBuckets.includes(resource.id)) continue;
|
|
1359
|
+
if (!env[resource.envKey]) continue;
|
|
1360
|
+
env[provideKey(resource.id, "eventsConnectionString")] = relay;
|
|
1361
|
+
env[provideKey(resource.id, "eventsToken")] = objectsWebhookToken(options.project ?? "", resource.id, credential.seed);
|
|
1362
|
+
}
|
|
1218
1363
|
if (plan.resources.some((r) => r.kind === "objects" && env[r.envKey])) Object.assign(env, storageCredentials(options.credentials));
|
|
1219
1364
|
return env;
|
|
1220
1365
|
}
|
|
@@ -1366,7 +1511,7 @@ function urlFor(resource, plan, ports, project, addresses, credential) {
|
|
|
1366
1511
|
if (resource.kind === "rest-api" || resource.kind === "site") {
|
|
1367
1512
|
const address = addresses?.[resource.id];
|
|
1368
1513
|
const edge = plan.containers.includes("caddy") ? ports[primaryPortKey("caddy")] : void 0;
|
|
1369
|
-
return address && edge !== void 0 ? `https://${hostFor(resource, project)}:${edge}` : address;
|
|
1514
|
+
return address && edge !== void 0 ? `https://${hostFor$1(resource, project)}:${edge}` : address;
|
|
1370
1515
|
}
|
|
1371
1516
|
if (!resource.container) return void 0;
|
|
1372
1517
|
const port = ports[primaryPortKey(resource.container)];
|
|
@@ -1393,7 +1538,7 @@ function urlFor(resource, plan, ports, project, addresses, credential) {
|
|
|
1393
1538
|
case "file-server": {
|
|
1394
1539
|
const origin = plan.resources.find((r) => r.id === resource.of);
|
|
1395
1540
|
if (!origin) return void 0;
|
|
1396
|
-
return plan.containers.includes("caddy") ? `https://${hostFor(resource, project)}:${port}` : `http://${LOCAL_HOST}:${port}/${origin.name}`;
|
|
1541
|
+
return plan.containers.includes("caddy") ? `https://${hostFor$1(resource, project)}:${port}` : `http://${LOCAL_HOST}:${port}/${origin.name}`;
|
|
1397
1542
|
}
|
|
1398
1543
|
case "cache":
|
|
1399
1544
|
if (resource.of) {
|
|
@@ -2582,6 +2727,56 @@ async function databasesExist(client, names) {
|
|
|
2582
2727
|
}
|
|
2583
2728
|
}
|
|
2584
2729
|
/**
|
|
2730
|
+
* Point each watched bucket's events at its webhook target, and stop sending
|
|
2731
|
+
* an unwatched one's.
|
|
2732
|
+
*
|
|
2733
|
+
* Merged rather than written whole: only the rule gkm owns on a bucket
|
|
2734
|
+
* (`gkm-<Bucket>`) is added or removed, and every other rule — one somebody
|
|
2735
|
+
* added by hand — is kept. Compared before writing, so a converged reconcile
|
|
2736
|
+
* changes nothing.
|
|
2737
|
+
*
|
|
2738
|
+
* @param buckets - each bucket the plan names, by construct id and name
|
|
2739
|
+
* @param watched - the construct ids whose events are relayed
|
|
2740
|
+
*/
|
|
2741
|
+
async function applyNotifications(client, buckets, watched) {
|
|
2742
|
+
const applied = [];
|
|
2743
|
+
for (const { id, name } of buckets) {
|
|
2744
|
+
const current = await client.notification(name);
|
|
2745
|
+
const queues = current.QueueConfigurations ?? [];
|
|
2746
|
+
const others = queues.filter((rule) => !rule.Id?.startsWith(NOTIFICATION_RULE_PREFIX));
|
|
2747
|
+
const wanted = watched.has(id) ? [...others, minioNotificationRule(id)] : others;
|
|
2748
|
+
if (!watched.has(id) && others.length === queues.length) continue;
|
|
2749
|
+
const describe = `object events from ${name}`;
|
|
2750
|
+
const ours = queues.filter((rule) => rule.Id?.startsWith(NOTIFICATION_RULE_PREFIX));
|
|
2751
|
+
if (watched.has(id) ? ours.length === 1 && sameRule(ours[0], minioNotificationRule(id)) : ours.length === 0) {
|
|
2752
|
+
applied.push({
|
|
2753
|
+
id: `${name}:events`,
|
|
2754
|
+
describe,
|
|
2755
|
+
created: false
|
|
2756
|
+
});
|
|
2757
|
+
continue;
|
|
2758
|
+
}
|
|
2759
|
+
await client.setNotification(name, {
|
|
2760
|
+
...current,
|
|
2761
|
+
QueueConfigurations: wanted
|
|
2762
|
+
});
|
|
2763
|
+
applied.push({
|
|
2764
|
+
id: `${name}:events`,
|
|
2765
|
+
describe,
|
|
2766
|
+
created: true
|
|
2767
|
+
});
|
|
2768
|
+
}
|
|
2769
|
+
return applied;
|
|
2770
|
+
}
|
|
2771
|
+
/**
|
|
2772
|
+
* Whether a rule read back says what gkm wrote: the same target, the same
|
|
2773
|
+
* events in any order, and no filter — the store may hand back an empty one.
|
|
2774
|
+
*/
|
|
2775
|
+
function sameRule(current, wanted) {
|
|
2776
|
+
const filter = current.Filter;
|
|
2777
|
+
return current.Id === wanted.Id && current.QueueArn === wanted.QueueArn && JSON.stringify([...current.Events ?? []].sort()) === JSON.stringify([...wanted.Events ?? []].sort()) && (filter?.Key?.FilterRules?.length ?? 0) === 0;
|
|
2778
|
+
}
|
|
2779
|
+
/**
|
|
2585
2780
|
* Apply each bucket's expiry, beside whatever other rules it has, skipping
|
|
2586
2781
|
* those already correct — the same merge the `s3` provider makes deployed.
|
|
2587
2782
|
*/
|
|
@@ -2703,14 +2898,7 @@ var WorkerNameTaken = class extends GkmError {
|
|
|
2703
2898
|
* @param background Each worker's files, from discovery's `background`.
|
|
2704
2899
|
*/
|
|
2705
2900
|
function workerUnits(workspace, manifest, background) {
|
|
2706
|
-
const hosts =
|
|
2707
|
-
const surface = Object.entries(manifest).find(([id, d]) => d.kind === "rest-api" && appKey(id) === name)?.[1];
|
|
2708
|
-
return {
|
|
2709
|
-
name,
|
|
2710
|
-
root: isAbsolute(app.path) ? app.path : join(workspace.root, app.path),
|
|
2711
|
-
generated: !(surface?.kind === "rest-api" && surface.endpoints.length > 0)
|
|
2712
|
-
};
|
|
2713
|
-
}).sort((a, b) => a.name.localeCompare(b.name));
|
|
2901
|
+
const hosts = hostApps(workspace, manifest);
|
|
2714
2902
|
const units = [];
|
|
2715
2903
|
for (const [id, declaration] of Object.entries(manifest)) {
|
|
2716
2904
|
if (declaration.kind !== "worker") continue;
|
|
@@ -2718,12 +2906,8 @@ function workerUnits(workspace, manifest, background) {
|
|
|
2718
2906
|
if (files.length === 0) continue;
|
|
2719
2907
|
const name = appKey(id);
|
|
2720
2908
|
if (workspace.apps[name]) throw new WorkerNameTaken(id, name);
|
|
2721
|
-
const
|
|
2722
|
-
|
|
2723
|
-
holds: files.filter((file) => isUnder(file, host.root)).length
|
|
2724
|
-
})).sort((a, b) => b.holds - a.holds || Number(b.host.generated) - Number(a.host.generated));
|
|
2725
|
-
const host = ranked[0]?.host;
|
|
2726
|
-
if (!host || ranked[0].holds === 0 && !host.generated) throw new WorkerHasNoApp(id);
|
|
2909
|
+
const host = hostFor(hosts, files);
|
|
2910
|
+
if (!host) throw new WorkerHasNoApp(id);
|
|
2727
2911
|
units.push({
|
|
2728
2912
|
id,
|
|
2729
2913
|
name,
|
|
@@ -2732,6 +2916,70 @@ function workerUnits(workspace, manifest, background) {
|
|
|
2732
2916
|
}
|
|
2733
2917
|
return units.sort((a, b) => a.name.localeCompare(b.name));
|
|
2734
2918
|
}
|
|
2919
|
+
/** The service and image name of a file server's processors: `assets-processors`. */
|
|
2920
|
+
function processorUnitName(fileServerId) {
|
|
2921
|
+
return `${appKey(fileServerId.replace(/Server$/, ""))}-processors`;
|
|
2922
|
+
}
|
|
2923
|
+
/** A file server with processors and no backend app to build them from. */
|
|
2924
|
+
var ProcessorsHaveNoApp = class extends GkmError {
|
|
2925
|
+
fileServer;
|
|
2926
|
+
constructor(fileServer) {
|
|
2927
|
+
super(`The file server '${fileServer}' has processors, and the workspace has no backend app that builds a server to carry their entry. Declare a RestApi whose app holds the processors' code.`);
|
|
2928
|
+
this.fileServer = fileServer;
|
|
2929
|
+
this.name = "ProcessorsHaveNoApp";
|
|
2930
|
+
}
|
|
2931
|
+
};
|
|
2932
|
+
/**
|
|
2933
|
+
* The processor units a workspace deploys — each file server with processors
|
|
2934
|
+
* — and the app each is built from, chosen as a worker's is: the backend app
|
|
2935
|
+
* holding the most of its processors' files.
|
|
2936
|
+
*/
|
|
2937
|
+
function processorUnits(workspace, manifest) {
|
|
2938
|
+
const hosts = hostApps(workspace, manifest);
|
|
2939
|
+
const units = [];
|
|
2940
|
+
for (const declaration of Object.values(manifest)) {
|
|
2941
|
+
if (declaration.kind !== "file-server") continue;
|
|
2942
|
+
const processors = declaration.processors ?? [];
|
|
2943
|
+
if (processors.length === 0) continue;
|
|
2944
|
+
const host = hostFor(hosts, processors.map(({ handler }) => {
|
|
2945
|
+
const file = handler.split("#")[0] ?? handler;
|
|
2946
|
+
return isAbsolute(file) ? file : join(workspace.root, file);
|
|
2947
|
+
}));
|
|
2948
|
+
if (!host) throw new ProcessorsHaveNoApp(declaration.id);
|
|
2949
|
+
const name = processorUnitName(declaration.id);
|
|
2950
|
+
if (workspace.apps[name]) throw new WorkerNameTaken(declaration.id, name);
|
|
2951
|
+
units.push({
|
|
2952
|
+
id: declaration.id,
|
|
2953
|
+
name,
|
|
2954
|
+
app: host.name
|
|
2955
|
+
});
|
|
2956
|
+
}
|
|
2957
|
+
return units.sort((a, b) => a.name.localeCompare(b.name));
|
|
2958
|
+
}
|
|
2959
|
+
/** The backend apps whose builds generate a server, each where it lives. */
|
|
2960
|
+
function hostApps(workspace, manifest) {
|
|
2961
|
+
return Object.entries(workspace.apps).filter(([, app]) => app.type === "backend" && !app.entry).map(([name, app]) => {
|
|
2962
|
+
const surface = Object.entries(manifest).find(([id, d]) => d.kind === "rest-api" && appKey(id) === name)?.[1];
|
|
2963
|
+
return {
|
|
2964
|
+
name,
|
|
2965
|
+
root: isAbsolute(app.path) ? app.path : join(workspace.root, app.path),
|
|
2966
|
+
generated: !(surface?.kind === "rest-api" && surface.endpoints.length > 0)
|
|
2967
|
+
};
|
|
2968
|
+
}).sort((a, b) => a.name.localeCompare(b.name));
|
|
2969
|
+
}
|
|
2970
|
+
/**
|
|
2971
|
+
* The app holding the most of these files; then one that generates a server;
|
|
2972
|
+
* then by name. None when no app holds any and none generates a server.
|
|
2973
|
+
*/
|
|
2974
|
+
function hostFor(hosts, files) {
|
|
2975
|
+
const ranked = hosts.map((host) => ({
|
|
2976
|
+
host,
|
|
2977
|
+
holds: files.filter((file) => isUnder(file, host.root)).length
|
|
2978
|
+
})).sort((a, b) => b.holds - a.holds || Number(b.host.generated) - Number(a.host.generated));
|
|
2979
|
+
const host = ranked[0]?.host;
|
|
2980
|
+
if (!host || ranked[0].holds === 0 && !host.generated) return void 0;
|
|
2981
|
+
return host;
|
|
2982
|
+
}
|
|
2735
2983
|
/** Whether `file` is inside `dir`. */
|
|
2736
2984
|
function isUnder(file, dir) {
|
|
2737
2985
|
const rel = relative(dir, file);
|
|
@@ -3381,6 +3629,8 @@ function generateBackendDockerfile(options) {
|
|
|
3381
3629
|
}
|
|
3382
3630
|
/** The port a worker answers its health check on, inside its container. */
|
|
3383
3631
|
const WORKER_PORT = 3e3;
|
|
3632
|
+
/** The port a processor unit answers on, inside its container. */
|
|
3633
|
+
const PROCESSOR_PORT = 3e3;
|
|
3384
3634
|
/**
|
|
3385
3635
|
* A Worker's image: the same build as its host app's backend — a pruned
|
|
3386
3636
|
* slice, `gkm build --provider server --production` in the image, with the
|
|
@@ -3397,6 +3647,19 @@ function generateWorkerDockerfile(options) {
|
|
|
3397
3647
|
expose: false
|
|
3398
3648
|
});
|
|
3399
3649
|
}
|
|
3650
|
+
/**
|
|
3651
|
+
* A file server's processor unit: its host app's build, and the unit's bundle
|
|
3652
|
+
* as the runner. Its native packages — `sharp` — are installed for the image's
|
|
3653
|
+
* platform beside the bundle, at the versions the build resolved.
|
|
3654
|
+
*/
|
|
3655
|
+
function generateProcessorDockerfile(options) {
|
|
3656
|
+
return gkmBundleDockerfile(options, {
|
|
3657
|
+
title: `Processors Dockerfile (${options.fileServer})`,
|
|
3658
|
+
bundle: options.bundle,
|
|
3659
|
+
as: "processors.mjs",
|
|
3660
|
+
expose: true
|
|
3661
|
+
});
|
|
3662
|
+
}
|
|
3400
3663
|
function gkmBundleDockerfile(options, runner) {
|
|
3401
3664
|
const { port, healthCheckPath = "/health" } = options;
|
|
3402
3665
|
const { app } = layout(options);
|
|
@@ -3423,7 +3686,7 @@ RUN apk add --no-cache tini
|
|
|
3423
3686
|
RUN addgroup --system --gid 1001 nodejs && \\
|
|
3424
3687
|
adduser --system --uid 1001 hono
|
|
3425
3688
|
|
|
3426
|
-
# The bundled ${runner.as
|
|
3689
|
+
# The bundled ${runner.as.replace(/\.mjs$/, "")}
|
|
3427
3690
|
COPY --from=builder --chown=hono:nodejs ${app}/.gkm/server/dist/${runner.bundle} ./${runner.as === runner.bundle ? "" : runner.as}${externals.copy}
|
|
3428
3691
|
|
|
3429
3692
|
ENV NODE_ENV=production
|
|
@@ -3915,7 +4178,7 @@ async function storeFromConfig(workspace, config, options = {}) {
|
|
|
3915
4178
|
const profile = options.profile ? { profile: options.profile } : {};
|
|
3916
4179
|
switch (config.provider) {
|
|
3917
4180
|
case "s3": {
|
|
3918
|
-
const { S3SecretsStore } = await import("./s3-
|
|
4181
|
+
const { S3SecretsStore } = await import("./s3-BZzSnker.mjs");
|
|
3919
4182
|
const { deployIdentity } = await import("./identity-CBXr4wNi.mjs").then((n) => n.r);
|
|
3920
4183
|
const location = s3SecretsLocation(config, workspace.state);
|
|
3921
4184
|
return new S3SecretsStore({
|
|
@@ -4289,6 +4552,14 @@ function composeFor(plan, options) {
|
|
|
4289
4552
|
const volume = volumeOf(container);
|
|
4290
4553
|
if (volume) volumes[volume] = {};
|
|
4291
4554
|
}
|
|
4555
|
+
const minio = services.minio;
|
|
4556
|
+
if (minio && options.objectWebhooks?.length) {
|
|
4557
|
+
minio.environment = {
|
|
4558
|
+
...minio.environment,
|
|
4559
|
+
...minioWebhookEnv(options.objectWebhooks)
|
|
4560
|
+
};
|
|
4561
|
+
if (options.objectWebhooks.some((hook) => hook.endpoint.includes(`//host.docker.internal:`))) minio.extra_hosts = [`${HOST_GATEWAY}:host-gateway`];
|
|
4562
|
+
}
|
|
4292
4563
|
return {
|
|
4293
4564
|
name: options.project,
|
|
4294
4565
|
services: {
|
|
@@ -4662,6 +4933,16 @@ function bucketClient(port, credentials) {
|
|
|
4662
4933
|
Bucket: bucket,
|
|
4663
4934
|
LifecycleConfiguration: { Rules: rules }
|
|
4664
4935
|
}));
|
|
4936
|
+
},
|
|
4937
|
+
async notification(bucket) {
|
|
4938
|
+
const { $metadata: _, ...notification } = await s3.send(new GetBucketNotificationConfigurationCommand({ Bucket: bucket }));
|
|
4939
|
+
return notification;
|
|
4940
|
+
},
|
|
4941
|
+
async setNotification(bucket, notification) {
|
|
4942
|
+
await s3.send(new PutBucketNotificationConfigurationCommand({
|
|
4943
|
+
Bucket: bucket,
|
|
4944
|
+
NotificationConfiguration: notification
|
|
4945
|
+
}));
|
|
4665
4946
|
}
|
|
4666
4947
|
};
|
|
4667
4948
|
}
|
|
@@ -5498,9 +5779,10 @@ async function reconcile(options) {
|
|
|
5498
5779
|
project,
|
|
5499
5780
|
ports,
|
|
5500
5781
|
credentials: options.credentials,
|
|
5501
|
-
...options.apps ? { apps: options.apps(plan.containers) } : {}
|
|
5782
|
+
...options.apps ? { apps: options.apps(plan.containers) } : {},
|
|
5783
|
+
...options.objectWebhooks?.length ? { objectWebhooks: options.objectWebhooks } : {}
|
|
5502
5784
|
});
|
|
5503
|
-
const caddyfile = toCaddyfile(sitesFor(plan, project, options.addresses));
|
|
5785
|
+
const caddyfile = toCaddyfile(sitesFor(plan, project, options.addresses, options.processorAddresses));
|
|
5504
5786
|
const hash = planHash(plan, compose, {
|
|
5505
5787
|
caddyfile,
|
|
5506
5788
|
postgres: postgresStatements(plan, project, seed).map((statement) => statement.create).join(";\n")
|
|
@@ -5516,7 +5798,8 @@ async function reconcile(options) {
|
|
|
5516
5798
|
...options.mailFrom ? { mailFrom: options.mailFrom } : {},
|
|
5517
5799
|
...options.addresses ? { addresses: options.addresses } : {},
|
|
5518
5800
|
...lan ? { lanAddress: lan } : {},
|
|
5519
|
-
...options.metroPorts ? { metroPorts: options.metroPorts } : {}
|
|
5801
|
+
...options.metroPorts ? { metroPorts: options.metroPorts } : {},
|
|
5802
|
+
...options.objectWebhooks?.length ? { relayedBuckets: options.objectWebhooks.map((hook) => hook.bucket) } : {}
|
|
5520
5803
|
});
|
|
5521
5804
|
const env = exports ? resolved : scopeTelemetryEnv(resolved, {
|
|
5522
5805
|
uses: false,
|
|
@@ -5573,7 +5856,7 @@ async function reconcile(options) {
|
|
|
5573
5856
|
};
|
|
5574
5857
|
const credentials = checked.credentials;
|
|
5575
5858
|
if (start && provision) options.progress?.("🗄️ Creating what the constructs declare — databases, roles, buckets, topics");
|
|
5576
|
-
const provisioned = start && provision ? await create(plan, ports, credentials, sql, buckets, carriers, project) : [];
|
|
5859
|
+
const provisioned = start && provision ? await create(plan, ports, credentials, sql, buckets, carriers, project, new Set(stage === (options.localStage ?? stage) ? (options.objectWebhooks ?? []).map((hook) => hook.bucket) : [])) : [];
|
|
5577
5860
|
await saveState(root, {
|
|
5578
5861
|
hash,
|
|
5579
5862
|
stage
|
|
@@ -5593,7 +5876,7 @@ async function reconcile(options) {
|
|
|
5593
5876
|
* Idempotent throughout, so this runs on every non-converged reconcile rather
|
|
5594
5877
|
* than being something to remember.
|
|
5595
5878
|
*/
|
|
5596
|
-
async function create(plan, ports, credentials, sql, buckets, emulatorCarriers, project) {
|
|
5879
|
+
async function create(plan, ports, credentials, sql, buckets, emulatorCarriers, project, watched) {
|
|
5597
5880
|
const applied = [];
|
|
5598
5881
|
const postgresPort = ports[primaryPortKey("postgres")];
|
|
5599
5882
|
const statements = postgresStatements(plan, project, credentials.seed);
|
|
@@ -5607,6 +5890,10 @@ async function create(plan, ports, credentials, sql, buckets, emulatorCarriers,
|
|
|
5607
5890
|
if (names.length > 0) applied.push(...await applyBuckets(client, names));
|
|
5608
5891
|
if (policies.length > 0) applied.push(...await applyPolicies(client, policies));
|
|
5609
5892
|
if (lifecycles.length > 0) applied.push(...await applyLifecycles(client, lifecycles));
|
|
5893
|
+
if (names.length > 0) applied.push(...await applyNotifications(client, plan.resources.filter((r) => r.provisions && r.kind === "objects").map((r) => ({
|
|
5894
|
+
id: r.id,
|
|
5895
|
+
name: r.name
|
|
5896
|
+
})), watched));
|
|
5610
5897
|
}
|
|
5611
5898
|
const emulatorPort = ports[primaryPortKey("localstack")];
|
|
5612
5899
|
const carriers = carrierNames(plan);
|
|
@@ -5672,7 +5959,9 @@ var workspace_exports = /* @__PURE__ */ __exportAll({
|
|
|
5672
5959
|
constructGlobs: () => constructGlobs,
|
|
5673
5960
|
derivedContainers: () => derivedContainers,
|
|
5674
5961
|
fakesApply: () => fakesApply,
|
|
5962
|
+
localObjectWebhooks: () => localObjectWebhooks,
|
|
5675
5963
|
metroPorts: () => metroPorts,
|
|
5964
|
+
processorAddresses: () => processorAddresses,
|
|
5676
5965
|
reconcileWorkspace: () => reconcileWorkspace,
|
|
5677
5966
|
surfaceAddresses: () => surfaceAddresses
|
|
5678
5967
|
});
|
|
@@ -5735,16 +6024,25 @@ function fakesApply(stage, fake, env = process.env) {
|
|
|
5735
6024
|
*/
|
|
5736
6025
|
async function reconcileWorkspace(workspace, options) {
|
|
5737
6026
|
const runnables = {};
|
|
6027
|
+
const background = {};
|
|
6028
|
+
const subscribers = {};
|
|
5738
6029
|
const discovered = await discover({
|
|
5739
6030
|
patterns: constructGlobs(workspace),
|
|
5740
6031
|
cwd: workspace.root,
|
|
5741
|
-
runnables
|
|
6032
|
+
runnables,
|
|
6033
|
+
background,
|
|
6034
|
+
subscribers
|
|
5742
6035
|
});
|
|
5743
6036
|
const manifest = options.manifest ?? discovered;
|
|
5744
6037
|
const faked = fakesApply(options.stage, options.fake);
|
|
5745
6038
|
const fakes = faked ? await readFakes(workspace.root, manifest) : {};
|
|
5746
6039
|
const credentialFakes = faked ? await readCredentialFakes(workspace.root, manifest) : {};
|
|
5747
6040
|
const { credentials } = await loadLocalCredentials(workspace);
|
|
6041
|
+
const objectWebhooks = localObjectWebhooks(workspace, manifest, {
|
|
6042
|
+
background,
|
|
6043
|
+
subscribers,
|
|
6044
|
+
seed: credentials.seed
|
|
6045
|
+
});
|
|
5748
6046
|
const result = await reconcile({
|
|
5749
6047
|
credentials,
|
|
5750
6048
|
root: workspace.root,
|
|
@@ -5755,9 +6053,11 @@ async function reconcileWorkspace(workspace, options) {
|
|
|
5755
6053
|
...backendsOf(workspace),
|
|
5756
6054
|
saved: await loadPortState(workspace.root),
|
|
5757
6055
|
addresses: surfaceAddresses(workspace, manifest),
|
|
6056
|
+
processorAddresses: processorAddresses(workspace, manifest),
|
|
5758
6057
|
metroPorts: metroPorts(workspace, manifest),
|
|
5759
6058
|
fakes,
|
|
5760
6059
|
credentialFakes,
|
|
6060
|
+
...objectWebhooks.length > 0 ? { objectWebhooks } : {},
|
|
5761
6061
|
apps: (containers) => appServices(workspace, manifest, containers, credentials, runnables, fakes, credentialFakes),
|
|
5762
6062
|
...options.start === void 0 ? {} : { start: options.start },
|
|
5763
6063
|
...options.progress ? { progress: options.progress } : {}
|
|
@@ -5772,6 +6072,40 @@ async function reconcileWorkspace(workspace, options) {
|
|
|
5772
6072
|
};
|
|
5773
6073
|
}
|
|
5774
6074
|
/**
|
|
6075
|
+
* Where MinIO posts each watched bucket's events under `gkm dev`: the dev
|
|
6076
|
+
* server of the app that builds the worker running the bucket's subscribers,
|
|
6077
|
+
* on the host, on that app's port — the process that relays them onto the
|
|
6078
|
+
* broker.
|
|
6079
|
+
*
|
|
6080
|
+
* None where the broker is SNS: there a bucket's events come from a topic, and
|
|
6081
|
+
* nothing relays them. None for a worker no app can build — `gkm build` says
|
|
6082
|
+
* so; a dev loop has nothing to post to.
|
|
6083
|
+
*/
|
|
6084
|
+
function localObjectWebhooks(workspace, manifest, options) {
|
|
6085
|
+
if (backendsOf(workspace).events === "sns") return [];
|
|
6086
|
+
const watched = watchedBuckets(manifest, options.subscribers);
|
|
6087
|
+
if (watched.size === 0) return [];
|
|
6088
|
+
let units;
|
|
6089
|
+
try {
|
|
6090
|
+
units = workerUnits(workspace, manifest, options.background);
|
|
6091
|
+
} catch (error) {
|
|
6092
|
+
if (error instanceof WorkerHasNoApp) return [];
|
|
6093
|
+
throw error;
|
|
6094
|
+
}
|
|
6095
|
+
const hooks = [];
|
|
6096
|
+
for (const [bucket, worker] of watched) {
|
|
6097
|
+
const app = units.find((unit) => unit.id === worker)?.app;
|
|
6098
|
+
const port = app ? workspace.apps[app]?.port : void 0;
|
|
6099
|
+
if (!port) continue;
|
|
6100
|
+
hooks.push({
|
|
6101
|
+
bucket,
|
|
6102
|
+
endpoint: `http://${HOST_GATEWAY}:${port}${objectsRoute(bucket)}`,
|
|
6103
|
+
token: objectsWebhookToken(workspace.name, bucket, options.seed)
|
|
6104
|
+
});
|
|
6105
|
+
}
|
|
6106
|
+
return hooks;
|
|
6107
|
+
}
|
|
6108
|
+
/**
|
|
5775
6109
|
* Where each mobile app's Metro runs: its app's port, which `gkm exec` hands
|
|
5776
6110
|
* Expo as `RCT_METRO_PORT`. Kept apart from the surfaces' addresses — a phone
|
|
5777
6111
|
* reaches Metro by `exp://`, not through the edge.
|
|
@@ -5786,6 +6120,19 @@ function metroPorts(workspace, manifest) {
|
|
|
5786
6120
|
return ports;
|
|
5787
6121
|
}
|
|
5788
6122
|
/**
|
|
6123
|
+
* Where each file server's processors answer locally: the dev server of the
|
|
6124
|
+
* app holding them, which `gkm dev` mounts them on — the same app whose build
|
|
6125
|
+
* writes their unit deployed.
|
|
6126
|
+
*/
|
|
6127
|
+
function processorAddresses(workspace, manifest) {
|
|
6128
|
+
const addresses = {};
|
|
6129
|
+
for (const unit of processorUnits(workspace, manifest)) {
|
|
6130
|
+
const port = workspace.apps[unit.app]?.port;
|
|
6131
|
+
if (port) addresses[unit.id] = localAddress(port);
|
|
6132
|
+
}
|
|
6133
|
+
return addresses;
|
|
6134
|
+
}
|
|
6135
|
+
/**
|
|
5789
6136
|
* Where each declared surface and site answers locally.
|
|
5790
6137
|
*
|
|
5791
6138
|
* The addresses come from the ports the workspace already assigns; which
|
|
@@ -5964,8 +6311,31 @@ function workerEnvKeys(manifest, workerId, runnables = {}) {
|
|
|
5964
6311
|
return keys;
|
|
5965
6312
|
}
|
|
5966
6313
|
/** What each construct an edge reaches hands the process that reaches it. */
|
|
6314
|
+
/**
|
|
6315
|
+
* The keys a file server's processor unit reads: its bucket's, and what each
|
|
6316
|
+
* of its processors depends on. `undefined` for a file server with none.
|
|
6317
|
+
*/
|
|
6318
|
+
function processorEnvKeys(manifest, fileServerId, runnables = {}) {
|
|
6319
|
+
const server = manifest[fileServerId];
|
|
6320
|
+
if (server?.kind !== "file-server" || !server.processors?.length) return;
|
|
6321
|
+
const keys = /* @__PURE__ */ new Set();
|
|
6322
|
+
addEdgeKeys(manifest, [
|
|
6323
|
+
server.of,
|
|
6324
|
+
...server.processors.flatMap((p) => p.dependencies.map((d) => d.target)),
|
|
6325
|
+
...runnables[fileServerId] ?? []
|
|
6326
|
+
], keys);
|
|
6327
|
+
return keys;
|
|
6328
|
+
}
|
|
5967
6329
|
function addEdgeKeys(manifest, edges, keys) {
|
|
5968
6330
|
for (const id of edges) {
|
|
6331
|
+
const bucket = eventsEdgeBucket(id);
|
|
6332
|
+
if (bucket) {
|
|
6333
|
+
if (manifest[bucket]?.kind === "objects") {
|
|
6334
|
+
keys.add(provideKey(bucket, "eventsConnectionString"));
|
|
6335
|
+
keys.add(provideKey(bucket, "eventsToken"));
|
|
6336
|
+
}
|
|
6337
|
+
continue;
|
|
6338
|
+
}
|
|
5969
6339
|
const target = manifest[id];
|
|
5970
6340
|
if (!target) continue;
|
|
5971
6341
|
if (target.kind === "rest-api") {
|
|
@@ -6423,6 +6793,308 @@ function validateImageRef(ref) {
|
|
|
6423
6793
|
return ref;
|
|
6424
6794
|
}
|
|
6425
6795
|
//#endregion
|
|
6796
|
+
//#region src/providers/processorOrigin.ts
|
|
6797
|
+
/**
|
|
6798
|
+
* A file server's processors as a CDN origin: the names both sides agree on.
|
|
6799
|
+
*
|
|
6800
|
+
* On a compose stage whose bucket is on S3, the CDN serves the bucket and
|
|
6801
|
+
* sends each processor pattern to the stack's processor unit, at a host of
|
|
6802
|
+
* its own. That host is public — a CDN reaches its origin over the internet —
|
|
6803
|
+
* so the CDN adds a secret header and the edge refuses any request without
|
|
6804
|
+
* it. One definition, read by the CDN that sends the header and by the stack
|
|
6805
|
+
* whose edge checks it, so the two cannot drift.
|
|
6806
|
+
*/
|
|
6807
|
+
/** The header the CDN adds to every request it sends a processor. */
|
|
6808
|
+
const ORIGIN_VERIFY_HEADER = "X-Gkm-Origin-Verify";
|
|
6809
|
+
/** Where a file server's processors answer the CDN: `<fileserver>-processors.<domain>`. */
|
|
6810
|
+
function processorOriginHost(serverId, domain) {
|
|
6811
|
+
return `${appKey(serverId)}-processors.${domain}`.toLowerCase();
|
|
6812
|
+
}
|
|
6813
|
+
/** The stage secret holding the header's value: `ASSETS_SERVER_ORIGIN_SECRET`. */
|
|
6814
|
+
function originSecretKey(serverId) {
|
|
6815
|
+
return provideKey(serverId, "originSecret");
|
|
6816
|
+
}
|
|
6817
|
+
/**
|
|
6818
|
+
* What the processor unit reads that secret from. Set wherever the CDN fronts
|
|
6819
|
+
* it, so the unit refuses a request without the header itself — on Dokploy,
|
|
6820
|
+
* whose edge cannot match a header, the only check; behind the stack's own
|
|
6821
|
+
* edge, a second one.
|
|
6822
|
+
*/
|
|
6823
|
+
const PROCESSOR_ORIGIN_SECRET_ENV = "GKM_PROCESSOR_ORIGIN_SECRET";
|
|
6824
|
+
/** A new value for that secret: random, generated once, kept in the stage. */
|
|
6825
|
+
function newOriginSecret() {
|
|
6826
|
+
return randomBytes(32).toString("base64url");
|
|
6827
|
+
}
|
|
6828
|
+
//#endregion
|
|
6829
|
+
//#region src/generators/ProcessorGenerator.ts
|
|
6830
|
+
/** Where a file server's processor entry lives, under an app's `.gkm/server`. */
|
|
6831
|
+
function processorEntryDir(serverDir, fileServerId) {
|
|
6832
|
+
return join(serverDir, "processors", processorUnitName(fileServerId));
|
|
6833
|
+
}
|
|
6834
|
+
/** The file its bundle is written to, beside the server's. */
|
|
6835
|
+
function processorBundleName(fileServerId) {
|
|
6836
|
+
return `${processorUnitName(fileServerId)}.mjs`;
|
|
6837
|
+
}
|
|
6838
|
+
/**
|
|
6839
|
+
* A file server's processors, as the build generates them.
|
|
6840
|
+
*
|
|
6841
|
+
* On AWS each is a Lambda behind a function URL — the CDN's origin for its
|
|
6842
|
+
* pattern. On a server they are one process per file server, the processor
|
|
6843
|
+
* unit (see {@link ProcessorGenerator.buildEntry}), so the `server` target
|
|
6844
|
+
* generates nothing per processor here.
|
|
6845
|
+
*/
|
|
6846
|
+
var ProcessorGenerator = class extends ConstructGenerator {
|
|
6847
|
+
isConstruct(value) {
|
|
6848
|
+
return Processor.isProcessor(value);
|
|
6849
|
+
}
|
|
6850
|
+
async build(context, constructs, outputDir, options) {
|
|
6851
|
+
if ((options?.target ?? "aws") !== "aws" || constructs.length === 0) return [];
|
|
6852
|
+
const dir = join(outputDir, "processors");
|
|
6853
|
+
await mkdir(dir, { recursive: true });
|
|
6854
|
+
const infos = [];
|
|
6855
|
+
for (const { key, construct, path } of constructs) {
|
|
6856
|
+
const file = join(dir, `${key}.ts`);
|
|
6857
|
+
const importPath = relative(dirname(file), path.relative).replace(/\.ts$/, ".js");
|
|
6858
|
+
await writeFile(file, `import { AWSLambdaProcessor } from '@geekmidas/constructs/processors';
|
|
6859
|
+
import { ${key} } from '${importPath}';
|
|
6860
|
+
${context.storageDrivers?.imports ?? ""}
|
|
6861
|
+
${context.storageDrivers?.setup ? `\n// The handler registers the drivers its target needs.\n${context.storageDrivers.setup}\n` : ""}
|
|
6862
|
+
const adapter = new AWSLambdaProcessor(${key});
|
|
6863
|
+
|
|
6864
|
+
export const handler = adapter.handler;
|
|
6865
|
+
`);
|
|
6866
|
+
infos.push(await processorInfo(key, construct, relative(options?.root ?? process.cwd(), file).replace(/\.ts$/, ".handler"), context));
|
|
6867
|
+
}
|
|
6868
|
+
return infos;
|
|
6869
|
+
}
|
|
6870
|
+
/**
|
|
6871
|
+
* `gkm dev`'s half: `processors.ts` beside the dev server's app, mounting
|
|
6872
|
+
* each file server's processors under `/__gkm/processors/<FileServer>`.
|
|
6873
|
+
* The local edge sends each pattern on the file server's host there, so
|
|
6874
|
+
* `processedUrl()` answers locally as it does deployed.
|
|
6875
|
+
*
|
|
6876
|
+
* In the dev server's own process rather than a unit of its own: locally
|
|
6877
|
+
* there is no CPU to protect, and the dev server already reloads on change.
|
|
6878
|
+
*/
|
|
6879
|
+
async buildDev(outputDir, processors) {
|
|
6880
|
+
await mkdir(outputDir, { recursive: true });
|
|
6881
|
+
const file = join(outputDir, "processors.ts");
|
|
6882
|
+
const imports = processors.map(({ key, path }) => {
|
|
6883
|
+
const specifier = relative(outputDir, path.absolute).replace(/\.ts$/, ".js");
|
|
6884
|
+
return `import { ${key} } from '${specifier.startsWith(".") ? specifier : `./${specifier}`}';`;
|
|
6885
|
+
}).join("\n");
|
|
6886
|
+
const byServer = /* @__PURE__ */ new Map();
|
|
6887
|
+
for (const { key, construct } of processors) byServer.set(construct.surfaceId, [...byServer.get(construct.surfaceId) ?? [], key]);
|
|
6888
|
+
const units = [...byServer.entries()].map(([id, keys]) => ` ${JSON.stringify(id)}: [${keys.join(", ")}],`).join("\n");
|
|
6889
|
+
await writeFile(file, `/**
|
|
6890
|
+
* Generated processors setup for \`gkm dev\`.
|
|
6891
|
+
*
|
|
6892
|
+
* Each file server's processors, under ${PROCESSORS_DEV_PREFIX}<FileServer>: the
|
|
6893
|
+
* local edge rewrites each processor pattern on the file server's host here.
|
|
6894
|
+
*/
|
|
6895
|
+
import type { EnvironmentParser } from '@geekmidas/envkit';
|
|
6896
|
+
import { processorServer } from '@geekmidas/constructs/processors';
|
|
6897
|
+
${imports}
|
|
6898
|
+
|
|
6899
|
+
export function setupProcessors(
|
|
6900
|
+
app: { all: (path: string, handler: (c: any) => unknown) => unknown },
|
|
6901
|
+
envParser: EnvironmentParser<any>,
|
|
6902
|
+
): void {
|
|
6903
|
+
const units: Record<string, ReturnType<typeof processorServer>> = Object.fromEntries(
|
|
6904
|
+
Object.entries({
|
|
6905
|
+
${units}
|
|
6906
|
+
}).map(([id, processors]) => [id, processorServer(processors, envParser)]),
|
|
6907
|
+
);
|
|
6908
|
+
|
|
6909
|
+
app.all('${PROCESSORS_DEV_PREFIX}:server/*', async (c: any) => {
|
|
6910
|
+
const server = c.req.param('server') as string;
|
|
6911
|
+
const serve = units[server];
|
|
6912
|
+
if (!serve) return c.notFound();
|
|
6913
|
+
const path = c.req.path.slice(\`${PROCESSORS_DEV_PREFIX}\${server}\`.length) || '/';
|
|
6914
|
+
const response = await serve({ method: c.req.method, path });
|
|
6915
|
+
if (!response) {
|
|
6916
|
+
return new Response(null, {
|
|
6917
|
+
status: 404,
|
|
6918
|
+
headers: { 'cache-control': 'public, max-age=60' },
|
|
6919
|
+
});
|
|
6920
|
+
}
|
|
6921
|
+
return new Response(c.req.method === 'HEAD' ? null : response.body, {
|
|
6922
|
+
status: response.status,
|
|
6923
|
+
headers: response.headers,
|
|
6924
|
+
});
|
|
6925
|
+
});
|
|
6926
|
+
}
|
|
6927
|
+
`);
|
|
6928
|
+
return file;
|
|
6929
|
+
}
|
|
6930
|
+
/**
|
|
6931
|
+
* A file server's processor unit: one process answering each of its
|
|
6932
|
+
* processors' patterns, and `/health`. Returns its entry, `processors.ts`.
|
|
6933
|
+
*
|
|
6934
|
+
* Its own process because making a derivative is CPU-bound — a resize
|
|
6935
|
+
* spike in the API's process would be every request's latency.
|
|
6936
|
+
*/
|
|
6937
|
+
async buildEntry(input) {
|
|
6938
|
+
const { context, outputDir, processors } = input;
|
|
6939
|
+
await mkdir(outputDir, { recursive: true });
|
|
6940
|
+
await writeTelemetryModule(outputDir, input.telemetry);
|
|
6941
|
+
const healthCheck = context.production?.healthCheck ?? "/health";
|
|
6942
|
+
const imports = processors.map(({ key, path }) => {
|
|
6943
|
+
const specifier = relative(outputDir, path.absolute).replace(/\.ts$/, ".js");
|
|
6944
|
+
return `import { ${key} } from '${specifier.startsWith(".") ? specifier : `./${specifier}`}';`;
|
|
6945
|
+
}).join("\n");
|
|
6946
|
+
const entry = join(outputDir, "processors.ts");
|
|
6947
|
+
await writeFile(entry, `#!/usr/bin/env node
|
|
6948
|
+
/**
|
|
6949
|
+
* Production entry for ${input.fileServerId}'s processors.
|
|
6950
|
+
* Generated by 'gkm build --production'.
|
|
6951
|
+
*
|
|
6952
|
+
* Answers each processor's pattern, and ${healthCheck} on PORT. The edge
|
|
6953
|
+
* routes only those patterns here; everything else on the file server's
|
|
6954
|
+
* domain is the bucket.
|
|
6955
|
+
*/
|
|
6956
|
+
import { timingSafeEqual } from 'node:crypto';
|
|
6957
|
+
import { createServer } from 'node:http';
|
|
6958
|
+
import { runShutdownHooks } from '@geekmidas/constructs';
|
|
6959
|
+
import { processorServer } from '@geekmidas/constructs/processors';
|
|
6960
|
+
${context.storageDrivers?.imports ?? ""}
|
|
6961
|
+
${imports}
|
|
6962
|
+
|
|
6963
|
+
${startTelemetryCode([healthCheck])}
|
|
6964
|
+
${context.storageDrivers?.setup ? `// The entry point registers the drivers its target needs.\n${context.storageDrivers.setup}\n` : ""}
|
|
6965
|
+
process.env.GKM_APP_NAME ??= ${JSON.stringify(processorUnitName(input.fileServerId))};
|
|
6966
|
+
|
|
6967
|
+
const processors = [${processors.map(({ key }) => key).join(", ")}];
|
|
6968
|
+
const serve = processorServer(processors);
|
|
6969
|
+
const port = Number(process.env.PORT) || 3000;
|
|
6970
|
+
let stopping = false;
|
|
6971
|
+
|
|
6972
|
+
${input.telemetry ? REQUEST_SPANS : NO_REQUEST_SPANS}
|
|
6973
|
+
// Where the CDN fronts this unit, the secret it adds to every request: one
|
|
6974
|
+
// without it is refused here, whatever the edge in front did or could not.
|
|
6975
|
+
const originSecret = process.env.${PROCESSOR_ORIGIN_SECRET_ENV}
|
|
6976
|
+
? Buffer.from(process.env.${PROCESSOR_ORIGIN_SECRET_ENV})
|
|
6977
|
+
: undefined;
|
|
6978
|
+
const fromOrigin = (header: string | string[] | undefined) => {
|
|
6979
|
+
if (!originSecret) return true;
|
|
6980
|
+
const given = Buffer.from(typeof header === 'string' ? header : '');
|
|
6981
|
+
return given.length === originSecret.length && timingSafeEqual(given, originSecret);
|
|
6982
|
+
};
|
|
6983
|
+
|
|
6984
|
+
const server = createServer(async (req, res) => {
|
|
6985
|
+
const method = req.method ?? 'GET';
|
|
6986
|
+
const path = (req.url ?? '/').split('?')[0] ?? '/';
|
|
6987
|
+
if (path === ${JSON.stringify(healthCheck)}) {
|
|
6988
|
+
res.writeHead(stopping ? 503 : 200, { 'content-type': 'application/json' });
|
|
6989
|
+
res.end(JSON.stringify({ status: stopping ? 'stopping' : 'ok' }));
|
|
6990
|
+
return;
|
|
6991
|
+
}
|
|
6992
|
+
if (!fromOrigin(req.headers[${JSON.stringify(ORIGIN_VERIFY_HEADER.toLowerCase())}])) {
|
|
6993
|
+
res.writeHead(403, { 'cache-control': 'no-store' }).end();
|
|
6994
|
+
return;
|
|
6995
|
+
}
|
|
6996
|
+
await traced(method, path, async () => {
|
|
6997
|
+
try {
|
|
6998
|
+
const response = await serve({ method, path });
|
|
6999
|
+
if (!response) {
|
|
7000
|
+
res.writeHead(404, { 'cache-control': 'public, max-age=60' }).end();
|
|
7001
|
+
return 404;
|
|
7002
|
+
}
|
|
7003
|
+
res.writeHead(response.status, response.headers);
|
|
7004
|
+
res.end(method === 'HEAD' ? undefined : response.body);
|
|
7005
|
+
return response.status;
|
|
7006
|
+
} catch (error) {
|
|
7007
|
+
console.error(error);
|
|
7008
|
+
res.writeHead(500, { 'cache-control': 'no-store' }).end();
|
|
7009
|
+
return 500;
|
|
7010
|
+
}
|
|
7011
|
+
});
|
|
7012
|
+
});
|
|
7013
|
+
server.listen(port);
|
|
7014
|
+
|
|
7015
|
+
const shutdown = async () => {
|
|
7016
|
+
if (stopping) return;
|
|
7017
|
+
stopping = true;
|
|
7018
|
+
setTimeout(() => process.exit(1), Number(process.env.GKM_SHUTDOWN_TIMEOUT_MS) || 8000).unref();
|
|
7019
|
+
server.close();
|
|
7020
|
+
await runShutdownHooks((error) => console.error(error));
|
|
7021
|
+
process.exit(0);
|
|
7022
|
+
};
|
|
7023
|
+
process.on('SIGTERM', shutdown);
|
|
7024
|
+
process.on('SIGINT', shutdown);
|
|
7025
|
+
`);
|
|
7026
|
+
return entry;
|
|
7027
|
+
}
|
|
7028
|
+
};
|
|
7029
|
+
/**
|
|
7030
|
+
* A SERVER span per request, once telemetry has started — opened by the unit
|
|
7031
|
+
* rather than by hooking node:http, which sees nothing inside a bundle. Named
|
|
7032
|
+
* after the processor's pattern, never the key, which has no bound.
|
|
7033
|
+
*
|
|
7034
|
+
* Only in an entry whose file server has a `Telemetry` node: the bundle
|
|
7035
|
+
* follows the import, and an app without one need not install the API.
|
|
7036
|
+
*/
|
|
7037
|
+
const REQUEST_SPANS = `const tracing = requestSpans ? await import('@opentelemetry/api') : undefined;
|
|
7038
|
+
const routeOf = (path: string) =>
|
|
7039
|
+
processors.find((processor) => path.startsWith(processor.prefix))?.pattern;
|
|
7040
|
+
|
|
7041
|
+
async function traced(
|
|
7042
|
+
method: string,
|
|
7043
|
+
path: string,
|
|
7044
|
+
run: () => Promise<number>,
|
|
7045
|
+
): Promise<void> {
|
|
7046
|
+
if (!tracing) {
|
|
7047
|
+
await run();
|
|
7048
|
+
return;
|
|
7049
|
+
}
|
|
7050
|
+
const route = routeOf(path);
|
|
7051
|
+
await tracing.trace.getTracer('@geekmidas/processors').startActiveSpan(
|
|
7052
|
+
route ? \`\${method} \${route}\` : method,
|
|
7053
|
+
{
|
|
7054
|
+
kind: tracing.SpanKind.SERVER,
|
|
7055
|
+
attributes: {
|
|
7056
|
+
'http.request.method': method,
|
|
7057
|
+
'url.path': path,
|
|
7058
|
+
...(route ? { 'http.route': route } : {}),
|
|
7059
|
+
},
|
|
7060
|
+
},
|
|
7061
|
+
async (span) => {
|
|
7062
|
+
try {
|
|
7063
|
+
const status = await run();
|
|
7064
|
+
span.setAttribute('http.response.status_code', status);
|
|
7065
|
+
if (status >= 500) span.setStatus({ code: tracing.SpanStatusCode.ERROR });
|
|
7066
|
+
} finally {
|
|
7067
|
+
span.end();
|
|
7068
|
+
}
|
|
7069
|
+
},
|
|
7070
|
+
);
|
|
7071
|
+
}
|
|
7072
|
+
`;
|
|
7073
|
+
/** The same shape without telemetry: each request just runs. */
|
|
7074
|
+
const NO_REQUEST_SPANS = `async function traced(
|
|
7075
|
+
_method: string,
|
|
7076
|
+
_path: string,
|
|
7077
|
+
run: () => Promise<number>,
|
|
7078
|
+
): Promise<void> {
|
|
7079
|
+
await run();
|
|
7080
|
+
}
|
|
7081
|
+
`;
|
|
7082
|
+
/** What the manifest records of one processor. */
|
|
7083
|
+
async function processorInfo(name, processor, handler, context) {
|
|
7084
|
+
return {
|
|
7085
|
+
name,
|
|
7086
|
+
handler,
|
|
7087
|
+
fileServer: processor.surfaceId,
|
|
7088
|
+
path: processor.pattern,
|
|
7089
|
+
prefix: processor.prefix,
|
|
7090
|
+
...processor.native.length ? { native: [...processor.native] } : {},
|
|
7091
|
+
...processor.timeout !== void 0 ? { timeout: processor.timeout } : {},
|
|
7092
|
+
...processor.memorySize !== void 0 ? { memorySize: processor.memorySize } : {},
|
|
7093
|
+
environment: await processor.getEnvironment({ markOptional: context.markOptional }),
|
|
7094
|
+
dependencies: processor.constructs
|
|
7095
|
+
};
|
|
7096
|
+
}
|
|
7097
|
+
//#endregion
|
|
6426
7098
|
//#region src/generators/CronGenerator.ts
|
|
6427
7099
|
var CronGenerator = class extends ConstructGenerator {
|
|
6428
7100
|
async build(context, constructs, outputDir, options) {
|
|
@@ -7001,17 +7673,59 @@ async function answers(connection: unknown): Promise<boolean> {
|
|
|
7001
7673
|
}
|
|
7002
7674
|
}
|
|
7003
7675
|
|
|
7676
|
+
/** A request to a route this worker mounted, and what it answered. */
|
|
7677
|
+
export interface WorkerRequest {
|
|
7678
|
+
headers: Record<string, string | string[] | undefined>;
|
|
7679
|
+
body: string;
|
|
7680
|
+
}
|
|
7681
|
+
export interface WorkerAnswer {
|
|
7682
|
+
status: number;
|
|
7683
|
+
body: unknown;
|
|
7684
|
+
}
|
|
7685
|
+
|
|
7686
|
+
// The routes something pushes to: an SNS subscriber's, and the one a local
|
|
7687
|
+
// object store posts a bucket's events to. Mounted by the subscribers, served
|
|
7688
|
+
// by the entry's HTTP server beside the health check.
|
|
7689
|
+
const routes = new Map<string, (c: any) => unknown>();
|
|
7690
|
+
const app = {
|
|
7691
|
+
post: (path: string, handler: (c: any) => unknown) => {
|
|
7692
|
+
routes.set(path, handler);
|
|
7693
|
+
},
|
|
7694
|
+
};
|
|
7695
|
+
|
|
7696
|
+
/** Run one mounted route, as the subscribers' handlers expect to be called. */
|
|
7697
|
+
async function route(path: string, request: WorkerRequest): Promise<WorkerAnswer | undefined> {
|
|
7698
|
+
const handler = routes.get(path);
|
|
7699
|
+
if (!handler) return undefined;
|
|
7700
|
+
let answer: WorkerAnswer | undefined;
|
|
7701
|
+
const c = {
|
|
7702
|
+
req: {
|
|
7703
|
+
text: async () => request.body,
|
|
7704
|
+
header: (name: string) => {
|
|
7705
|
+
const value = request.headers[name.toLowerCase()];
|
|
7706
|
+
return Array.isArray(value) ? value[0] : value;
|
|
7707
|
+
},
|
|
7708
|
+
},
|
|
7709
|
+
json: (body: unknown, status = 200) => {
|
|
7710
|
+
answer = { status, body };
|
|
7711
|
+
return answer;
|
|
7712
|
+
},
|
|
7713
|
+
};
|
|
7714
|
+
await handler(c);
|
|
7715
|
+
return answer ?? { status: 204, body: null };
|
|
7716
|
+
}
|
|
7717
|
+
|
|
7004
7718
|
/** Start everything this worker runs. */
|
|
7005
7719
|
export async function startWorker(): Promise<{
|
|
7006
7720
|
health: () => Promise<WorkerHealth>;
|
|
7007
7721
|
stop: () => Promise<void>;
|
|
7722
|
+
route: typeof route;
|
|
7723
|
+
/** Subscribe the pushed routes, once the server answers on \`port\`. */
|
|
7724
|
+
subscribeForPush: (port: number) => Promise<void>;
|
|
7008
7725
|
}> {
|
|
7009
|
-
|
|
7010
|
-
// subscriber, so there is nothing to mount.
|
|
7011
|
-
const noRoutes = { post: () => undefined };
|
|
7012
|
-
|
|
7013
|
-
await setupSubscribers(noRoutes, envParser, logger).catch((error) => {
|
|
7726
|
+
const subscribeForPush = await setupSubscribers(app, envParser, logger).catch((error) => {
|
|
7014
7727
|
logger.error({ err: error }, 'Failed to start subscribers');
|
|
7728
|
+
return async (_port: number) => {};
|
|
7015
7729
|
});
|
|
7016
7730
|
await setupQueues(envParser, logger).catch((error) => {
|
|
7017
7731
|
logger.error({ err: error }, 'Failed to start queue consumers');
|
|
@@ -7023,6 +7737,8 @@ export async function startWorker(): Promise<{
|
|
|
7023
7737
|
const statuses = () => [subscribersStatus(), queuesStatus(), cronsStatus()];
|
|
7024
7738
|
|
|
7025
7739
|
return {
|
|
7740
|
+
route,
|
|
7741
|
+
subscribeForPush,
|
|
7026
7742
|
async health() {
|
|
7027
7743
|
const all = statuses();
|
|
7028
7744
|
const failed = all.flatMap((s) => s.failed);
|
|
@@ -7053,8 +7769,10 @@ function workerEntry(healthCheck) {
|
|
|
7053
7769
|
* Generated by 'gkm build --production'.
|
|
7054
7770
|
*
|
|
7055
7771
|
* Runs this worker's crons, queue consumers and topic subscribers, and serves
|
|
7056
|
-
*
|
|
7057
|
-
* every broker connection answers, 503 otherwise.
|
|
7772
|
+
* its health check: ${healthCheck}, on PORT — 200 when every consumer started
|
|
7773
|
+
* and every broker connection answers, 503 otherwise. Beside it, on the same
|
|
7774
|
+
* port, the POST routes its subscribers mounted: where a local object store
|
|
7775
|
+
* posts a bucket's events.
|
|
7058
7776
|
*
|
|
7059
7777
|
* On SIGTERM it stops pulling messages and scheduling crons, lets the handlers
|
|
7060
7778
|
* in flight finish, closes its broker connections and database pools, and
|
|
@@ -7073,15 +7791,39 @@ let worker: Awaited<ReturnType<typeof startWorker>> | undefined;
|
|
|
7073
7791
|
let stopping = false;
|
|
7074
7792
|
|
|
7075
7793
|
const server = createServer((req, res) => {
|
|
7076
|
-
const path = (req.url ?? '/').split('?')[0];
|
|
7077
|
-
if (path !== ${JSON.stringify(healthCheck)} || (req.method !== 'GET' && req.method !== 'HEAD')) {
|
|
7078
|
-
res.writeHead(404).end();
|
|
7079
|
-
return;
|
|
7080
|
-
}
|
|
7794
|
+
const path = (req.url ?? '/').split('?')[0] ?? '/';
|
|
7081
7795
|
const answer = (status: number, body: unknown) => {
|
|
7082
7796
|
res.writeHead(status, { 'content-type': 'application/json' });
|
|
7083
7797
|
res.end(JSON.stringify(body));
|
|
7084
7798
|
};
|
|
7799
|
+
|
|
7800
|
+
// A route a subscriber mounted: what SNS pushes to, or what a local object
|
|
7801
|
+
// store posts a bucket's events to. Refused while starting or stopping, so
|
|
7802
|
+
// the sender keeps the message and sends it again.
|
|
7803
|
+
if (req.method === 'POST') {
|
|
7804
|
+
let body = '';
|
|
7805
|
+
req.setEncoding('utf8');
|
|
7806
|
+
req.on('data', (chunk) => {
|
|
7807
|
+
body += chunk;
|
|
7808
|
+
});
|
|
7809
|
+
req.on('end', () => {
|
|
7810
|
+
if (stopping) return answer(503, { status: 'stopping' });
|
|
7811
|
+
if (!worker) return answer(503, { status: 'starting' });
|
|
7812
|
+
worker.route(path, { headers: req.headers, body }).then(
|
|
7813
|
+
(routed) => (routed ? answer(routed.status, routed.body) : answer(404, { status: 'not found' })),
|
|
7814
|
+
(error) => {
|
|
7815
|
+
logger.error({ err: error, path }, 'Route failed');
|
|
7816
|
+
answer(500, { status: 'failed' });
|
|
7817
|
+
},
|
|
7818
|
+
);
|
|
7819
|
+
});
|
|
7820
|
+
return;
|
|
7821
|
+
}
|
|
7822
|
+
|
|
7823
|
+
if (path !== ${JSON.stringify(healthCheck)} || (req.method !== 'GET' && req.method !== 'HEAD')) {
|
|
7824
|
+
res.writeHead(404).end();
|
|
7825
|
+
return;
|
|
7826
|
+
}
|
|
7085
7827
|
if (stopping) return answer(503, { status: 'stopping' });
|
|
7086
7828
|
if (!worker) return answer(503, { status: 'starting' });
|
|
7087
7829
|
worker.health().then(
|
|
@@ -7115,6 +7857,9 @@ process.on('SIGINT', shutdown);
|
|
|
7115
7857
|
|
|
7116
7858
|
worker = await startWorker();
|
|
7117
7859
|
logger.info({ port }, 'Worker started');
|
|
7860
|
+
await worker.subscribeForPush(port).catch((error) => {
|
|
7861
|
+
logger.error({ err: error }, 'Failed to subscribe for push');
|
|
7862
|
+
});
|
|
7118
7863
|
`;
|
|
7119
7864
|
}
|
|
7120
7865
|
//#endregion
|
|
@@ -7260,6 +8005,25 @@ async function workspaceDockerCommand(workspace, manifest, imageNames = {}, back
|
|
|
7260
8005
|
imageName: imageNames[worker.name] ?? worker.name
|
|
7261
8006
|
});
|
|
7262
8007
|
}
|
|
8008
|
+
for (const unit of manifest ? processorUnits(workspace, manifest) : []) {
|
|
8009
|
+
logger$1.log(`\n 📄 Generating Dockerfile for ${unit.name} (processors of ${unit.id}, built from ${unit.app})`);
|
|
8010
|
+
const dockerfile = processorDockerfile(unit, workspace.apps[unit.app], {
|
|
8011
|
+
layout,
|
|
8012
|
+
workspaceRoot: workspace.root,
|
|
8013
|
+
native: processorNative(manifest, unit.id)
|
|
8014
|
+
});
|
|
8015
|
+
const path = workerDockerfileOf(unit.name);
|
|
8016
|
+
const dockerfilePath = join(workspace.root, path);
|
|
8017
|
+
await mkdir(dirname(dockerfilePath), { recursive: true });
|
|
8018
|
+
await writeFile(dockerfilePath, dockerfile);
|
|
8019
|
+
logger$1.log(` Generated: ${path}`);
|
|
8020
|
+
results.push({
|
|
8021
|
+
appName: unit.name,
|
|
8022
|
+
type: "backend",
|
|
8023
|
+
dockerfile: dockerfilePath,
|
|
8024
|
+
imageName: imageNames[unit.name] ?? unit.name
|
|
8025
|
+
});
|
|
8026
|
+
}
|
|
7263
8027
|
const dockerignorePath = await ensureDockerignore(layout.buildRoot);
|
|
7264
8028
|
logger$1.log(`\n Ensured: ${dockerignorePath}`);
|
|
7265
8029
|
const composePath = await writeConstructsCompose(workspace);
|
|
@@ -7319,6 +8083,29 @@ function workerDockerfile(worker, host, options) {
|
|
|
7319
8083
|
...options.cache ? { cache: options.cache } : {}
|
|
7320
8084
|
});
|
|
7321
8085
|
}
|
|
8086
|
+
/** The native packages a file server's processors named, once each. */
|
|
8087
|
+
function processorNative(manifest, fileServerId) {
|
|
8088
|
+
const server = manifest[fileServerId];
|
|
8089
|
+
if (server?.kind !== "file-server") return [];
|
|
8090
|
+
return [...new Set((server.processors ?? []).flatMap((p) => p.native ?? []))].sort();
|
|
8091
|
+
}
|
|
8092
|
+
/**
|
|
8093
|
+
* A file server's processor unit's Dockerfile: its host app's build, and the
|
|
8094
|
+
* unit's bundle as the runner, with its processors' native packages
|
|
8095
|
+
* installed beside it — see `generateProcessorDockerfile`.
|
|
8096
|
+
*/
|
|
8097
|
+
function processorDockerfile(unit, host, options) {
|
|
8098
|
+
return generateProcessorDockerfile({
|
|
8099
|
+
...appImageOptions(options.layout, unit.app, host, options.workspaceRoot),
|
|
8100
|
+
imageName: unit.name,
|
|
8101
|
+
port: PROCESSOR_PORT,
|
|
8102
|
+
fileServer: unit.id,
|
|
8103
|
+
bundle: processorBundleName(unit.id),
|
|
8104
|
+
healthCheckPath: "/health",
|
|
8105
|
+
...options.native.length ? { external: [...options.native] } : {},
|
|
8106
|
+
...options.cache ? { cache: options.cache } : {}
|
|
8107
|
+
});
|
|
8108
|
+
}
|
|
7322
8109
|
/**
|
|
7323
8110
|
* A site's Dockerfile, with an `ARG` for every key its bundler inlines.
|
|
7324
8111
|
*
|
|
@@ -7483,7 +8270,7 @@ async function createDnsProvider(options) {
|
|
|
7483
8270
|
if (isDnsProvider(config.provider)) return config.provider;
|
|
7484
8271
|
const provider = config.provider;
|
|
7485
8272
|
if (provider === "hostinger") {
|
|
7486
|
-
const { HostingerProvider } = await import("./HostingerProvider-
|
|
8273
|
+
const { HostingerProvider } = await import("./HostingerProvider-Cjh_2Ml6.mjs");
|
|
7487
8274
|
return new HostingerProvider(options.hostinger);
|
|
7488
8275
|
}
|
|
7489
8276
|
if (provider === "route53") {
|
|
@@ -7496,7 +8283,7 @@ async function createDnsProvider(options) {
|
|
|
7496
8283
|
});
|
|
7497
8284
|
}
|
|
7498
8285
|
if (provider === "godaddy") {
|
|
7499
|
-
const { GoDaddyProvider } = await import("./GoDaddyProvider-
|
|
8286
|
+
const { GoDaddyProvider } = await import("./GoDaddyProvider-BN1dODm9.mjs");
|
|
7500
8287
|
return new GoDaddyProvider(options.godaddy ?? {});
|
|
7501
8288
|
}
|
|
7502
8289
|
if (provider === "cloudflare") throw new DnsProviderNotImplemented("cloudflare");
|
|
@@ -8345,6 +9132,6 @@ function reportPlannedSeeds(planned, log) {
|
|
|
8345
9132
|
for (const { folder, seeds } of planned) log(`🌱 ${folder}: would run ${seeds.length} (${seeds.join(", ")})`);
|
|
8346
9133
|
}
|
|
8347
9134
|
//#endregion
|
|
8348
|
-
export {
|
|
9135
|
+
export { ORIGIN_VERIFY_HEADER as $, telemetryOf as $n, maskPassword as $t, StageProviderDisabled as A, postgresSuperuser as An, LEGACY_LOCAL_LOGIN as At, processorDockerfile as B, redisService as Bn, createCredentialsPreload as Bt, recordRollback as C, postgresDatabaseNames as Cn, backendsOf as Ct, setPostgresBackupId as D, LOCAL_RABBITMQ_USER as Dn, workspace_exports as Dt, setGeneratedSecret as E, mergeLifecycle as En, reconcileWorkspace as Et, DockerBuildFailed as F, projectKey as Fn, composeFor as Ft, workerBundleName as G, localRolePassword as Gn, loadPortState as Gt, workerDockerfile as H, stackRedis as Hn, findAvailablePort as Ht, DockerPushFailed as I, readKey as In, cacheBackendFor as It, CronGenerator as J, resourceName as Jn, isRemoteStore as Jt, workerEntryDir as K, TELEMETRY_CONTAINER as Kn, loadSecretsForApp as Kt, appDockerfile as L, withGeneratedSecrets as Ln, eventsBackendFor as Lt, provisionCommand as M, FileSecretsStore as Mn, bucketClient as Mt, stageProviderNotes as N, getKeyPath as Nn, pgClient as Nt, DnsRecordsUnreadable as O, loadLocalCredentials as On, LOCAL_CA_PATH as Ot, PROVIDER_KINDS as P, keystoreProject as Pn, CLUSTER_DATABASE as Pt, processorInfo as Q, surfaceClientTraced as Qn, initStageSecrets as Qt, dockerCommand as R, REDIS_SERVICE as Rn, providerOf as Rt, recordRelease as S, bucketPolicies as Sn, FAKE_ENV as St, setBackupState as T, quoteIdentifier as Tn, derivedContainers as Tt, workerDockerfileOf as U, withRedisPassword as Un, isPortAvailable as Ut, processorNative as V, runsRedis as Vn, createEntryWrapper as Vt, WorkerGenerator as W, envFor as Wn, loadEnvFiles as Wt, processorBundleName as X, isTelemetryKey as Xn, storeFromConfig as Xt, ProcessorGenerator as Y, appTelemetry as Yn, secretsStoreFor as Yt, processorEntryDir as Z, scopeTelemetryEnv as Zn, store_exports as Zt, asDokployState as _, applyLifecycles as _n, withLogsPassword as _r, appEnvKeys as _t, dnsResources as a, PROCESSOR_PORT as an, DEFAULT_IMAGES as ar, AllowDevServicesTakesNoValue as at, getBackupState as b, applyPostgres as bn, resolveHost as br, processorEnvKeys as bt, currentActor as c, detectPackageManager as cn, portKeys as cr, ExternalServicesNotConfigured as ct, DeployJournal as d, isImageBuild as dn, primaryPortKey as dr, devServicesUsed as dt, storage_exports as en, usesTelemetry as er, PROCESSOR_ORIGIN_SECRET_ENV as et, extractSubdomain as f, publicBuildValues as fn, LOCAL_LOGS_EMAIL as fr, externalServices as ft, verifyDnsRecords as g, applyBuckets as gn, stackLogs as gr, suppliedOnly as gt, orchestrateDns as h, workerUnits as hn, logsService as hr, stageServiceDeclarations as ht, dnsRecordResource as i, imageLayout as in, hostFor$1 as ir, validateImageRef as it, assertStageProvidersEnabled as j, readLocalCredentials as jn, ensurePostgresLogin as jt, createDnsProvider as k, postgresRoleSlug as kn, forgetState as kt, describeActor as l, findBuildRoot as ln, portsOf as lr, STORAGE_KEY_PAIR as lt, normalizeDnsConfig as m, processorUnits as mn, logsAccess as mr, requiredStageKeys as mt, reportDatabaseRuns as n, withCustomSecret as nn, watchedBuckets as nr, originSecretKey as nt, forgetDnsResource as o, TURBO_VERSION as on, describeServices as or, DEV_SERVICES as ot, findRootDomain as p, isUnder as pn, LOGS_SERVICE as pr, reportDevServices as pt, QueueGenerator as q, planFor as qn, prepareEntryCredentials as qt, reportPlannedSeeds as r, composeBuildPaths as rn, EMULATOR_ACCESS_KEY_ID as rr, processorOriginHost as rt, recordDnsChanges as s, WORKER_PORT as sn, fakeKey as sr, DevServicesNeedServerTarget as st, DeploySeedsFailed as t, toEmbeddableSecrets as tn, minioWebhookEnv as tr, newOriginSecret as tt, isCi as u, imageBuildCredentials as un, postgresImage as ur, assertDevServicesFlag as ut, createComposeState as v, applyNotifications as vn, NoDomainForStage as vr, dockerfileOf as vt, setApplicationId as w, postgresStatements as wn, constructGlobs as wt, getGeneratedSecret as x, bucketLifecycles as xn, workerEnvKeys as xt, createEmptyState as y, applyPolicies as yn, isMainFrontendApp as yr, networkEnv as yt, ensureDockerignore as z, STACK_CACHE as zn, storageBackendFor as zt };
|
|
8349
9136
|
|
|
8350
|
-
//# sourceMappingURL=seeds-
|
|
9137
|
+
//# sourceMappingURL=seeds-CK7w79VC.mjs.map
|