@geekmidas/cli 10.0.0-alpha.91 → 10.0.0-alpha.92
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-CVc6Oi1Y.mjs → EndpointGenerator-Bor2tZCm.mjs} +14 -4
- package/dist/EndpointGenerator-Bor2tZCm.mjs.map +1 -0
- package/dist/{EndpointGenerator-CwLjBCFE.cjs → EndpointGenerator-De03t1hd.cjs} +14 -4
- package/dist/EndpointGenerator-De03t1hd.cjs.map +1 -0
- package/dist/{GoDaddyProvider-DSStJVAv.mjs → GoDaddyProvider-BqYGk9XT.mjs} +11 -10
- package/dist/GoDaddyProvider-BqYGk9XT.mjs.map +1 -0
- package/dist/{GoDaddyProvider-DqZ1qD_F.cjs → GoDaddyProvider-CJwg2r_9.cjs} +11 -10
- package/dist/GoDaddyProvider-CJwg2r_9.cjs.map +1 -0
- package/dist/{HostingerProvider-BI6J7oJr.mjs → HostingerProvider-1mqD6aG8.mjs} +2 -2
- package/dist/{HostingerProvider-BI6J7oJr.mjs.map → HostingerProvider-1mqD6aG8.mjs.map} +1 -1
- package/dist/{HostingerProvider-CyTpG_jy.cjs → HostingerProvider-C6t-PSNe.cjs} +2 -2
- package/dist/{HostingerProvider-CyTpG_jy.cjs.map → HostingerProvider-C6t-PSNe.cjs.map} +1 -1
- package/dist/{LegacyStateStore-DXmNA7hy.cjs → LegacyStateStore-CeEwayMB.cjs} +2 -2
- package/dist/{LegacyStateStore-DXmNA7hy.cjs.map → LegacyStateStore-CeEwayMB.cjs.map} +1 -1
- package/dist/{LegacyStateStore-ZkdmiUFZ.mjs → LegacyStateStore-DdJ1bCRV.mjs} +2 -2
- package/dist/{LegacyStateStore-ZkdmiUFZ.mjs.map → LegacyStateStore-DdJ1bCRV.mjs.map} +1 -1
- package/dist/{LocalStateStore-BBeTlRTH.cjs → LocalStateStore-BMfseje2.cjs} +2 -2
- package/dist/{LocalStateStore-BBeTlRTH.cjs.map → LocalStateStore-BMfseje2.cjs.map} +1 -1
- package/dist/{LocalStateStore-CU-O0SBp.mjs → LocalStateStore-Y8XHdoCR.mjs} +2 -2
- package/dist/{LocalStateStore-CU-O0SBp.mjs.map → LocalStateStore-Y8XHdoCR.mjs.map} +1 -1
- package/dist/{OpenApiTsGenerator-B--HV-sW.cjs → OpenApiTsGenerator-CS_RtWOz.cjs} +2 -2
- package/dist/{OpenApiTsGenerator-B--HV-sW.cjs.map → OpenApiTsGenerator-CS_RtWOz.cjs.map} +1 -1
- package/dist/{OpenApiTsGenerator-CkvsPdgg.mjs → OpenApiTsGenerator-DWAF-7Fu.mjs} +2 -2
- package/dist/{OpenApiTsGenerator-CkvsPdgg.mjs.map → OpenApiTsGenerator-DWAF-7Fu.mjs.map} +1 -1
- package/dist/{S3StateStore-bwxcI_wQ.mjs → S3StateStore-BfoY4MvD.mjs} +2 -2
- package/dist/{S3StateStore-bwxcI_wQ.mjs.map → S3StateStore-BfoY4MvD.mjs.map} +1 -1
- package/dist/{S3StateStore-Br8VMY22.cjs → S3StateStore-BmMt2HhW.cjs} +2 -2
- package/dist/{S3StateStore-Br8VMY22.cjs.map → S3StateStore-BmMt2HhW.cjs.map} +1 -1
- package/dist/{SSMStateStore-CsOS0W1y.mjs → SSMStateStore-Bdyw_0Jj.mjs} +2 -2
- package/dist/{SSMStateStore-CsOS0W1y.mjs.map → SSMStateStore-Bdyw_0Jj.mjs.map} +1 -1
- package/dist/{SSMStateStore-DoZE1Wi1.cjs → SSMStateStore-D9K62wM4.cjs} +2 -2
- package/dist/{SSMStateStore-DoZE1Wi1.cjs.map → SSMStateStore-D9K62wM4.cjs.map} +1 -1
- package/dist/{SubscriberGenerator-BPqQf8fB.mjs → SubscriberGenerator-CyN2VF5X.mjs} +6 -5
- package/dist/SubscriberGenerator-CyN2VF5X.mjs.map +1 -0
- package/dist/{SubscriberGenerator-BVn7cQ-2.cjs → SubscriberGenerator-Dvz7EzqK.cjs} +6 -5
- package/dist/SubscriberGenerator-Dvz7EzqK.cjs.map +1 -0
- package/dist/{add-BSHk5Ngn.mjs → add-2Opn2VVE.mjs} +7 -6
- package/dist/add-2Opn2VVE.mjs.map +1 -0
- package/dist/{add-DmmBDV2e.cjs → add-oN0b18HN.cjs} +7 -6
- package/dist/add-oN0b18HN.cjs.map +1 -0
- package/dist/{aws-BTawF0F-.cjs → aws-BTReGjpY.cjs} +24 -6
- package/dist/aws-BTReGjpY.cjs.map +1 -0
- package/dist/{aws-B-6uEhgP.mjs → aws-Dz2bYxK1.mjs} +24 -6
- package/dist/aws-Dz2bYxK1.mjs.map +1 -0
- package/dist/awsStore-B5We7bMg.cjs +136 -0
- package/dist/awsStore-B5We7bMg.cjs.map +1 -0
- package/dist/awsStore-BWa1KNY-.mjs +119 -0
- package/dist/awsStore-BWa1KNY-.mjs.map +1 -0
- package/dist/{bundler-D6eBfVkl.mjs → bundler-C-AdesaG.mjs} +5 -5
- package/dist/{bundler-D6eBfVkl.mjs.map → bundler-C-AdesaG.mjs.map} +1 -1
- package/dist/{bundler-BQJiB7Ub.cjs → bundler-CXQDrhzN.cjs} +5 -5
- package/dist/{bundler-BQJiB7Ub.cjs.map → bundler-CXQDrhzN.cjs.map} +1 -1
- package/dist/{cli-BE9mqeX8.cjs → cli-BRZFYvdb.cjs} +5 -5
- package/dist/{cli-BE9mqeX8.cjs.map → cli-BRZFYvdb.cjs.map} +1 -1
- package/dist/{cli-DD-Yk-I5.mjs → cli-BjhaVObS.mjs} +5 -5
- package/dist/{cli-DD-Yk-I5.mjs.map → cli-BjhaVObS.mjs.map} +1 -1
- package/dist/{compose-Bhmf0n4o.mjs → compose-CTdVeeVk.mjs} +45 -34
- package/dist/compose-CTdVeeVk.mjs.map +1 -0
- package/dist/{compose-CvpU6ooi.cjs → compose-fnYmPk_c.cjs} +45 -34
- package/dist/compose-fnYmPk_c.cjs.map +1 -0
- package/dist/{config-pST7E0CS.d.cts → config-BJo8Wggp.d.mts} +8 -8
- package/dist/{config-CdEcTrjG.d.mts.map → config-BJo8Wggp.d.mts.map} +1 -1
- package/dist/{config-CdEcTrjG.d.mts → config-C9zx7KsX.d.cts} +8 -8
- package/dist/{config-pST7E0CS.d.cts.map → config-C9zx7KsX.d.cts.map} +1 -1
- package/dist/config.cjs +8 -7
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.cts +3 -3
- package/dist/config.d.mts +3 -3
- package/dist/config.mjs +8 -7
- package/dist/config.mjs.map +1 -1
- package/dist/{credentialSchemas-BJqRJNK4.mjs → credentialSchemas-BJFvfkaz.mjs} +5 -4
- package/dist/credentialSchemas-BJFvfkaz.mjs.map +1 -0
- package/dist/{credentialSchemas-DCR4Wd1W.cjs → credentialSchemas-CAPKFkrG.cjs} +5 -4
- package/dist/credentialSchemas-CAPKFkrG.cjs.map +1 -0
- package/dist/{credentials-CnVqoXMb.mjs → credentials-C4KII3PD.mjs} +3 -2
- package/dist/{credentials-CnVqoXMb.mjs.map → credentials-C4KII3PD.mjs.map} +1 -1
- package/dist/{credentials-BKfIugu2.cjs → credentials-QBu7VgaQ.cjs} +3 -2
- package/dist/credentials-QBu7VgaQ.cjs.map +1 -0
- package/dist/{databases-CzszQV0N.mjs → databases-BeGP5eDh.mjs} +11 -10
- package/dist/databases-BeGP5eDh.mjs.map +1 -0
- package/dist/{databases-BY-pEImp.cjs → databases-DGALFDG3.cjs} +11 -10
- package/dist/databases-DGALFDG3.cjs.map +1 -0
- package/dist/{define-DR8xGYfL.mjs → define-6poNqm5O.mjs} +12 -3
- package/dist/define-6poNqm5O.mjs.map +1 -0
- package/dist/{define-DzRz8FgH.cjs → define-crmMcXFS.cjs} +12 -3
- package/dist/define-crmMcXFS.cjs.map +1 -0
- package/dist/deploy/public.cjs +17 -16
- package/dist/deploy/public.cjs.map +1 -1
- package/dist/deploy/public.d.cts +30 -30
- package/dist/deploy/public.d.cts.map +1 -1
- package/dist/deploy/public.d.mts +30 -30
- package/dist/deploy/public.d.mts.map +1 -1
- package/dist/deploy/public.mjs +17 -16
- package/dist/deploy/public.mjs.map +1 -1
- package/dist/{deploy-CuHasEA4.cjs → deploy-6ToUHJmM.cjs} +14 -13
- package/dist/deploy-6ToUHJmM.cjs.map +1 -0
- package/dist/{deploy-QNtx_eWR.mjs → deploy-yuJHlkY0.mjs} +14 -13
- package/dist/deploy-yuJHlkY0.mjs.map +1 -0
- package/dist/{discover-CU0paUyH.d.cts → discover-CRMhvHG9.d.cts} +2 -2
- package/dist/{discover-CU0paUyH.d.cts.map → discover-CRMhvHG9.d.cts.map} +1 -1
- package/dist/{discover-D1It5r_h.cjs → discover-DPgu-ScS.cjs} +4 -3
- package/dist/discover-DPgu-ScS.cjs.map +1 -0
- package/dist/{discover-DFiJxwfv.d.mts → discover-G76EQ4vY.d.mts} +2 -2
- package/dist/{discover-DFiJxwfv.d.mts.map → discover-G76EQ4vY.d.mts.map} +1 -1
- package/dist/{discover-BPelzeGW.mjs → discover-qjFapetL.mjs} +4 -3
- package/dist/discover-qjFapetL.mjs.map +1 -0
- package/dist/{dokploy-CYcMLh9-.mjs → dokploy-BvbJ_A4Q.mjs} +3 -3
- package/dist/{dokploy-CYcMLh9-.mjs.map → dokploy-BvbJ_A4Q.mjs.map} +1 -1
- package/dist/{dokploy-DIowwyIB.cjs → dokploy-Bvvfj2qe.cjs} +3 -3
- package/dist/{dokploy-DIowwyIB.cjs.map → dokploy-Bvvfj2qe.cjs.map} +1 -1
- package/dist/{drivers-DTZzxeIl.mjs → drivers-CTaWrA_L.mjs} +2 -2
- package/dist/{drivers-DTZzxeIl.mjs.map → drivers-CTaWrA_L.mjs.map} +1 -1
- package/dist/{drivers-LYSdQDmd.cjs → drivers-DDojgzz0.cjs} +2 -2
- package/dist/{drivers-LYSdQDmd.cjs.map → drivers-DDojgzz0.cjs.map} +1 -1
- package/dist/{encryption-BrEef7Jk.mjs → encryption-C9Jm9dfn.mjs} +7 -6
- package/dist/encryption-C9Jm9dfn.mjs.map +1 -0
- package/dist/{encryption-CjYdUb2s.cjs → encryption-CgW167LO.cjs} +7 -6
- package/dist/encryption-CgW167LO.cjs.map +1 -0
- package/dist/{engine-2h4k5ICq.mjs → engine-C8Ye9MUm.mjs} +36 -25
- package/dist/engine-C8Ye9MUm.mjs.map +1 -0
- package/dist/{engine-BhJ7ZP9K.cjs → engine-Dj7ZKO8H.cjs} +36 -25
- package/dist/engine-Dj7ZKO8H.cjs.map +1 -0
- package/dist/errors-D8Gwp5kU.mjs +23 -0
- package/dist/errors-D8Gwp5kU.mjs.map +1 -0
- package/dist/errors-DMC4Umsb.cjs +34 -0
- package/dist/errors-DMC4Umsb.cjs.map +1 -0
- package/dist/{github-BCDF6xVH.mjs → github-B1k42hIt.mjs} +31 -9
- package/dist/github-B1k42hIt.mjs.map +1 -0
- package/dist/{github-B5Bw4HIu.cjs → github-BD051KZZ.cjs} +31 -9
- package/dist/github-BD051KZZ.cjs.map +1 -0
- package/dist/{index-YFNxR5Ns.d.mts → index-B5uuwx-Y.d.cts} +6 -6
- package/dist/{index-D-F_hTDz.d.cts.map → index-B5uuwx-Y.d.cts.map} +1 -1
- package/dist/{index-D-F_hTDz.d.cts → index-D1pHwqkK.d.mts} +6 -6
- package/dist/{index-YFNxR5Ns.d.mts.map → index-D1pHwqkK.d.mts.map} +1 -1
- package/dist/index.cjs +159 -208
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +159 -208
- package/dist/index.mjs.map +1 -1
- package/dist/{local-BjxDfPvy.cjs → local-B61kUh0w.cjs} +3 -3
- package/dist/{local-BjxDfPvy.cjs.map → local-B61kUh0w.cjs.map} +1 -1
- package/dist/{local-IuoIF3CT.mjs → local-Otp74ak0.mjs} +3 -3
- package/dist/{local-IuoIF3CT.mjs.map → local-Otp74ak0.mjs.map} +1 -1
- package/dist/{migrate-DbtcuOr_.mjs → migrate-BSbZ7-9n.mjs} +8 -7
- package/dist/migrate-BSbZ7-9n.mjs.map +1 -0
- package/dist/{migrate-q2YYLJLE.cjs → migrate-D3VVsiHp.cjs} +8 -7
- package/dist/migrate-D3VVsiHp.cjs.map +1 -0
- package/dist/{migrate-DWZz9kah.mjs → migrate-IZsYyZ5F.mjs} +8 -7
- package/dist/migrate-IZsYyZ5F.mjs.map +1 -0
- package/dist/{migrate-CGaancHY.cjs → migrate-f6JDjyCp.cjs} +8 -7
- package/dist/migrate-f6JDjyCp.cjs.map +1 -0
- package/dist/openapi.cjs +3 -3
- package/dist/openapi.d.cts +1 -10
- package/dist/openapi.d.cts.map +1 -1
- package/dist/openapi.d.mts +1 -10
- package/dist/openapi.d.mts.map +1 -1
- package/dist/openapi.mjs +3 -3
- package/dist/{provider-C9_-45d5.cjs → provider-BqpjG9QM.cjs} +4 -3
- package/dist/provider-BqpjG9QM.cjs.map +1 -0
- package/dist/{provider-q4lsYzYG.mjs → provider-CEOKk8NS.mjs} +4 -3
- package/dist/provider-CEOKk8NS.mjs.map +1 -0
- package/dist/{ready-BpNbORRB.cjs → ready-D3PIjx3S.cjs} +9 -8
- package/dist/ready-D3PIjx3S.cjs.map +1 -0
- package/dist/{ready-b6vm0-W-.mjs → ready-naOSJpeJ.mjs} +9 -8
- package/dist/ready-naOSJpeJ.mjs.map +1 -0
- package/dist/reconcile/public.cjs +1 -1
- package/dist/reconcile/public.d.cts +2 -2
- package/dist/reconcile/public.d.mts +2 -2
- package/dist/reconcile/public.mjs +1 -1
- package/dist/{resolve-DNg0icnh.mjs → resolve-Be4bF1w4.mjs} +12 -11
- package/dist/resolve-Be4bF1w4.mjs.map +1 -0
- package/dist/{resolve-Cns0v6dN.cjs → resolve-DUp65SnJ.cjs} +12 -11
- package/dist/resolve-DUp65SnJ.cjs.map +1 -0
- package/dist/{rollback-CFrNLmxs.mjs → rollback-D1d-fW0r.mjs} +11 -10
- package/dist/rollback-D1d-fW0r.mjs.map +1 -0
- package/dist/{rollback-UIjS25F1.cjs → rollback-DDHtd0GJ.cjs} +11 -10
- package/dist/rollback-DDHtd0GJ.cjs.map +1 -0
- package/dist/{runtime-CN_kmqxo.d.cts → runtime-DDrHlYe5.d.cts} +11 -19
- package/dist/runtime-DDrHlYe5.d.cts.map +1 -0
- package/dist/{runtime-Cjvon3oj.d.mts → runtime-Es1V4e9M.d.mts} +11 -19
- package/dist/runtime-Es1V4e9M.d.mts.map +1 -0
- package/dist/sandbox/credentials-worker.cjs +3 -3
- package/dist/sandbox/credentials-worker.mjs +3 -3
- package/dist/sandbox/discover-worker.cjs +1 -1
- package/dist/sandbox/discover-worker.mjs +1 -1
- package/dist/sandbox/migrate-worker.cjs +3 -3
- package/dist/sandbox/migrate-worker.mjs +3 -3
- package/dist/{sandbox-M8UnLhXE.d.cts → sandbox-C3WsTa5m.d.mts} +4 -26
- package/dist/sandbox-C3WsTa5m.d.mts.map +1 -0
- package/dist/{sandbox-CL--ZDui.cjs → sandbox-CHv3OYXg.cjs} +4 -3
- package/dist/sandbox-CHv3OYXg.cjs.map +1 -0
- package/dist/{sandbox-Dm247NGx.mjs → sandbox-CbVB7uOD.mjs} +4 -3
- package/dist/sandbox-CbVB7uOD.mjs.map +1 -0
- package/dist/{sandbox-M8UnLhXE.d.mts → sandbox-MaxSeU92.d.cts} +4 -26
- package/dist/sandbox-MaxSeU92.d.cts.map +1 -0
- package/dist/{secretsManager-Chs9VV2r.mjs → secretsManager-C6wm-9uZ.mjs} +24 -3
- package/dist/secretsManager-C6wm-9uZ.mjs.map +1 -0
- package/dist/{secretsManager-DcMlHnRC.cjs → secretsManager-Cwwgs81g.cjs} +24 -3
- package/dist/secretsManager-Cwwgs81g.cjs.map +1 -0
- package/dist/{seeds-CkIPFGNO.cjs → seeds-BKoJYsE7.cjs} +58 -57
- package/dist/seeds-BKoJYsE7.cjs.map +1 -0
- package/dist/{seeds-odfgSCmu.mjs → seeds-CvGQJnPO.mjs} +58 -57
- package/dist/seeds-CvGQJnPO.mjs.map +1 -0
- package/dist/{sniffer-BTtX3NUM.mjs → sniffer-BTtGAtm3.mjs} +14 -8
- package/dist/sniffer-BTtGAtm3.mjs.map +1 -0
- package/dist/{sniffer-IAQEpsQb.cjs → sniffer-Dq5BS98f.cjs} +14 -8
- package/dist/{sniffer-IAQEpsQb.cjs.map → sniffer-Dq5BS98f.cjs.map} +1 -1
- package/dist/{sst-CJzvyrEz.mjs → sst-BaRXp2Dd.mjs} +18 -8
- package/dist/sst-BaRXp2Dd.mjs.map +1 -0
- package/dist/{sst-BgcOsaBi.cjs → sst-CnKY-CSh.cjs} +18 -8
- package/dist/sst-CnKY-CSh.cjs.map +1 -0
- package/dist/{stages-CUn6gvXL.cjs → stages-BI8JvPzI.cjs} +6 -5
- package/dist/stages-BI8JvPzI.cjs.map +1 -0
- package/dist/{stages-CnZRYmkI.mjs → stages-DY-fQKmo.mjs} +6 -5
- package/dist/stages-DY-fQKmo.mjs.map +1 -0
- package/dist/target/public.cjs +3 -3
- package/dist/target/public.d.cts +2 -2
- package/dist/target/public.d.mts +2 -2
- package/dist/target/public.mjs +3 -3
- package/dist/{terminal-DuapRxh6.cjs → terminal-8XngdSTo.cjs} +6 -5
- package/dist/terminal-8XngdSTo.cjs.map +1 -0
- package/dist/{terminal-D8TPK7o9.mjs → terminal-Dtd6BZX9.mjs} +6 -5
- package/dist/terminal-Dtd6BZX9.mjs.map +1 -0
- package/dist/{types-ZHuZ89hv.d.mts → types-B3Gqs175.d.mts} +19 -23
- package/dist/types-B3Gqs175.d.mts.map +1 -0
- package/dist/{types-NqX1Hz4P.d.cts → types-Bk9st2cd.d.cts} +19 -23
- package/dist/types-Bk9st2cd.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-CJ0lKFq-.mjs → workspace-Bzw7Zs82.mjs} +34 -33
- package/dist/workspace-Bzw7Zs82.mjs.map +1 -0
- package/dist/{workspace-CwW3Gkru.cjs → workspace-CHOB2Xg3.cjs} +34 -33
- package/dist/workspace-CHOB2Xg3.cjs.map +1 -0
- package/package.json +15 -15
- package/dist/EndpointGenerator-CVc6Oi1Y.mjs.map +0 -1
- package/dist/EndpointGenerator-CwLjBCFE.cjs.map +0 -1
- package/dist/GoDaddyProvider-DSStJVAv.mjs.map +0 -1
- package/dist/GoDaddyProvider-DqZ1qD_F.cjs.map +0 -1
- package/dist/SubscriberGenerator-BPqQf8fB.mjs.map +0 -1
- package/dist/SubscriberGenerator-BVn7cQ-2.cjs.map +0 -1
- package/dist/add-BSHk5Ngn.mjs.map +0 -1
- package/dist/add-DmmBDV2e.cjs.map +0 -1
- package/dist/aws-B-6uEhgP.mjs.map +0 -1
- package/dist/aws-BTawF0F-.cjs.map +0 -1
- package/dist/awsStore-7VmS_cZR.cjs +0 -54
- package/dist/awsStore-7VmS_cZR.cjs.map +0 -1
- package/dist/awsStore-B529csle.mjs +0 -43
- package/dist/awsStore-B529csle.mjs.map +0 -1
- package/dist/compose-Bhmf0n4o.mjs.map +0 -1
- package/dist/compose-CvpU6ooi.cjs.map +0 -1
- package/dist/credentialSchemas-BJqRJNK4.mjs.map +0 -1
- package/dist/credentialSchemas-DCR4Wd1W.cjs.map +0 -1
- package/dist/credentials-BKfIugu2.cjs.map +0 -1
- package/dist/databases-BY-pEImp.cjs.map +0 -1
- package/dist/databases-CzszQV0N.mjs.map +0 -1
- package/dist/define-DR8xGYfL.mjs.map +0 -1
- package/dist/define-DzRz8FgH.cjs.map +0 -1
- package/dist/deploy-CuHasEA4.cjs.map +0 -1
- package/dist/deploy-QNtx_eWR.mjs.map +0 -1
- package/dist/discover-BPelzeGW.mjs.map +0 -1
- package/dist/discover-D1It5r_h.cjs.map +0 -1
- package/dist/encryption-BrEef7Jk.mjs.map +0 -1
- package/dist/encryption-CjYdUb2s.cjs.map +0 -1
- package/dist/engine-2h4k5ICq.mjs.map +0 -1
- package/dist/engine-BhJ7ZP9K.cjs.map +0 -1
- package/dist/github-B5Bw4HIu.cjs.map +0 -1
- package/dist/github-BCDF6xVH.mjs.map +0 -1
- package/dist/migrate-CGaancHY.cjs.map +0 -1
- package/dist/migrate-DWZz9kah.mjs.map +0 -1
- package/dist/migrate-DbtcuOr_.mjs.map +0 -1
- package/dist/migrate-q2YYLJLE.cjs.map +0 -1
- package/dist/provider-C9_-45d5.cjs.map +0 -1
- package/dist/provider-q4lsYzYG.mjs.map +0 -1
- package/dist/ready-BpNbORRB.cjs.map +0 -1
- package/dist/ready-b6vm0-W-.mjs.map +0 -1
- package/dist/resolve-Cns0v6dN.cjs.map +0 -1
- package/dist/resolve-DNg0icnh.mjs.map +0 -1
- package/dist/rollback-CFrNLmxs.mjs.map +0 -1
- package/dist/rollback-UIjS25F1.cjs.map +0 -1
- package/dist/runtime-CN_kmqxo.d.cts.map +0 -1
- package/dist/runtime-Cjvon3oj.d.mts.map +0 -1
- package/dist/sandbox-CL--ZDui.cjs.map +0 -1
- package/dist/sandbox-Dm247NGx.mjs.map +0 -1
- package/dist/sandbox-M8UnLhXE.d.cts.map +0 -1
- package/dist/sandbox-M8UnLhXE.d.mts.map +0 -1
- package/dist/secretsManager-Chs9VV2r.mjs.map +0 -1
- package/dist/secretsManager-DcMlHnRC.cjs.map +0 -1
- package/dist/seeds-CkIPFGNO.cjs.map +0 -1
- package/dist/seeds-odfgSCmu.mjs.map +0 -1
- package/dist/sniffer-BTtX3NUM.mjs.map +0 -1
- package/dist/sst-BgcOsaBi.cjs.map +0 -1
- package/dist/sst-CJzvyrEz.mjs.map +0 -1
- package/dist/stages-CUn6gvXL.cjs.map +0 -1
- package/dist/stages-CnZRYmkI.mjs.map +0 -1
- package/dist/terminal-D8TPK7o9.mjs.map +0 -1
- package/dist/terminal-DuapRxh6.cjs.map +0 -1
- package/dist/types-NqX1Hz4P.d.cts.map +0 -1
- package/dist/types-ZHuZ89hv.d.mts.map +0 -1
- package/dist/workspace-CJ0lKFq-.mjs.map +0 -1
- package/dist/workspace-CwW3Gkru.cjs.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"credentialSchemas-BJqRJNK4.mjs","names":["z.toJSONSchema","z.object","z.string","z.discriminatedUnion","z.literal","z.array","z.enum","z.record","z.unknown","z.boolean"],"sources":["../src/secrets/credentialSchemas.ts"],"sourcesContent":["/**\n * A third party's credentials, checked against the schema of the construct\n * that reads them — before they are stored, and before a deploy builds\n * anything with them.\n *\n * The schema lives on the construct (`ExternalApi.credentialsSchema`,\n * `Credential.credentialsSchema`), which is the project's code. So the\n * constructs are loaded where a deploy loads them: in the run's sandbox when\n * there is one, here otherwise. What comes back is data only — each schema as\n * JSON Schema where it can say so, and `{ ok, issues }` for each value asked\n * about — because a live schema cannot cross an isolating sandbox. A value\n * asked about goes in as a secret file, never an argument, and no issue\n * carries it back: an issue is a path and a message.\n */\n\nimport { decodeCredentials } from '@geekmidas/constructs/credential';\nimport { type ConstructManifest, provideKey } from '@geekmidas/manifest';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { z } from 'zod';\nimport {\n\tConstructDiscoveryFailed,\n\ttype ConstructSource,\n\tdiscover,\n} from '../reconcile/discover';\nimport { activeSandbox } from '../sandbox/sandbox';\nimport { runWorker } from '../sandbox/worker';\n\n/** One thing a schema refused: where in the value, and why. */\nexport interface CredentialIssue {\n\t/** Dotted, from the value's root — `apiKey`, `keys.0`; empty for the root. */\n\tpath: string;\n\tmessage: string;\n}\n\n/** What a candidate value came to. */\nexport interface CredentialCheck {\n\tok: boolean;\n\tissues: CredentialIssue[];\n}\n\n/** A construct's credentials key, and its schema as data. */\nexport interface CredentialSchemaInfo {\n\tkey: string;\n\tid: string;\n\tkind: 'external-api' | 'credential';\n\t/**\n\t * The schema as JSON Schema, where the schema can say — zod's, or any\n\t * Standard JSON Schema. Absent otherwise: the value is asked for as JSON.\n\t */\n\tjsonSchema?: Record<string, unknown>;\n}\n\n/** What loading the constructs answers. */\nexport interface CredentialInspection {\n\tschemas: CredentialSchemaInfo[];\n\t/** Each value asked about, by key. */\n\tchecks: Record<string, CredentialCheck>;\n}\n\n/** A construct whose credentials have a schema. */\ninterface LiveCredential {\n\tkey: string;\n\tid: string;\n\tkind: 'external-api' | 'credential';\n\tschema: StandardSchemaV1;\n}\n\n/** The credentials keys a manifest declares, by the construct that reads each. */\nexport function credentialKeys(\n\tmanifest: ConstructManifest,\n): { key: string; id: string; kind: 'external-api' | 'credential' }[] {\n\treturn Object.entries(manifest).flatMap(([id, declaration]) =>\n\t\tdeclaration.kind === 'external-api' || declaration.kind === 'credential'\n\t\t\t? [{ key: provideKey(id, 'credentials'), id, kind: declaration.kind }]\n\t\t\t: [],\n\t);\n}\n\n/** The constructs that read a third party's credentials, with their schemas. */\nexport function liveCredentials(\n\tmanifest: ConstructManifest,\n\tsources: Readonly<Record<string, ConstructSource>>,\n): LiveCredential[] {\n\treturn credentialKeys(manifest).flatMap(({ key, id, kind }) => {\n\t\tconst schema = (sources[id]?.construct as { credentialsSchema?: unknown })\n\t\t\t?.credentialsSchema;\n\t\treturn isStandardSchema(schema) ? [{ key, id, kind, schema }] : [];\n\t});\n}\n\nfunction isStandardSchema(value: unknown): value is StandardSchemaV1 {\n\treturn (\n\t\ttypeof value === 'object' &&\n\t\tvalue !== null &&\n\t\ttypeof (value as StandardSchemaV1)['~standard']?.validate === 'function'\n\t);\n}\n\n/**\n * A schema as JSON Schema: through the Standard JSON Schema extension where\n * the schema has it, as zod 4's does, else zod's own converter — or nothing,\n * for a schema neither can describe.\n */\nexport function jsonSchemaOf(\n\tschema: StandardSchemaV1,\n): Record<string, unknown> | undefined {\n\tconst standard = schema['~standard'] as StandardSchemaV1['~standard'] & {\n\t\tjsonSchema?: {\n\t\t\tinput?: (options: { target: string }) => Record<string, unknown>;\n\t\t};\n\t};\n\ttry {\n\t\tconst described = standard.jsonSchema?.input?.({\n\t\t\ttarget: 'draft-2020-12',\n\t\t});\n\t\tif (described) return described;\n\t} catch {\n\t\t// A type JSON Schema cannot express: try zod's converter, then nothing.\n\t}\n\tif (typeof schema === 'object' && schema !== null && '_zod' in schema) {\n\t\ttry {\n\t\t\treturn z.toJSONSchema(schema as unknown as z.ZodType, {\n\t\t\t\tio: 'input',\n\t\t\t}) as Record<string, unknown>;\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\treturn undefined;\n}\n\n/** A stored value against its schema, read the way the construct reads it. */\nexport async function checkCredential(\n\tschema: StandardSchemaV1,\n\traw: string,\n): Promise<CredentialCheck> {\n\tconst result = await schema['~standard'].validate(decodeCredentials(raw));\n\tif (!result.issues) return { ok: true, issues: [] };\n\treturn {\n\t\tok: false,\n\t\tissues: result.issues.map((issue) => ({\n\t\t\tpath: (issue.path ?? [])\n\t\t\t\t.map((segment) =>\n\t\t\t\t\ttypeof segment === 'object' ? String(segment.key) : String(segment),\n\t\t\t\t)\n\t\t\t\t.join('.'),\n\t\t\tmessage: issue.message,\n\t\t})),\n\t};\n}\n\n/** What a set of live credentials says about some values: data only. */\nexport async function inspectLive(\n\tlive: readonly LiveCredential[],\n\tvalues: Readonly<Record<string, string>>,\n): Promise<CredentialInspection> {\n\tconst checks: Record<string, CredentialCheck> = {};\n\tfor (const credential of live) {\n\t\tconst value = values[credential.key];\n\t\tif (value === undefined) continue;\n\t\tchecks[credential.key] = await checkCredential(credential.schema, value);\n\t}\n\treturn {\n\t\tschemas: live.map(({ key, id, kind, schema }) => {\n\t\t\tconst jsonSchema = jsonSchemaOf(schema);\n\t\t\treturn { key, id, kind, ...(jsonSchema ? { jsonSchema } : {}) };\n\t\t}),\n\t\tchecks,\n\t};\n}\n\nexport interface InspectOptions {\n\t/** The workspace root. */\n\troot: string;\n\t/** Its construct globs. */\n\tpatterns: readonly string[];\n\t/** The values to check, by key. Only credentials keys are looked at. */\n\tvalues?: Readonly<Record<string, string>>;\n}\n\nconst Issue = z.object({ path: z.string(), message: z.string() });\n\n/** What the credentials worker answers. Checked: the worker ran project code. */\nconst CredentialsAnswer = z.discriminatedUnion('reason', [\n\tz.object({\n\t\treason: z.literal('inspected'),\n\t\tschemas: z.array(\n\t\t\tz.object({\n\t\t\t\tkey: z.string(),\n\t\t\t\tid: z.string(),\n\t\t\t\tkind: z.enum(['external-api', 'credential']),\n\t\t\t\tjsonSchema: z.record(z.string(), z.unknown()).optional(),\n\t\t\t}),\n\t\t),\n\t\tchecks: z.record(\n\t\t\tz.string(),\n\t\t\tz.object({ ok: z.boolean(), issues: z.array(Issue) }),\n\t\t),\n\t}),\n\tz.object({\n\t\treason: z.literal('failed'),\n\t\terror: z.object({ name: z.string(), message: z.string() }).loose(),\n\t}),\n]);\n\n/** How long loading the constructs to check credentials may take. */\nconst INSPECT_TIMEOUT_MS = 60_000;\n\n/**\n * The workspace's credential schemas, and what each of `values` comes to —\n * loaded in the run's sandbox when there is one, here otherwise.\n */\nexport async function inspectCredentials(\n\toptions: InspectOptions,\n): Promise<CredentialInspection> {\n\tconst sandbox = activeSandbox();\n\tif (!sandbox) {\n\t\treturn (await loadCredentialSchemasHere(options)).inspect(\n\t\t\toptions.values ?? {},\n\t\t);\n\t}\n\n\tconst secrets = Object.fromEntries(\n\t\tObject.entries(options.values ?? {}).filter(([key]) =>\n\t\t\tkey.endsWith('_CREDENTIALS'),\n\t\t),\n\t);\n\tconst { value } = await runWorker(sandbox, 'credentials check', {\n\t\tname: 'credentials-worker',\n\t\targs: [JSON.stringify({ patterns: options.patterns, cwd: options.root })],\n\t\tcwd: options.root,\n\t\ttimeoutMs: INSPECT_TIMEOUT_MS,\n\t\tschema: CredentialsAnswer,\n\t\t...(Object.keys(secrets).length > 0 ? { secrets } : {}),\n\t});\n\tif (value.reason === 'failed') {\n\t\tthrow new ConstructDiscoveryFailed(value.error.name, value.error.message);\n\t}\n\tconst { schemas, checks } = value;\n\treturn {\n\t\tschemas: schemas.map(({ jsonSchema, ...rest }) => ({\n\t\t\t...rest,\n\t\t\t...(jsonSchema ? { jsonSchema } : {}),\n\t\t})),\n\t\tchecks,\n\t};\n}\n\n/** The constructs loaded once, here, and asked about as often as needed. */\nasync function loadCredentialSchemasHere(options: InspectOptions): Promise<{\n\tinspect(\n\t\tvalues: Readonly<Record<string, string>>,\n\t): Promise<CredentialInspection>;\n}> {\n\tconst sources: Record<string, ConstructSource> = {};\n\tconst manifest = await discover({\n\t\tpatterns: [...options.patterns],\n\t\tcwd: options.root,\n\t\tsources,\n\t});\n\tconst live = liveCredentials(manifest, sources);\n\treturn { inspect: (values) => inspectLive(live, values) };\n}\n\n/** The schemas, and a check of one candidate value at a time. */\nexport interface CredentialSchemas {\n\tschemas: CredentialSchemaInfo[];\n\t/** `raw` against `key`'s schema; ok for a key no schema describes. */\n\tcheck(key: string, raw: string): Promise<CredentialCheck>;\n}\n\n/**\n * The workspace's credential schemas, for a command that checks values one\n * at a time as they are typed. In-process the constructs are loaded once;\n * in a sandbox each check is a run of the worker.\n */\nexport async function loadCredentialSchemas(\n\toptions: Omit<InspectOptions, 'values'>,\n): Promise<CredentialSchemas> {\n\tconst here = activeSandbox()\n\t\t? undefined\n\t\t: await loadCredentialSchemasHere(options);\n\tconst inspect = (values: Readonly<Record<string, string>>) =>\n\t\there ? here.inspect(values) : inspectCredentials({ ...options, values });\n\n\tconst { schemas } = await inspect({});\n\treturn {\n\t\tschemas,\n\t\tasync check(key, raw) {\n\t\t\tif (!schemas.some((s) => s.key === key)) return { ok: true, issues: [] };\n\t\t\tconst { checks } = await inspect({ [key]: raw });\n\t\t\treturn checks[key] ?? { ok: true, issues: [] };\n\t\t},\n\t};\n}\n\n/** One stored credential its schema refuses. */\nexport interface InvalidCredential {\n\tkey: string;\n\tissues: CredentialIssue[];\n}\n\n/** An issue as the line that names it: `SHIPPING_CREDENTIALS.apiKey: …`. */\nexport function issueLine(key: string, issue: CredentialIssue): string {\n\treturn `${key}${issue.path ? `.${issue.path}` : ''}: ${issue.message}`;\n}\n\n/**\n * Credentials their construct's schema refuses — on their way into the\n * stage's secrets, or already there when a deploy reads them. The values are\n * never in the message: only where each is wrong, and why.\n */\nexport class CredentialsInvalid extends Error {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly invalid: readonly InvalidCredential[],\n\t\t/** Whether a value was being set (and was not saved), or deployed. */\n\t\treadonly during: 'set' | 'deploy',\n\t) {\n\t\tconst lines = invalid.flatMap(({ key, issues }) =>\n\t\t\tissues.map((issue) => ` ${issueLine(key, issue)}`),\n\t\t);\n\t\tconst keys = invalid.map((i) => i.key);\n\t\tsuper(\n\t\t\tduring === 'set'\n\t\t\t\t? `${keys.join(', ')} is not what its construct's schema describes, ` +\n\t\t\t\t\t\t`so nothing was saved:\\n\\n${lines.join('\\n')}\\n\\n` +\n\t\t\t\t\t\t`Set a value that matches, or let gkm ask for each field: ` +\n\t\t\t\t\t\t`gkm secrets:add --stage ${stage}`\n\t\t\t\t: `The stage '${stage}' holds ${keys.length === 1 ? 'a credential' : 'credentials'} ` +\n\t\t\t\t\t\t`${keys.length === 1 ? \"its construct's schema refuses\" : \"their constructs' schemas refuse\"}, ` +\n\t\t\t\t\t\t`and every app reading ${keys.length === 1 ? 'it' : 'them'} would fail ` +\n\t\t\t\t\t\t`as it starts:\\n\\n${lines.join('\\n')}\\n\\n` +\n\t\t\t\t\t\t`Set ${keys.length === 1 ? 'it' : 'them'} again, field by field: ` +\n\t\t\t\t\t\t`gkm secrets:add --stage ${stage}`,\n\t\t);\n\t\tthis.name = 'CredentialsInvalid';\n\t}\n}\n\n/**\n * Every credential the stage holds for a construct, against its schema —\n * before a deploy builds anything with one the app would refuse as it\n * starts. Loads nothing when the stage holds none.\n *\n * @throws {CredentialsInvalid} naming each key and every issue\n */\nexport async function assertStageCredentials(options: {\n\troot: string;\n\tpatterns: readonly string[];\n\tmanifest: ConstructManifest;\n\tstage: string;\n\tsupplied: Readonly<Record<string, string>>;\n}): Promise<void> {\n\tconst values = Object.fromEntries(\n\t\tcredentialKeys(options.manifest)\n\t\t\t.map(({ key }) => [key, options.supplied[key]] as const)\n\t\t\t.filter((entry): entry is [string, string] => entry[1] !== undefined),\n\t);\n\tif (Object.keys(values).length === 0) return;\n\n\tconst { checks } = await inspectCredentials({\n\t\troot: options.root,\n\t\tpatterns: options.patterns,\n\t\tvalues,\n\t});\n\tconst invalid = Object.entries(checks)\n\t\t.filter(([, check]) => !check.ok)\n\t\t.map(([key, check]) => ({ key, issues: check.issues }))\n\t\t.sort((a, b) => a.key.localeCompare(b.key));\n\tif (invalid.length > 0) {\n\t\tthrow new CredentialsInvalid(options.stage, invalid, 'deploy');\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"credentialSchemas-DCR4Wd1W.cjs","names":["provideKey","decodeCredentials","activeSandbox","runWorker","ConstructDiscoveryFailed","discover"],"sources":["../src/secrets/credentialSchemas.ts"],"sourcesContent":["/**\n * A third party's credentials, checked against the schema of the construct\n * that reads them — before they are stored, and before a deploy builds\n * anything with them.\n *\n * The schema lives on the construct (`ExternalApi.credentialsSchema`,\n * `Credential.credentialsSchema`), which is the project's code. So the\n * constructs are loaded where a deploy loads them: in the run's sandbox when\n * there is one, here otherwise. What comes back is data only — each schema as\n * JSON Schema where it can say so, and `{ ok, issues }` for each value asked\n * about — because a live schema cannot cross an isolating sandbox. A value\n * asked about goes in as a secret file, never an argument, and no issue\n * carries it back: an issue is a path and a message.\n */\n\nimport { decodeCredentials } from '@geekmidas/constructs/credential';\nimport { type ConstructManifest, provideKey } from '@geekmidas/manifest';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { z } from 'zod';\nimport {\n\tConstructDiscoveryFailed,\n\ttype ConstructSource,\n\tdiscover,\n} from '../reconcile/discover';\nimport { activeSandbox } from '../sandbox/sandbox';\nimport { runWorker } from '../sandbox/worker';\n\n/** One thing a schema refused: where in the value, and why. */\nexport interface CredentialIssue {\n\t/** Dotted, from the value's root — `apiKey`, `keys.0`; empty for the root. */\n\tpath: string;\n\tmessage: string;\n}\n\n/** What a candidate value came to. */\nexport interface CredentialCheck {\n\tok: boolean;\n\tissues: CredentialIssue[];\n}\n\n/** A construct's credentials key, and its schema as data. */\nexport interface CredentialSchemaInfo {\n\tkey: string;\n\tid: string;\n\tkind: 'external-api' | 'credential';\n\t/**\n\t * The schema as JSON Schema, where the schema can say — zod's, or any\n\t * Standard JSON Schema. Absent otherwise: the value is asked for as JSON.\n\t */\n\tjsonSchema?: Record<string, unknown>;\n}\n\n/** What loading the constructs answers. */\nexport interface CredentialInspection {\n\tschemas: CredentialSchemaInfo[];\n\t/** Each value asked about, by key. */\n\tchecks: Record<string, CredentialCheck>;\n}\n\n/** A construct whose credentials have a schema. */\ninterface LiveCredential {\n\tkey: string;\n\tid: string;\n\tkind: 'external-api' | 'credential';\n\tschema: StandardSchemaV1;\n}\n\n/** The credentials keys a manifest declares, by the construct that reads each. */\nexport function credentialKeys(\n\tmanifest: ConstructManifest,\n): { key: string; id: string; kind: 'external-api' | 'credential' }[] {\n\treturn Object.entries(manifest).flatMap(([id, declaration]) =>\n\t\tdeclaration.kind === 'external-api' || declaration.kind === 'credential'\n\t\t\t? [{ key: provideKey(id, 'credentials'), id, kind: declaration.kind }]\n\t\t\t: [],\n\t);\n}\n\n/** The constructs that read a third party's credentials, with their schemas. */\nexport function liveCredentials(\n\tmanifest: ConstructManifest,\n\tsources: Readonly<Record<string, ConstructSource>>,\n): LiveCredential[] {\n\treturn credentialKeys(manifest).flatMap(({ key, id, kind }) => {\n\t\tconst schema = (sources[id]?.construct as { credentialsSchema?: unknown })\n\t\t\t?.credentialsSchema;\n\t\treturn isStandardSchema(schema) ? [{ key, id, kind, schema }] : [];\n\t});\n}\n\nfunction isStandardSchema(value: unknown): value is StandardSchemaV1 {\n\treturn (\n\t\ttypeof value === 'object' &&\n\t\tvalue !== null &&\n\t\ttypeof (value as StandardSchemaV1)['~standard']?.validate === 'function'\n\t);\n}\n\n/**\n * A schema as JSON Schema: through the Standard JSON Schema extension where\n * the schema has it, as zod 4's does, else zod's own converter — or nothing,\n * for a schema neither can describe.\n */\nexport function jsonSchemaOf(\n\tschema: StandardSchemaV1,\n): Record<string, unknown> | undefined {\n\tconst standard = schema['~standard'] as StandardSchemaV1['~standard'] & {\n\t\tjsonSchema?: {\n\t\t\tinput?: (options: { target: string }) => Record<string, unknown>;\n\t\t};\n\t};\n\ttry {\n\t\tconst described = standard.jsonSchema?.input?.({\n\t\t\ttarget: 'draft-2020-12',\n\t\t});\n\t\tif (described) return described;\n\t} catch {\n\t\t// A type JSON Schema cannot express: try zod's converter, then nothing.\n\t}\n\tif (typeof schema === 'object' && schema !== null && '_zod' in schema) {\n\t\ttry {\n\t\t\treturn z.toJSONSchema(schema as unknown as z.ZodType, {\n\t\t\t\tio: 'input',\n\t\t\t}) as Record<string, unknown>;\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\treturn undefined;\n}\n\n/** A stored value against its schema, read the way the construct reads it. */\nexport async function checkCredential(\n\tschema: StandardSchemaV1,\n\traw: string,\n): Promise<CredentialCheck> {\n\tconst result = await schema['~standard'].validate(decodeCredentials(raw));\n\tif (!result.issues) return { ok: true, issues: [] };\n\treturn {\n\t\tok: false,\n\t\tissues: result.issues.map((issue) => ({\n\t\t\tpath: (issue.path ?? [])\n\t\t\t\t.map((segment) =>\n\t\t\t\t\ttypeof segment === 'object' ? String(segment.key) : String(segment),\n\t\t\t\t)\n\t\t\t\t.join('.'),\n\t\t\tmessage: issue.message,\n\t\t})),\n\t};\n}\n\n/** What a set of live credentials says about some values: data only. */\nexport async function inspectLive(\n\tlive: readonly LiveCredential[],\n\tvalues: Readonly<Record<string, string>>,\n): Promise<CredentialInspection> {\n\tconst checks: Record<string, CredentialCheck> = {};\n\tfor (const credential of live) {\n\t\tconst value = values[credential.key];\n\t\tif (value === undefined) continue;\n\t\tchecks[credential.key] = await checkCredential(credential.schema, value);\n\t}\n\treturn {\n\t\tschemas: live.map(({ key, id, kind, schema }) => {\n\t\t\tconst jsonSchema = jsonSchemaOf(schema);\n\t\t\treturn { key, id, kind, ...(jsonSchema ? { jsonSchema } : {}) };\n\t\t}),\n\t\tchecks,\n\t};\n}\n\nexport interface InspectOptions {\n\t/** The workspace root. */\n\troot: string;\n\t/** Its construct globs. */\n\tpatterns: readonly string[];\n\t/** The values to check, by key. Only credentials keys are looked at. */\n\tvalues?: Readonly<Record<string, string>>;\n}\n\nconst Issue = z.object({ path: z.string(), message: z.string() });\n\n/** What the credentials worker answers. Checked: the worker ran project code. */\nconst CredentialsAnswer = z.discriminatedUnion('reason', [\n\tz.object({\n\t\treason: z.literal('inspected'),\n\t\tschemas: z.array(\n\t\t\tz.object({\n\t\t\t\tkey: z.string(),\n\t\t\t\tid: z.string(),\n\t\t\t\tkind: z.enum(['external-api', 'credential']),\n\t\t\t\tjsonSchema: z.record(z.string(), z.unknown()).optional(),\n\t\t\t}),\n\t\t),\n\t\tchecks: z.record(\n\t\t\tz.string(),\n\t\t\tz.object({ ok: z.boolean(), issues: z.array(Issue) }),\n\t\t),\n\t}),\n\tz.object({\n\t\treason: z.literal('failed'),\n\t\terror: z.object({ name: z.string(), message: z.string() }).loose(),\n\t}),\n]);\n\n/** How long loading the constructs to check credentials may take. */\nconst INSPECT_TIMEOUT_MS = 60_000;\n\n/**\n * The workspace's credential schemas, and what each of `values` comes to —\n * loaded in the run's sandbox when there is one, here otherwise.\n */\nexport async function inspectCredentials(\n\toptions: InspectOptions,\n): Promise<CredentialInspection> {\n\tconst sandbox = activeSandbox();\n\tif (!sandbox) {\n\t\treturn (await loadCredentialSchemasHere(options)).inspect(\n\t\t\toptions.values ?? {},\n\t\t);\n\t}\n\n\tconst secrets = Object.fromEntries(\n\t\tObject.entries(options.values ?? {}).filter(([key]) =>\n\t\t\tkey.endsWith('_CREDENTIALS'),\n\t\t),\n\t);\n\tconst { value } = await runWorker(sandbox, 'credentials check', {\n\t\tname: 'credentials-worker',\n\t\targs: [JSON.stringify({ patterns: options.patterns, cwd: options.root })],\n\t\tcwd: options.root,\n\t\ttimeoutMs: INSPECT_TIMEOUT_MS,\n\t\tschema: CredentialsAnswer,\n\t\t...(Object.keys(secrets).length > 0 ? { secrets } : {}),\n\t});\n\tif (value.reason === 'failed') {\n\t\tthrow new ConstructDiscoveryFailed(value.error.name, value.error.message);\n\t}\n\tconst { schemas, checks } = value;\n\treturn {\n\t\tschemas: schemas.map(({ jsonSchema, ...rest }) => ({\n\t\t\t...rest,\n\t\t\t...(jsonSchema ? { jsonSchema } : {}),\n\t\t})),\n\t\tchecks,\n\t};\n}\n\n/** The constructs loaded once, here, and asked about as often as needed. */\nasync function loadCredentialSchemasHere(options: InspectOptions): Promise<{\n\tinspect(\n\t\tvalues: Readonly<Record<string, string>>,\n\t): Promise<CredentialInspection>;\n}> {\n\tconst sources: Record<string, ConstructSource> = {};\n\tconst manifest = await discover({\n\t\tpatterns: [...options.patterns],\n\t\tcwd: options.root,\n\t\tsources,\n\t});\n\tconst live = liveCredentials(manifest, sources);\n\treturn { inspect: (values) => inspectLive(live, values) };\n}\n\n/** The schemas, and a check of one candidate value at a time. */\nexport interface CredentialSchemas {\n\tschemas: CredentialSchemaInfo[];\n\t/** `raw` against `key`'s schema; ok for a key no schema describes. */\n\tcheck(key: string, raw: string): Promise<CredentialCheck>;\n}\n\n/**\n * The workspace's credential schemas, for a command that checks values one\n * at a time as they are typed. In-process the constructs are loaded once;\n * in a sandbox each check is a run of the worker.\n */\nexport async function loadCredentialSchemas(\n\toptions: Omit<InspectOptions, 'values'>,\n): Promise<CredentialSchemas> {\n\tconst here = activeSandbox()\n\t\t? undefined\n\t\t: await loadCredentialSchemasHere(options);\n\tconst inspect = (values: Readonly<Record<string, string>>) =>\n\t\there ? here.inspect(values) : inspectCredentials({ ...options, values });\n\n\tconst { schemas } = await inspect({});\n\treturn {\n\t\tschemas,\n\t\tasync check(key, raw) {\n\t\t\tif (!schemas.some((s) => s.key === key)) return { ok: true, issues: [] };\n\t\t\tconst { checks } = await inspect({ [key]: raw });\n\t\t\treturn checks[key] ?? { ok: true, issues: [] };\n\t\t},\n\t};\n}\n\n/** One stored credential its schema refuses. */\nexport interface InvalidCredential {\n\tkey: string;\n\tissues: CredentialIssue[];\n}\n\n/** An issue as the line that names it: `SHIPPING_CREDENTIALS.apiKey: …`. */\nexport function issueLine(key: string, issue: CredentialIssue): string {\n\treturn `${key}${issue.path ? `.${issue.path}` : ''}: ${issue.message}`;\n}\n\n/**\n * Credentials their construct's schema refuses — on their way into the\n * stage's secrets, or already there when a deploy reads them. The values are\n * never in the message: only where each is wrong, and why.\n */\nexport class CredentialsInvalid extends Error {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly invalid: readonly InvalidCredential[],\n\t\t/** Whether a value was being set (and was not saved), or deployed. */\n\t\treadonly during: 'set' | 'deploy',\n\t) {\n\t\tconst lines = invalid.flatMap(({ key, issues }) =>\n\t\t\tissues.map((issue) => ` ${issueLine(key, issue)}`),\n\t\t);\n\t\tconst keys = invalid.map((i) => i.key);\n\t\tsuper(\n\t\t\tduring === 'set'\n\t\t\t\t? `${keys.join(', ')} is not what its construct's schema describes, ` +\n\t\t\t\t\t\t`so nothing was saved:\\n\\n${lines.join('\\n')}\\n\\n` +\n\t\t\t\t\t\t`Set a value that matches, or let gkm ask for each field: ` +\n\t\t\t\t\t\t`gkm secrets:add --stage ${stage}`\n\t\t\t\t: `The stage '${stage}' holds ${keys.length === 1 ? 'a credential' : 'credentials'} ` +\n\t\t\t\t\t\t`${keys.length === 1 ? \"its construct's schema refuses\" : \"their constructs' schemas refuse\"}, ` +\n\t\t\t\t\t\t`and every app reading ${keys.length === 1 ? 'it' : 'them'} would fail ` +\n\t\t\t\t\t\t`as it starts:\\n\\n${lines.join('\\n')}\\n\\n` +\n\t\t\t\t\t\t`Set ${keys.length === 1 ? 'it' : 'them'} again, field by field: ` +\n\t\t\t\t\t\t`gkm secrets:add --stage ${stage}`,\n\t\t);\n\t\tthis.name = 'CredentialsInvalid';\n\t}\n}\n\n/**\n * Every credential the stage holds for a construct, against its schema —\n * before a deploy builds anything with one the app would refuse as it\n * starts. Loads nothing when the stage holds none.\n *\n * @throws {CredentialsInvalid} naming each key and every issue\n */\nexport async function assertStageCredentials(options: {\n\troot: string;\n\tpatterns: readonly string[];\n\tmanifest: ConstructManifest;\n\tstage: string;\n\tsupplied: Readonly<Record<string, string>>;\n}): Promise<void> {\n\tconst values = Object.fromEntries(\n\t\tcredentialKeys(options.manifest)\n\t\t\t.map(({ key }) => [key, options.supplied[key]] as const)\n\t\t\t.filter((entry): entry is [string, string] => entry[1] !== undefined),\n\t);\n\tif (Object.keys(values).length === 0) return;\n\n\tconst { checks } = await inspectCredentials({\n\t\troot: options.root,\n\t\tpatterns: options.patterns,\n\t\tvalues,\n\t});\n\tconst invalid = Object.entries(checks)\n\t\t.filter(([, check]) => !check.ok)\n\t\t.map(([key, check]) => ({ key, issues: check.issues }))\n\t\t.sort((a, b) => a.key.localeCompare(b.key));\n\tif (invalid.length > 0) {\n\t\tthrow new CredentialsInvalid(options.stage, invalid, 'deploy');\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"credentials-BKfIugu2.cjs","names":["resolve","join","homedir","join","existsSync","readFile","writeFile","spawn"],"sources":["../src/home.ts","../src/auth/credentials.ts","../src/run.ts","../src/deploy/credentials.ts"],"sourcesContent":["/**\n * Where the CLI keeps what belongs to a person rather than a project: stage\n * keys and stored provider credentials.\n *\n * `GKM_HOME` names it, so a CI job, a shared build runner or a test can give\n * every run its own instead of reaching into whoever's home directory the\n * process happens to have. Unset, it is `~/.gkm`, where it has always been.\n */\n\nimport { homedir } from 'node:os';\nimport { join, resolve } from 'node:path';\n\n/** The environment variable that moves the CLI's home. */\nexport const GKM_HOME_ENV = 'GKM_HOME';\n\n/** The CLI's home: `GKM_HOME` if set, else `~/.gkm`. */\nexport function gkmHome(env: NodeJS.ProcessEnv = process.env): string {\n\tconst configured = env[GKM_HOME_ENV];\n\treturn configured ? resolve(configured) : join(homedir(), '.gkm');\n}\n","import { existsSync, mkdirSync } from 'node:fs';\nimport { readFile, unlink, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { gkmHome } from '../home';\n\n/**\n * Stored credentials for various services\n */\nexport interface StoredCredentials {\n\tdokploy?: {\n\t\t/** API token */\n\t\ttoken: string;\n\t\t/** Dokploy endpoint URL */\n\t\tendpoint: string;\n\t\t/** Registry ID in Dokploy (for Docker image pulls) */\n\t\tregistryId?: string;\n\t\t/** When the credentials were stored */\n\t\tstoredAt: string;\n\t};\n\thostinger?: {\n\t\t/** API token from hpanel.hostinger.com/profile/api */\n\t\ttoken: string;\n\t\t/** When the credentials were stored */\n\t\tstoredAt: string;\n\t};\n\tgodaddy?: {\n\t\t/** A Personal Access Token, scoped to domains.dns:update */\n\t\ttoken: string;\n\t\t/** When the credentials were stored */\n\t\tstoredAt: string;\n\t};\n}\n\n/**\n * Options for credential operations\n */\nexport interface CredentialOptions {\n\t/**\n\t * A directory whose `.gkm` holds the credentials. Default: the CLI's home\n\t * (`GKM_HOME`, else `~/.gkm`).\n\t */\n\troot?: string;\n\t/** The CLI's home itself, holding `credentials.json`. Wins over `root`. */\n\thome?: string;\n}\n\n/**\n * Get the path to the credentials directory\n */\nexport function getCredentialsDir(options?: CredentialOptions): string {\n\tif (options?.home) return options.home;\n\treturn options?.root ? join(options.root, '.gkm') : gkmHome();\n}\n\n/**\n * Get the path to the credentials file\n */\nexport function getCredentialsPath(options?: CredentialOptions): string {\n\treturn join(getCredentialsDir(options), 'credentials.json');\n}\n\n/**\n * Ensure the credentials directory exists\n */\nfunction ensureCredentialsDir(options?: CredentialOptions): void {\n\tconst dir = getCredentialsDir(options);\n\tif (!existsSync(dir)) {\n\t\tmkdirSync(dir, { recursive: true, mode: 0o700 });\n\t}\n}\n\n/**\n * Read stored credentials from disk\n */\nexport async function readCredentials(\n\toptions?: CredentialOptions,\n): Promise<StoredCredentials> {\n\tconst path = getCredentialsPath(options);\n\n\tif (!existsSync(path)) {\n\t\treturn {};\n\t}\n\n\ttry {\n\t\tconst content = await readFile(path, 'utf-8');\n\t\treturn JSON.parse(content) as StoredCredentials;\n\t} catch {\n\t\treturn {};\n\t}\n}\n\n/**\n * Write credentials to disk\n */\nexport async function writeCredentials(\n\tcredentials: StoredCredentials,\n\toptions?: CredentialOptions,\n): Promise<void> {\n\tensureCredentialsDir(options);\n\tconst path = getCredentialsPath(options);\n\n\tawait writeFile(path, JSON.stringify(credentials, null, 2), {\n\t\tmode: 0o600, // Owner read/write only\n\t});\n}\n\n/**\n * Store Dokploy credentials\n */\nexport async function storeDokployCredentials(\n\ttoken: string,\n\tendpoint: string,\n\toptions?: CredentialOptions,\n): Promise<void> {\n\tconst credentials = await readCredentials(options);\n\n\tcredentials.dokploy = {\n\t\ttoken,\n\t\tendpoint,\n\t\tstoredAt: new Date().toISOString(),\n\t};\n\n\tawait writeCredentials(credentials, options);\n}\n\n/**\n * Get stored Dokploy credentials\n */\nexport async function getDokployCredentials(\n\toptions?: CredentialOptions,\n): Promise<{\n\ttoken: string;\n\tendpoint: string;\n\tregistryId?: string;\n} | null> {\n\tconst credentials = await readCredentials(options);\n\n\t// The environment first, and each half independently.\n\t//\n\t// `getDokployToken` has always read `DOKPLOY_API_TOKEN` while this — the\n\t// function the deploy path actually calls — read only the file. So a CI job\n\t// given the token as a secret had a token and nowhere to send it, and the\n\t// only way through was committing a credentials file or running an\n\t// interactive login on the runner.\n\t//\n\t// Read separately rather than as a pair because they arrive separately: a\n\t// token is a secret and belongs in the environment, while an endpoint is\n\t// configuration and usually belongs in `deploy.dokploy.endpoint`. Requiring\n\t// both from the same place is what made either one useless alone.\n\tconst token = process.env.DOKPLOY_API_TOKEN ?? credentials.dokploy?.token;\n\tconst endpoint =\n\t\tprocess.env.DOKPLOY_ENDPOINT ?? credentials.dokploy?.endpoint;\n\n\tif (!token || !endpoint) return null;\n\n\treturn {\n\t\ttoken,\n\t\tendpoint: endpoint.replace(/\\/$/, ''),\n\t\t...(credentials.dokploy?.registryId\n\t\t\t? { registryId: credentials.dokploy.registryId }\n\t\t\t: {}),\n\t};\n}\n\n/**\n * Remove Dokploy credentials\n */\nexport async function removeDokployCredentials(\n\toptions?: CredentialOptions,\n): Promise<boolean> {\n\tconst credentials = await readCredentials(options);\n\n\tif (!credentials.dokploy) {\n\t\treturn false;\n\t}\n\n\tdelete credentials.dokploy;\n\tawait writeCredentials(credentials, options);\n\treturn true;\n}\n\n/**\n * Remove all stored credentials\n */\nexport async function removeAllCredentials(\n\toptions?: CredentialOptions,\n): Promise<void> {\n\tconst path = getCredentialsPath(options);\n\n\tif (existsSync(path)) {\n\t\tawait unlink(path);\n\t}\n}\n\n/**\n * Get Dokploy API token, checking stored credentials first, then environment\n */\nexport async function getDokployToken(\n\toptions?: CredentialOptions,\n): Promise<string | null> {\n\t// First check environment variable (takes precedence)\n\tconst envToken = process.env.DOKPLOY_API_TOKEN;\n\tif (envToken) {\n\t\treturn envToken;\n\t}\n\n\t// Then check stored credentials\n\tconst stored = await getDokployCredentials(options);\n\tif (stored) {\n\t\treturn stored.token;\n\t}\n\n\treturn null;\n}\n\n/**\n * Get Dokploy endpoint from stored credentials\n */\nexport async function getDokployEndpoint(\n\toptions?: CredentialOptions,\n): Promise<string | null> {\n\tconst stored = await getDokployCredentials(options);\n\treturn stored?.endpoint ?? null;\n}\n\n/**\n * Store Dokploy registry ID\n */\nexport async function storeDokployRegistryId(\n\tregistryId: string,\n\toptions?: CredentialOptions,\n): Promise<void> {\n\tconst credentials = await readCredentials(options);\n\n\tif (!credentials.dokploy) {\n\t\tthrow new Error(\n\t\t\t'Dokploy credentials not found. Run \"gkm login --service dokploy\" first.',\n\t\t);\n\t}\n\n\tcredentials.dokploy.registryId = registryId;\n\tawait writeCredentials(credentials, options);\n}\n\n/**\n * Get Dokploy registry ID from stored credentials\n */\nexport async function getDokployRegistryId(\n\toptions?: CredentialOptions,\n): Promise<string | undefined> {\n\tconst stored = await getDokployCredentials(options);\n\treturn stored?.registryId ?? undefined;\n}\n\n// ============================================\n// Hostinger credentials\n// ============================================\n\n/**\n * Store Hostinger API token\n *\n * @param token - API token from hpanel.hostinger.com/profile/api\n */\nexport async function storeHostingerToken(\n\ttoken: string,\n\toptions?: CredentialOptions,\n): Promise<void> {\n\tconst credentials = await readCredentials(options);\n\n\tcredentials.hostinger = {\n\t\ttoken,\n\t\tstoredAt: new Date().toISOString(),\n\t};\n\n\tawait writeCredentials(credentials, options);\n}\n\n/**\n * Get stored Hostinger API token\n *\n * Checks environment variable first (HOSTINGER_API_TOKEN),\n * then falls back to stored credentials.\n */\nexport async function getHostingerToken(\n\toptions?: CredentialOptions,\n): Promise<string | null> {\n\t// First check environment variable (takes precedence)\n\tconst envToken = process.env.HOSTINGER_API_TOKEN;\n\tif (envToken) {\n\t\treturn envToken;\n\t}\n\n\t// Then check stored credentials\n\tconst credentials = await readCredentials(options);\n\treturn credentials.hostinger?.token ?? null;\n}\n\n/**\n * Remove Hostinger credentials\n */\nexport async function removeHostingerCredentials(\n\toptions?: CredentialOptions,\n): Promise<boolean> {\n\tconst credentials = await readCredentials(options);\n\n\tif (!credentials.hostinger) {\n\t\treturn false;\n\t}\n\n\tdelete credentials.hostinger;\n\tawait writeCredentials(credentials, options);\n\treturn true;\n}\n\n// ============================================\n// GoDaddy credentials\n// ============================================\n\n/**\n * Store a GoDaddy Personal Access Token.\n */\nexport async function storeGoDaddyToken(\n\ttoken: string,\n\toptions?: CredentialOptions,\n): Promise<void> {\n\tconst credentials = await readCredentials(options);\n\n\tcredentials.godaddy = {\n\t\ttoken,\n\t\tstoredAt: new Date().toISOString(),\n\t};\n\n\tawait writeCredentials(credentials, options);\n}\n\n/**\n * Remove GoDaddy credentials\n */\nexport async function removeGoDaddyCredentials(\n\toptions?: CredentialOptions,\n): Promise<boolean> {\n\tconst credentials = await readCredentials(options);\n\n\tif (!credentials.godaddy) {\n\t\treturn false;\n\t}\n\n\tdelete credentials.godaddy;\n\tawait writeCredentials(credentials, options);\n\treturn true;\n}\n","/**\n * Start a program with an argument array, never a shell string.\n *\n * `gkm build`, `gkm docker` and `gkm deploy` pass values that come from the\n * workspace — image refs, tags, build args, package names — to `docker` and\n * `turbo`. Joined into a string and handed to a shell, a package named\n * `x;curl …|sh` or a tag holding `$(…)` became part of the command line, which\n * matters most where the CLI builds a repository it didn't write: a CI job for a\n * fork, a shared build runner. Here every value reaches the program as one\n * `argv` element, whatever it contains.\n */\n\nimport { type SpawnOptions, spawn } from 'node:child_process';\n\n/** Long enough for a cold image build; short enough that a hung one ends. */\nexport const DEFAULT_TIMEOUT_MS = 30 * 60_000;\n\n/** How long a timed-out child has to exit on SIGTERM before it is killed. */\nconst KILL_GRACE_MS = 5_000;\n\nexport interface RunOptions {\n\tcwd?: string;\n\tenv?: NodeJS.ProcessEnv;\n\tstdio?: SpawnOptions['stdio'];\n\t/** Kill the child after this long. Defaults to {@link DEFAULT_TIMEOUT_MS}. */\n\ttimeoutMs?: number;\n\t/**\n\t * Kills the child when aborted, and rejects with the signal's reason — the\n\t * same as a stopped deploy's other calls.\n\t */\n\tsignal?: AbortSignal;\n}\n\n/** The command line as a person would read it — for messages, never to run. */\nfunction display(command: string, args: readonly string[]): string {\n\treturn [command, ...args].join(' ');\n}\n\n/** The program ran and exited unsuccessfully; its own output said why. */\nexport class CommandFailed extends Error {\n\tconstructor(\n\t\treadonly command: string,\n\t\treadonly args: readonly string[],\n\t\treadonly exitCode: number | null,\n\t\treadonly signal: NodeJS.Signals | null,\n\t) {\n\t\tsuper(\n\t\t\t`\\`${display(command, args)}\\` ${exitCode === null ? `was stopped by ${signal ?? 'a signal'}` : `exited with code ${exitCode}`}. Its output above says why; run it by hand to see it again.`,\n\t\t);\n\t\tthis.name = 'CommandFailed';\n\t}\n}\n\n/** The program ran past its timeout and was killed. */\nexport class CommandTimedOut extends Error {\n\tconstructor(\n\t\treadonly command: string,\n\t\treadonly args: readonly string[],\n\t\treadonly timeoutMs: number,\n\t) {\n\t\tsuper(\n\t\t\t`\\`${display(command, args)}\\` was still running after ${Math.round(timeoutMs / 1000)}s and was stopped. Check what it was waiting on — a registry, the Docker daemon, a prompt — and run it again.`,\n\t\t);\n\t\tthis.name = 'CommandTimedOut';\n\t}\n}\n\n/**\n * Run `command` with `args`, resolving when it exits 0.\n *\n * Rejects with {@link CommandFailed} on any other exit, {@link CommandTimedOut}\n * once `timeoutMs` passes (after the child is gone), and with the spawn error\n * itself when the program cannot be started at all — `ENOENT` already names it.\n */\nexport async function run(\n\tcommand: string,\n\targs: readonly string[],\n\toptions: RunOptions = {},\n): Promise<void> {\n\tawait spawnChild(command, args, options, false);\n}\n\n/**\n * {@link run}, resolving with what the program wrote to stdout — for a value\n * the CLI reads back, such as the digest `docker inspect` reports. stderr still\n * reaches the terminal, so a failure explains itself the same way.\n */\nexport function runOutput(\n\tcommand: string,\n\targs: readonly string[],\n\toptions: Omit<RunOptions, 'stdio'> = {},\n): Promise<string> {\n\treturn spawnChild(command, args, options, true);\n}\n\nfunction spawnChild(\n\tcommand: string,\n\targs: readonly string[],\n\toptions: RunOptions,\n\tcapture: boolean,\n): Promise<string> {\n\tconst { timeoutMs = DEFAULT_TIMEOUT_MS, ...spawnOptions } = options;\n\n\treturn new Promise<string>((resolve, reject) => {\n\t\tconst child = spawn(command, [...args], {\n\t\t\tstdio: 'inherit',\n\t\t\t...spawnOptions,\n\t\t\t...(capture ? { stdio: ['ignore', 'pipe', 'inherit'] } : {}),\n\t\t\tshell: false,\n\t\t});\n\n\t\tlet stdout = '';\n\t\tchild.stdout?.on('data', (chunk: Buffer) => {\n\t\t\tstdout += chunk.toString();\n\t\t});\n\n\t\tlet timedOut = false;\n\t\tlet killer: NodeJS.Timeout | undefined;\n\t\tconst timer = setTimeout(() => {\n\t\t\ttimedOut = true;\n\t\t\tchild.kill('SIGTERM');\n\t\t\t// A child that ignores SIGTERM would hold the deploy open forever.\n\t\t\tkiller = setTimeout(() => child.kill('SIGKILL'), KILL_GRACE_MS);\n\t\t\tkiller.unref?.();\n\t\t}, timeoutMs);\n\t\ttimer.unref?.();\n\n\t\tconst settle = () => {\n\t\t\tclearTimeout(timer);\n\t\t\tif (killer) clearTimeout(killer);\n\t\t};\n\n\t\tchild.on('error', (error) => {\n\t\t\tsettle();\n\t\t\t// Node reports an abort as its own `AbortError`; the caller asked\n\t\t\t// with a reason, and that is what it should get back.\n\t\t\treject(spawnOptions.signal?.aborted ? spawnOptions.signal.reason : error);\n\t\t});\n\n\t\tchild.on('close', (code: number | null, signal: NodeJS.Signals | null) => {\n\t\t\tsettle();\n\t\t\tif (spawnOptions.signal?.aborted) reject(spawnOptions.signal.reason);\n\t\t\telse if (timedOut) reject(new CommandTimedOut(command, args, timeoutMs));\n\t\t\telse if (code === 0) resolve(stdout);\n\t\t\telse reject(new CommandFailed(command, args, code, signal));\n\t\t});\n\t});\n}\n","/**\n * How a deploy gets the credentials it acts with.\n *\n * `deploy()` never prompts and never reads a terminal: it asks a\n * `CredentialProvider` and, when the provider has nothing, stops with\n * `MissingCredential` naming what it needed and how to supply it. Whether to\n * ask a person, read a vault or fail is the caller's decision — `gkm deploy`\n * prompts at a terminal and stores what was typed; a CI job sets the\n * environment; a host passes its own provider.\n */\n\nimport { readCredentials } from '../auth/credentials';\n\n/**\n * What each kind of credential is asked for with, and what it is.\n *\n * A target that needs a kind the CLI does not know declares it by augmenting\n * this interface, and lists it in its `credentials`:\n *\n * ```ts\n * declare module '@geekmidas/cli/target' {\n * interface CredentialKinds {\n * fly: { request: { org: string }; value: { token: string } };\n * }\n * }\n * ```\n */\nexport interface CredentialKinds {\n\t/** The Dokploy API a stage deploys through. */\n\tdokploy: {\n\t\t/** `deploy.dokploy.endpoint`, when the config names one. */\n\t\trequest: { endpoint?: string };\n\t\tvalue: { endpoint: string; token: string };\n\t};\n\t/**\n\t * A container registry login, for Dokploy to pull with. Asked for only when\n\t * Dokploy has no registry for `deploy.registry` and one has to be\n\t * created.\n\t */\n\tregistry: {\n\t\t/** `deploy.registry`. */\n\t\trequest: { url: string };\n\t\tvalue: { username: string; password: string };\n\t};\n\t/**\n\t * The AWS account a stage deploys to, for the SST target's `sst deploy`.\n\t * Handed to that one command's environment and to nothing else.\n\t */\n\taws: {\n\t\t/** The stage, for a provider that keeps one account per stage. */\n\t\trequest: { stage: string };\n\t\tvalue: AwsCredential;\n\t};\n\t/**\n\t * A GoDaddy Personal Access Token, for a `dns` domain whose provider is\n\t * `godaddy`. Read on the machine that writes the records — never the\n\t * server's.\n\t */\n\tgodaddy: {\n\t\trequest: Record<never, never>;\n\t\tvalue: GoDaddyCredential;\n\t};\n}\n\n/** A GoDaddy Personal Access Token, sent as `Authorization: Bearer <token>`. */\nexport interface GoDaddyCredential {\n\ttoken: string;\n}\n\n/**\n * Who to act as on AWS: a named profile, or keys.\n *\n * Never both. Given both, every AWS SDK takes the keys and ignores the\n * profile, so `--profile prod` with staging's keys still exported would deploy\n * production's stack into staging's account — the reason `secretsStoreFor`\n * resolves a named profile on its own, and why this cannot express the mix.\n */\nexport type AwsCredential =\n\t| {\n\t\t\t/** A profile in `~/.aws/config` — keys, SSO, an assumed role. */\n\t\t\tprofile: string;\n\t\t\tregion?: string;\n\t }\n\t| {\n\t\t\taccessKeyId: string;\n\t\t\tsecretAccessKey: string;\n\t\t\t/** Present for temporary keys: an assumed role, GitHub's OIDC. */\n\t\t\tsessionToken?: string;\n\t\t\tregion?: string;\n\t };\n\nexport type CredentialKind = keyof CredentialKinds;\n\n/** What a deploy asks a provider for. */\nexport type CredentialRequest<K extends CredentialKind = CredentialKind> = {\n\t[P in K]: { kind: P } & CredentialKinds[P]['request'];\n}[K];\n\n/** What a provider answers with. */\nexport type Credential<K extends CredentialKind> = CredentialKinds[K]['value'];\n\nexport interface CredentialProvider {\n\t/**\n\t * The credential `request` asks for, or `undefined` when this provider has\n\t * none — the deploy then raises {@link MissingCredential}.\n\t */\n\tget<K extends CredentialKind>(\n\t\trequest: CredentialRequest<K>,\n\t\toptions?: { signal?: AbortSignal },\n\t): Promise<Credential<K> | undefined>;\n}\n\n/**\n * How to supply each kind the CLI knows, for the error that says it is\n * missing. A target's own kinds are its to explain.\n */\nconst HOW_TO_PROVIDE: Partial<Record<CredentialKind, string>> = {\n\tdokploy:\n\t\t\"Set DOKPLOY_API_TOKEN (and DOKPLOY_ENDPOINT, or deploy.dokploy.endpoint in gkm.config.ts), run `gkm login --provider dokploy`, or pass them through the deploy's CredentialProvider.\",\n\tregistry:\n\t\t'Add the registry in Dokploy (Settings → Docker Registry) and set deploy.dokploy.registryId, set DOCKER_REGISTRY_USERNAME and DOCKER_REGISTRY_PASSWORD, or run `gkm deploy` at a terminal to be asked for them.',\n\tgodaddy:\n\t\t'Set GODADDY_API_TOKEN to a Personal Access Token scoped to domains.dns:update (create one in the GoDaddy developer dashboard), or run `gkm login --provider godaddy`.',\n\taws: \"Set AWS_PROFILE to the stage account's profile, or AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY (and AWS_SESSION_TOKEN) — in CI, the keys aws-actions/configure-aws-credentials exports — or pass them through the deploy's CredentialProvider.\",\n};\n\n/** A deploy needed a credential, and its provider had none. */\nexport class MissingCredential extends Error {\n\tconstructor(\n\t\treadonly kind: CredentialKind,\n\t\t/** What it was for: the Dokploy endpoint or the registry URL. */\n\t\treadonly target: string | undefined,\n\t\t/** How to supply it. */\n\t\treadonly howToProvide: string = HOW_TO_PROVIDE[kind] ??\n\t\t\t\"Pass it through the deploy's CredentialProvider.\",\n\t) {\n\t\tconst what =\n\t\t\tkind === 'dokploy'\n\t\t\t\t? `No Dokploy credentials${target ? ` for ${target}` : ''}.`\n\t\t\t\t: kind === 'registry'\n\t\t\t\t\t? `Dokploy has no registry for ${target ?? 'the configured registry'}, and there are no credentials to create one with.`\n\t\t\t\t\t: kind === 'aws'\n\t\t\t\t\t\t? `No AWS credentials${target ? ` for the \"${target}\" stage` : ''}.`\n\t\t\t\t\t\t: kind === 'godaddy'\n\t\t\t\t\t\t\t? `No GoDaddy API token${target ? ` for ${target}` : ''}.`\n\t\t\t\t\t\t\t: `No \"${String(kind)}\" credentials${target ? ` for ${target}` : ''}.`;\n\t\tsuper(`${what} ${howToProvide}`);\n\t\tthis.name = 'MissingCredential';\n\t}\n}\n\n/** Whatever was asked for, from the first provider that has it. */\nexport function chainCredentials(\n\t...providers: CredentialProvider[]\n): CredentialProvider {\n\treturn {\n\t\tasync get(request, options) {\n\t\t\tfor (const provider of providers) {\n\t\t\t\tconst found = await provider.get(request, options);\n\t\t\t\tif (found) return found;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t},\n\t};\n}\n\nexport interface StoredCredentialsOptions {\n\t/** Where to read the variables from. Defaults to `process.env`. */\n\tenv?: NodeJS.ProcessEnv;\n\t/**\n\t * The CLI's home, whose `credentials.json` holds stored logins. Defaults\n\t * to `GKM_HOME`, else `~/.gkm`.\n\t */\n\thome?: string;\n}\n\n/**\n * Credentials from the environment, then from what `gkm login` stored. What\n * `deploy()` uses when it is handed no provider.\n *\n * - Dokploy: `DOKPLOY_API_TOKEN` / `DOKPLOY_ENDPOINT`, each falling back to\n * the stored login, and the endpoint finally to `deploy.dokploy.endpoint`.\n * Each half is read on its own: a token is a secret and belongs in the\n * environment, an endpoint is configuration and belongs in the config.\n * - Registry: `DOCKER_REGISTRY_USERNAME` / `DOCKER_REGISTRY_PASSWORD`.\n * - AWS: `AWS_PROFILE` when it is set, and then nothing else; otherwise\n * `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` / `AWS_SESSION_TOKEN`. The\n * profile winning is `secretsStoreFor`'s rule: a profile is a choice of\n * account someone made, exported keys may be left over from another.\n * Read here, by name, and handed only to the command that deploys — the\n * sandbox every other step runs in passes no `AWS_*` on.\n */\nexport function storedCredentials(\n\toptions: StoredCredentialsOptions = {},\n): CredentialProvider {\n\tconst env = options.env ?? process.env;\n\n\treturn {\n\t\tasync get(asked) {\n\t\t\t// The union, so a switch on `kind` narrows it: `K` itself never is.\n\t\t\tconst request = asked as CredentialRequest;\n\t\t\tswitch (request.kind) {\n\t\t\t\tcase 'dokploy': {\n\t\t\t\t\tconst stored = (\n\t\t\t\t\t\tawait readCredentials(\n\t\t\t\t\t\t\toptions.home ? { home: options.home } : undefined,\n\t\t\t\t\t\t)\n\t\t\t\t\t).dokploy;\n\t\t\t\t\tconst token = env.DOKPLOY_API_TOKEN ?? stored?.token;\n\t\t\t\t\tconst endpoint =\n\t\t\t\t\t\tenv.DOKPLOY_ENDPOINT ?? stored?.endpoint ?? request.endpoint;\n\t\t\t\t\tif (!token || !endpoint) return undefined;\n\t\t\t\t\treturn { endpoint: endpoint.replace(/\\/$/, ''), token } as never;\n\t\t\t\t}\n\t\t\t\tcase 'registry': {\n\t\t\t\t\tconst username = env.DOCKER_REGISTRY_USERNAME;\n\t\t\t\t\tconst password = env.DOCKER_REGISTRY_PASSWORD;\n\t\t\t\t\tif (!username || !password) return undefined;\n\t\t\t\t\treturn { username, password } as never;\n\t\t\t\t}\n\t\t\t\tcase 'godaddy': {\n\t\t\t\t\t// The environment first, then the stored login.\n\t\t\t\t\tconst token =\n\t\t\t\t\t\tenv.GODADDY_API_TOKEN ??\n\t\t\t\t\t\t(\n\t\t\t\t\t\t\tawait readCredentials(\n\t\t\t\t\t\t\t\toptions.home ? { home: options.home } : undefined,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t).godaddy?.token;\n\t\t\t\t\tif (!token) return undefined;\n\t\t\t\t\treturn { token } as never;\n\t\t\t\t}\n\t\t\t\tcase 'aws': {\n\t\t\t\t\tconst region = env.AWS_REGION ?? env.AWS_DEFAULT_REGION;\n\t\t\t\t\tconst where = region ? { region } : {};\n\t\t\t\t\tif (env.AWS_PROFILE) {\n\t\t\t\t\t\treturn { profile: env.AWS_PROFILE, ...where } as never;\n\t\t\t\t\t}\n\t\t\t\t\tconst accessKeyId = env.AWS_ACCESS_KEY_ID;\n\t\t\t\t\tconst secretAccessKey = env.AWS_SECRET_ACCESS_KEY;\n\t\t\t\t\tif (!accessKeyId || !secretAccessKey) return undefined;\n\t\t\t\t\treturn {\n\t\t\t\t\t\taccessKeyId,\n\t\t\t\t\t\tsecretAccessKey,\n\t\t\t\t\t\t...(env.AWS_SESSION_TOKEN\n\t\t\t\t\t\t\t? { sessionToken: env.AWS_SESSION_TOKEN }\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t...where,\n\t\t\t\t\t} as never;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// A kind a target declared: nothing stored here can answer it.\n\t\t\treturn undefined;\n\t\t},\n\t};\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"databases-BY-pEImp.cjs","names":["readdir","extname","join","pathToFileURL","stat","readFile","join","migrationTargets","MIGRATIONS_ROOT","canonicalId","readdir","DATABASE_FOLDERS","provideKey","require","createRequire","pathToFileURL"],"sources":["../src/migrate/provider.ts","../src/migrate/databases.ts"],"sourcesContent":["/**\n * One construct's migrations and seeds, read from its folders.\n *\n * Two kinds of file, told apart by extension rather than by configuration: a\n * script exports `up` (and may export `down`) — or `seed(db, { stage })`, in\n * `seeds/` — and a\n * `.sql` file is run whole. The second is what makes a folder independent of\n * the client the app uses — Better Auth's schema is SQL whatever the\n * application queries with.\n */\n\nimport { readdir, readFile, stat } from 'node:fs/promises';\nimport { extname, join } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { SeedContext } from '@geekmidas/constructs';\nimport type { Kysely, sql } from 'kysely';\nimport type { Migration, MigrationProvider } from 'kysely/migration';\n\nconst SCRIPTS = new Set(['.ts', '.mts', '.js', '.mjs']);\n\n/** Two files in one folder that would be one migration. */\nexport class DuplicateMigration extends Error {\n\tconstructor(\n\t\treadonly folder: string,\n\t\treadonly migration: string,\n\t) {\n\t\tsuper(\n\t\t\t`${folder} has two migrations named '${migration}' — a script and a .sql file ` +\n\t\t\t\t`with the same name. A migration is recorded by name, so only one of ` +\n\t\t\t\t`them could ever run; rename or remove the other.`,\n\t\t);\n\t\tthis.name = 'DuplicateMigration';\n\t}\n}\n\n/** A script in a migrations folder that exports no `up`. */\nexport class MigrationHasNoUp extends Error {\n\tconstructor(readonly file: string) {\n\t\tsuper(\n\t\t\t`${file} is in a migrations folder but exports no \\`up\\` function. ` +\n\t\t\t\t`Export \\`async function up(db: Kysely<unknown>)\\`, or move the file ` +\n\t\t\t\t`out of the folder if it is not a migration.`,\n\t\t);\n\t\tthis.name = 'MigrationHasNoUp';\n\t}\n}\n\n/** A script in a seeds folder that exports no `seed`. */\nexport class SeedHasNoSeed extends Error {\n\tconstructor(readonly file: string) {\n\t\tsuper(\n\t\t\t`${file} is in a seeds folder but exports no \\`seed\\` function. ` +\n\t\t\t\t`Export \\`async function seed(db, { stage })\\`, or move the ` +\n\t\t\t\t`file out of the folder if it is not a seed.`,\n\t\t);\n\t\tthis.name = 'SeedHasNoSeed';\n\t}\n}\n\n/**\n * What a seed is handed beside its database — `@geekmidas/constructs`'\n * `SeedContext`, whose `stage` is the union of the declared stages once\n * `.gkm/stages.d.ts` is generated.\n */\nexport type { SeedContext };\n\n/** One seed: what it is called, and what it does. */\nexport interface Seed {\n\tname: string;\n\trun: (db: Kysely<unknown>, context: SeedContext) => Promise<void>;\n}\n\n/**\n * The migration files in a folder, by name, without reading them.\n *\n * Also what `gkm dev` compares against a construct's history to report what\n * is pending, which is why it is separate from loading them.\n */\nexport async function migrationFiles(\n\tfolder: string,\n): Promise<Map<string, string>> {\n\tconst entries = await readdir(folder, { withFileTypes: true }).catch(\n\t\t(error: NodeJS.ErrnoException) => {\n\t\t\tif (error.code === 'ENOENT') return [];\n\t\t\tthrow error;\n\t\t},\n\t);\n\n\tconst files = new Map<string, string>();\n\tfor (const entry of entries) {\n\t\tif (!entry.isFile() || !isMigration(entry.name)) continue;\n\n\t\tconst name = entry.name.slice(0, -extname(entry.name).length);\n\t\tif (files.has(name)) throw new DuplicateMigration(folder, name);\n\n\t\tfiles.set(name, join(folder, entry.name));\n\t}\n\n\treturn files;\n}\n\n/** A Kysely provider over one construct's folder. */\nexport function folderProvider(\n\tfolder: string,\n\traw: typeof sql,\n): MigrationProvider {\n\treturn {\n\t\tasync getMigrations() {\n\t\t\tconst migrations: Record<string, Migration> = {};\n\n\t\t\tfor (const [name, file] of await migrationFiles(folder)) {\n\t\t\t\tmigrations[name] =\n\t\t\t\t\textname(file) === '.sql'\n\t\t\t\t\t\t? await sqlMigration(file, raw)\n\t\t\t\t\t\t: await scriptMigration(file);\n\t\t\t}\n\n\t\t\treturn migrations;\n\t\t},\n\t};\n}\n\n/**\n * The seeds in a folder, loaded, in name order.\n *\n * Every one runs on every pass — there is no history to consult — so the order\n * is the only thing between two seeds where one reads what another writes.\n * Name them to sort that way.\n */\nexport async function loadSeeds(\n\tfolder: string,\n\traw: typeof sql,\n): Promise<Seed[]> {\n\tconst files = [...(await migrationFiles(folder))].sort(([a], [b]) =>\n\t\ta < b ? -1 : a > b ? 1 : 0,\n\t);\n\n\tconst seeds: Seed[] = [];\n\tfor (const [name, file] of files) {\n\t\tseeds.push({\n\t\t\tname,\n\t\t\trun:\n\t\t\t\textname(file) === '.sql'\n\t\t\t\t\t? (await sqlMigration(file, raw)).up\n\t\t\t\t\t: await scriptSeed(file),\n\t\t});\n\t}\n\n\treturn seeds;\n}\n\n/**\n * A seed script, imported as it is on disk now.\n *\n * Keyed by its modification time: a seed is run again after it is edited, and\n * a process that has imported it once would otherwise run the old one.\n */\nasync function scriptSeed(file: string): Promise<Seed['run']> {\n\tconst url = pathToFileURL(file);\n\turl.searchParams.set('mtime', String((await stat(file)).mtimeMs));\n\tconst module = (await import(url.href)) as { seed?: unknown };\n\tif (typeof module.seed !== 'function') throw new SeedHasNoSeed(file);\n\n\treturn module.seed as Seed['run'];\n}\n\nfunction isMigration(file: string): boolean {\n\tif (file.endsWith('.d.ts')) return false;\n\tif (/\\.(spec|test)\\.[cm]?[jt]s$/.test(file)) return false;\n\n\tconst extension = extname(file);\n\treturn extension === '.sql' || SCRIPTS.has(extension);\n}\n\nasync function scriptMigration(file: string): Promise<Migration> {\n\tconst module = (await import(pathToFileURL(file).href)) as Partial<Migration>;\n\tif (typeof module.up !== 'function') throw new MigrationHasNoUp(file);\n\n\treturn {\n\t\tup: module.up,\n\t\t...(typeof module.down === 'function' ? { down: module.down } : {}),\n\t};\n}\n\n/**\n * A `.sql` file, run whole.\n *\n * One query rather than split on `;`: with no parameters, Postgres takes\n * several statements in one message, and splitting by hand breaks on the first\n * `;` inside a string or a function body. There is no `down` — a generated\n * schema is rolled back by a migration that says how, not by guessing.\n */\nasync function sqlMigration(file: string, raw: typeof sql): Promise<Migration> {\n\tconst statements = await readFile(file, 'utf-8');\n\n\treturn {\n\t\tup: async (db: Kysely<unknown>) => {\n\t\t\tawait raw.raw(statements).execute(db);\n\t\t},\n\t};\n}\n","/**\n * Bring each construct's schema up to its migrations folder.\n *\n * Which constructs, in what order, from which folder, is `migrationTargets`'\n * answer and nobody else's. What is decided here is only how one target is\n * run: as its owner, in its own schema, with a history of its own.\n */\n\nimport { readdir } from 'node:fs/promises';\nimport { createRequire } from 'node:module';\nimport { join } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport {\n\ttype ConstructManifest,\n\tcanonicalId,\n\tDATABASE_FOLDERS,\n\tMIGRATIONS_ROOT,\n\ttype MigrationTarget,\n\tmigrationTargets,\n\tprovideKey,\n} from '@geekmidas/manifest';\nimport type { Kysely } from 'kysely';\nimport type { Migrator } from 'kysely/migration';\nimport pg from 'pg';\nimport type { ConstructSource } from '../reconcile/discover';\nimport { folderProvider, loadSeeds, migrationFiles } from './provider';\n\nexport interface DatabasesOptions {\n\t/** The project root — where `db/` lives. */\n\troot: string;\n\tmanifest: ConstructManifest;\n\t/**\n\t * Where each construct was declared. A construct's migrations run with the\n\t * Kysely its own file resolves, which is the one its migrations import.\n\t */\n\tsources?: Readonly<Record<string, ConstructSource>>;\n\t/** The resolved environment: each construct's owner URL is read from it. */\n\tenv: Readonly<Record<string, string | undefined>>;\n\t/** One construct, by name. All of them when absent. */\n\tonly?: string;\n}\n\nexport interface MigrationRun {\n\ttarget: MigrationTarget;\n\t/** The migrations this run applied, in order. */\n\tapplied: string[];\n}\n\nexport interface SeedOptions extends DatabasesOptions {\n\t/** The stage being seeded, handed to every seed. */\n\tstage: string;\n}\n\nexport interface SeedRun {\n\ttarget: MigrationTarget;\n\t/** The seeds this run ran, in order — every one, every time. */\n\tseeded: string[];\n}\n\n/** The seeds a run would run for one construct, read without connecting. */\nexport interface PlannedSeeds {\n\ttarget: MigrationTarget;\n\t/** In the order they would run. */\n\tseeds: string[];\n}\n\nexport interface PendingMigrations {\n\ttarget: MigrationTarget;\n\tpending: string[];\n}\n\n/** A folder under `db/` that names no construct. */\nexport class UnknownMigrationFolder extends Error {\n\tconstructor(\n\t\treadonly folder: string,\n\t\treadonly known: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`${folder} names no database construct, so its migrations would never ` +\n\t\t\t\t`run. ${known.length > 0 ? `The folders that do: ${known.join(', ')}.` : 'This project declares no database.'} ` +\n\t\t\t\t`Rename the folder after the construct it belongs to.`,\n\t\t);\n\t\tthis.name = 'UnknownMigrationFolder';\n\t}\n}\n\n/** A construct was asked for by a name that takes no migrations. */\nexport class NoSuchMigrationTarget extends Error {\n\tconstructor(\n\t\treadonly construct: string,\n\t\treadonly known: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`'${construct}' is not a database construct. ` +\n\t\t\t\t(known.length > 0\n\t\t\t\t\t? `The ones with migrations: ${known.join(', ')}.`\n\t\t\t\t\t: 'This project declares no database.'),\n\t\t);\n\t\tthis.name = 'NoSuchMigrationTarget';\n\t}\n}\n\n/**\n * A migration left directly in a construct's folder, where nothing reads it.\n *\n * Migrations moved into `migrations/` so seeds could sit beside them. A file\n * still at the old level would otherwise never run, and nothing would say so.\n */\nexport class MigrationsOutsideFolder extends Error {\n\tconstructor(\n\t\treadonly folder: string,\n\t\treadonly files: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`${folder} has ${files.join(', ')} directly inside it, where nothing ` +\n\t\t\t\t`runs them. Migrations live in ${folder}/migrations/ and seeds in ` +\n\t\t\t\t`${folder}/seeds/ — move ${files.length === 1 ? 'it' : 'them'} there.`,\n\t\t);\n\t\tthis.name = 'MigrationsOutsideFolder';\n\t}\n}\n\n/** A folder inside a construct's folder that is neither `migrations` nor `seeds`. */\nexport class UnknownDatabaseFolder extends Error {\n\tconstructor(readonly folder: string) {\n\t\tsuper(\n\t\t\t`${folder} is neither migrations/ nor seeds/, so nothing in it would ` +\n\t\t\t\t`ever run. A construct's folder holds those two; rename or remove it.`,\n\t\t);\n\t\tthis.name = 'UnknownDatabaseFolder';\n\t}\n}\n\n/** A seed failed; its own writes are rolled back, the ones before it stay. */\nexport class SeedFailed extends Error {\n\tconstructor(\n\t\treadonly construct: string,\n\t\treadonly seed: string,\n\t\toverride readonly cause: unknown,\n\t) {\n\t\tsuper(\n\t\t\t`${construct}: seed '${seed}' failed — ` +\n\t\t\t\t`${cause instanceof Error ? cause.message : String(cause)}. ` +\n\t\t\t\t`Its writes were rolled back; fix it and run it again.`,\n\t\t);\n\t\tthis.name = 'SeedFailed';\n\t}\n}\n\n/** A construct with roles has no owner URL to migrate with. */\nexport class NoOwnerCredential extends Error {\n\tconstructor(\n\t\treadonly construct: string,\n\t\treadonly key: string,\n\t) {\n\t\tsuper(\n\t\t\t`${construct} has no ${key} to migrate with. Migrations run as the owner ` +\n\t\t\t\t`role — the runtime one cannot create a table — and nothing ` +\n\t\t\t\t`resolved its URL. Is the stage reconciled?`,\n\t\t);\n\t\tthis.name = 'NoOwnerCredential';\n\t}\n}\n\n/** A migration failed; the ones before it in the run stay applied. */\nexport class MigrationFailed extends Error {\n\tconstructor(\n\t\treadonly construct: string,\n\t\treadonly migration: string | undefined,\n\t\toverride readonly cause: unknown,\n\t) {\n\t\tsuper(\n\t\t\t`${construct}: migration ${migration ? `'${migration}' ` : ''}failed — ` +\n\t\t\t\t`${cause instanceof Error ? cause.message : String(cause)}`,\n\t\t);\n\t\tthis.name = 'MigrationFailed';\n\t}\n}\n\n/** The project's Kysely could not be found from where a database is declared. */\nexport class KyselyNotFound extends Error {\n\tconstructor(readonly from: string) {\n\t\tsuper(\n\t\t\t`Could not resolve 'kysely' from ${from}. Migrations run with the ` +\n\t\t\t\t`project's own Kysely — install it where the database is declared.`,\n\t\t);\n\t\tthis.name = 'KyselyNotFound';\n\t}\n}\n\n/** Apply every pending migration, construct by construct, parents first. */\nexport async function migrateDatabases(\n\toptions: DatabasesOptions,\n): Promise<MigrationRun[]> {\n\tconst runs: MigrationRun[] = [];\n\n\tfor (const target of await targetsFor(options)) {\n\t\t// No folder, or an empty one, is nothing to do — and no reason to\n\t\t// connect, which a construct nobody has written a migration for yet\n\t\t// should not need.\n\t\tconst files = await migrationFiles(join(options.root, target.migrations));\n\t\tif (files.size === 0) {\n\t\t\truns.push({ target, applied: [] });\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst applied = await withMigrator(options, target, async (migrator) => {\n\t\t\tconst { error, results = [] } = await migrator.migrateToLatest();\n\t\t\tif (error) {\n\t\t\t\tconst failed = results.find((result) => result.status === 'Error');\n\t\t\t\tthrow new MigrationFailed(target.id, failed?.migrationName, error);\n\t\t\t}\n\n\t\t\treturn results\n\t\t\t\t.filter((result) => result.status === 'Success')\n\t\t\t\t.map((result) => result.migrationName);\n\t\t});\n\n\t\truns.push({ target, applied });\n\t}\n\n\treturn runs;\n}\n\n/**\n * Run every construct's seeds, parents first — each one, every time, on every\n * stage, production included. Each is handed the stage, so a seed decides\n * what belongs where.\n *\n * Seeds are reference data kept up to date by being run again, so there is no\n * history: a seed is an upsert, and a changed one is applied by the next run.\n * Each runs in its own transaction, as the construct's owner, so a failure\n * leaves nothing half-written. Assumes the schema is migrated —\n * {@link migrateAndSeed} is what makes that true.\n */\nexport async function seedDatabases(options: SeedOptions): Promise<SeedRun[]> {\n\tconst runs: SeedRun[] = [];\n\n\tfor (const target of await targetsFor(options)) {\n\t\tconst folder = join(options.root, target.seeds);\n\t\tif ((await migrationFiles(folder)).size === 0) {\n\t\t\truns.push({ target, seeded: [] });\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst seeded = await withOwner(options, target, async (db, kysely) => {\n\t\t\tconst names: string[] = [];\n\t\t\tfor (const seed of await loadSeeds(folder, kysely.sql)) {\n\t\t\t\ttry {\n\t\t\t\t\tawait db\n\t\t\t\t\t\t.transaction()\n\t\t\t\t\t\t.execute((trx) => seed.run(trx, { stage: options.stage }));\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthrow new SeedFailed(target.id, seed.name, error);\n\t\t\t\t}\n\t\t\t\tnames.push(seed.name);\n\t\t\t}\n\t\t\treturn names;\n\t\t});\n\n\t\truns.push({ target, seeded });\n\t}\n\n\treturn runs;\n}\n\n/**\n * The seeds {@link seedDatabases} would run, construct by construct, parents\n * first, in the order it would run them — read from the folders alone, with\n * no connection. What a dry run lists. Constructs with none are left out.\n */\nexport async function plannedSeeds(\n\toptions: Pick<DatabasesOptions, 'root' | 'manifest' | 'only'>,\n): Promise<PlannedSeeds[]> {\n\tconst planned: PlannedSeeds[] = [];\n\tfor (const target of await targetsFor(options)) {\n\t\tconst files = await migrationFiles(join(options.root, target.seeds));\n\t\tif (files.size === 0) continue;\n\t\tplanned.push({ target, seeds: [...files.keys()].sort() });\n\t}\n\treturn planned;\n}\n\n/**\n * Migrate, then seed: a seed only ever runs against the schema it was written\n * for.\n *\n * With `only`, the constructs it lives in are migrated too — a tenant's seed\n * cannot run before the database that owns its schema exists — and only it is\n * seeded.\n */\nexport async function migrateAndSeed(options: SeedOptions): Promise<{\n\tmigrations: MigrationRun[];\n\tseeds: SeedRun[];\n}> {\n\tconst migrations: MigrationRun[] = [];\n\tfor (const target of await withParents(options)) {\n\t\tmigrations.push(\n\t\t\t...(await migrateDatabases({ ...options, only: target.id })),\n\t\t);\n\t}\n\n\treturn { migrations, seeds: await seedDatabases(options) };\n}\n\n/** The targets `only` names, after the ones it lives in; all without it. */\nasync function withParents(\n\toptions: DatabasesOptions,\n): Promise<MigrationTarget[]> {\n\tconst chosen = await targetsFor(options);\n\tif (!options.only) return chosen;\n\n\tconst all = migrationTargets(options.manifest);\n\tconst ids = new Set<string>();\n\tfor (const target of chosen) {\n\t\tlet current: MigrationTarget | undefined = target;\n\t\twhile (current) {\n\t\t\tids.add(current.id);\n\t\t\tconst parent: string | undefined = current.of;\n\t\t\tcurrent = parent ? all.find((t) => t.id === parent) : undefined;\n\t\t}\n\t}\n\n\treturn all.filter((target) => ids.has(target.id));\n}\n\n/**\n * What each construct's folder has that its history does not — without\n * applying any of it. What `gkm dev` reports.\n */\nexport async function pendingMigrations(\n\toptions: DatabasesOptions,\n): Promise<PendingMigrations[]> {\n\tconst found: PendingMigrations[] = [];\n\n\tfor (const target of await targetsFor(options)) {\n\t\tconst files = await migrationFiles(join(options.root, target.migrations));\n\t\tif (files.size === 0) continue;\n\n\t\tconst pending = await withMigrator(options, target, async (migrator) =>\n\t\t\t(await migrator.getMigrations())\n\t\t\t\t.filter((migration) => migration.executedAt === undefined)\n\t\t\t\t.map((migration) => migration.name),\n\t\t);\n\n\t\tif (pending.length > 0) found.push({ target, pending });\n\t}\n\n\treturn found;\n}\n\n/**\n * The targets this run covers, with every folder under `db/` accounted for.\n *\n * A folder that names no construct is an error rather than something to skip:\n * a typo in its name is otherwise a migration that silently never runs.\n */\nasync function targetsFor(\n\toptions: Pick<DatabasesOptions, 'root' | 'manifest' | 'only'>,\n): Promise<MigrationTarget[]> {\n\tconst targets = migrationTargets(options.manifest);\n\tconst folders = targets.map((target) => target.folder);\n\n\tfor (const folder of await foldersIn(join(options.root, MIGRATIONS_ROOT))) {\n\t\tconst relative = `${MIGRATIONS_ROOT}/${folder}`;\n\t\tif (!folders.includes(relative)) {\n\t\t\tthrow new UnknownMigrationFolder(relative, folders);\n\t\t}\n\t\tawait checkLayout(options.root, relative);\n\t}\n\n\tif (!options.only) return targets;\n\n\tconst id = canonicalId(options.only);\n\tconst chosen = targets.filter((target) => target.id === id);\n\tif (chosen.length === 0) {\n\t\tthrow new NoSuchMigrationTarget(\n\t\t\toptions.only,\n\t\t\ttargets.map((target) => target.id),\n\t\t);\n\t}\n\n\treturn chosen;\n}\n\n/**\n * A construct's folder holds `migrations/` and `seeds/`, and nothing else —\n * anything beside them is a file or folder nothing would ever run.\n */\nasync function checkLayout(root: string, folder: string): Promise<void> {\n\tconst entries = await readdir(join(root, folder), { withFileTypes: true });\n\n\tconst loose = entries\n\t\t.filter((entry) => entry.isFile() && !entry.name.startsWith('.'))\n\t\t.map((entry) => entry.name);\n\tif (loose.length > 0) throw new MigrationsOutsideFolder(folder, loose);\n\n\tfor (const entry of entries) {\n\t\tif (\n\t\t\tentry.isDirectory() &&\n\t\t\t!(DATABASE_FOLDERS as readonly string[]).includes(entry.name)\n\t\t) {\n\t\t\tthrow new UnknownDatabaseFolder(`${folder}/${entry.name}`);\n\t\t}\n\t}\n}\n\nasync function foldersIn(dir: string): Promise<string[]> {\n\tconst entries = await readdir(dir, { withFileTypes: true }).catch(\n\t\t(error: NodeJS.ErrnoException) => {\n\t\t\tif (error.code === 'ENOENT') return [];\n\t\t\tthrow error;\n\t\t},\n\t);\n\n\treturn entries\n\t\t.filter((entry) => entry.isDirectory())\n\t\t.map((entry) => entry.name);\n}\n\n/**\n * A migrator for one target, connected as its owner.\n *\n * No `migrationTableSchema`: the owner role's `search_path` is pinned to the\n * construct's schema, so the history lands there — `app.kysely_migration` for\n * the database, `auth_database.kysely_migration` for its tenant — and two\n * constructs in one Postgres never share one.\n *\n * Unordered migrations are allowed. Files are timestamped, so a branch merged\n * after another's migration ran carries an older one; refusing it would make\n * every such merge a manual repair.\n */\nasync function withMigrator<T>(\n\toptions: DatabasesOptions,\n\ttarget: MigrationTarget,\n\trun: (migrator: Migrator) => Promise<T>,\n): Promise<T> {\n\treturn withOwner(options, target, (db, { Migrator, sql }) =>\n\t\trun(\n\t\t\tnew Migrator({\n\t\t\t\tdb,\n\t\t\t\tprovider: folderProvider(join(options.root, target.migrations), sql),\n\t\t\t\tallowUnorderedMigrations: true,\n\t\t\t}),\n\t\t),\n\t);\n}\n\n/**\n * One target's database, connected as its owner and closed afterwards — what\n * migrations and seeds both run as.\n */\nasync function withOwner<T>(\n\toptions: DatabasesOptions,\n\ttarget: MigrationTarget,\n\trun: (\n\t\tdb: Kysely<unknown>,\n\t\tkysely: Awaited<ReturnType<typeof kyselyFrom>>,\n\t) => Promise<T>,\n): Promise<T> {\n\tconst from =\n\t\toptions.sources?.[target.id]?.file ??\n\t\t(target.of ? options.sources?.[target.of]?.file : undefined) ??\n\t\tjoin(options.root, 'package.json');\n\tconst kysely = await kyselyFrom(from);\n\n\tconst db: Kysely<unknown> = new kysely.Kysely<unknown>({\n\t\tdialect: new kysely.PostgresDialect({\n\t\t\tpool: new pg.Pool(poolConfig(ownerUrl(options, target))),\n\t\t}),\n\t});\n\n\ttry {\n\t\treturn await run(db, kysely);\n\t} finally {\n\t\tawait db.destroy();\n\t}\n}\n\n/**\n * The owner's URL, with no fallback to the runtime one.\n *\n * The runtime role can create nothing, so falling back would only move the\n * failure to the first `CREATE TABLE` and make it read like a permissions\n * bug. The one exception is a database declared `roles: false`, where there is\n * no owner — both URLs are the master credential by design.\n */\nfunction ownerUrl(options: DatabasesOptions, target: MigrationTarget): string {\n\tconst key = provideKey(target.id, 'ownerUrl');\n\tconst owner = options.env[key];\n\tif (owner) return owner;\n\n\tconst database = options.manifest[target.of ?? target.id];\n\tif (database?.kind === 'database' && database.roles === false) {\n\t\tconst url = options.env[provideKey(target.id, 'url')];\n\t\tif (url) return url;\n\t}\n\n\tthrow new NoOwnerCredential(target.id, key);\n}\n\n/**\n * Pool settings for a URL, with `?search_path=` as the startup option Postgres\n * reads. Only a `roles: false` URL carries it — a role pins its own.\n */\nfunction poolConfig(url: string): pg.PoolConfig {\n\tconst parsed = new URL(url);\n\tconst searchPath = parsed.searchParams.get('search_path');\n\tif (!searchPath) return { connectionString: url };\n\n\tparsed.searchParams.delete('search_path');\n\treturn {\n\t\tconnectionString: parsed.toString(),\n\t\toptions: `-c search_path=${searchPath}`,\n\t};\n}\n\n/** Kysely as the project resolves it from `from`. */\nasync function kyselyFrom(from: string) {\n\tconst require = createRequire(from);\n\n\ttry {\n\t\tconst [main, migration] = await Promise.all([\n\t\t\timport(pathToFileURL(require.resolve('kysely')).href),\n\t\t\timport(pathToFileURL(require.resolve('kysely/migration')).href),\n\t\t]);\n\n\t\treturn {\n\t\t\tKysely: main.Kysely as typeof import('kysely').Kysely,\n\t\t\tPostgresDialect:\n\t\t\t\tmain.PostgresDialect as typeof import('kysely').PostgresDialect,\n\t\t\tsql: main.sql as typeof import('kysely').sql,\n\t\t\tMigrator:\n\t\t\t\tmigration.Migrator as typeof import('kysely/migration').Migrator,\n\t\t};\n\t} catch {\n\t\tthrow new KyselyNotFound(from);\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"databases-CzszQV0N.mjs","names":[],"sources":["../src/migrate/provider.ts","../src/migrate/databases.ts"],"sourcesContent":["/**\n * One construct's migrations and seeds, read from its folders.\n *\n * Two kinds of file, told apart by extension rather than by configuration: a\n * script exports `up` (and may export `down`) — or `seed(db, { stage })`, in\n * `seeds/` — and a\n * `.sql` file is run whole. The second is what makes a folder independent of\n * the client the app uses — Better Auth's schema is SQL whatever the\n * application queries with.\n */\n\nimport { readdir, readFile, stat } from 'node:fs/promises';\nimport { extname, join } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { SeedContext } from '@geekmidas/constructs';\nimport type { Kysely, sql } from 'kysely';\nimport type { Migration, MigrationProvider } from 'kysely/migration';\n\nconst SCRIPTS = new Set(['.ts', '.mts', '.js', '.mjs']);\n\n/** Two files in one folder that would be one migration. */\nexport class DuplicateMigration extends Error {\n\tconstructor(\n\t\treadonly folder: string,\n\t\treadonly migration: string,\n\t) {\n\t\tsuper(\n\t\t\t`${folder} has two migrations named '${migration}' — a script and a .sql file ` +\n\t\t\t\t`with the same name. A migration is recorded by name, so only one of ` +\n\t\t\t\t`them could ever run; rename or remove the other.`,\n\t\t);\n\t\tthis.name = 'DuplicateMigration';\n\t}\n}\n\n/** A script in a migrations folder that exports no `up`. */\nexport class MigrationHasNoUp extends Error {\n\tconstructor(readonly file: string) {\n\t\tsuper(\n\t\t\t`${file} is in a migrations folder but exports no \\`up\\` function. ` +\n\t\t\t\t`Export \\`async function up(db: Kysely<unknown>)\\`, or move the file ` +\n\t\t\t\t`out of the folder if it is not a migration.`,\n\t\t);\n\t\tthis.name = 'MigrationHasNoUp';\n\t}\n}\n\n/** A script in a seeds folder that exports no `seed`. */\nexport class SeedHasNoSeed extends Error {\n\tconstructor(readonly file: string) {\n\t\tsuper(\n\t\t\t`${file} is in a seeds folder but exports no \\`seed\\` function. ` +\n\t\t\t\t`Export \\`async function seed(db, { stage })\\`, or move the ` +\n\t\t\t\t`file out of the folder if it is not a seed.`,\n\t\t);\n\t\tthis.name = 'SeedHasNoSeed';\n\t}\n}\n\n/**\n * What a seed is handed beside its database — `@geekmidas/constructs`'\n * `SeedContext`, whose `stage` is the union of the declared stages once\n * `.gkm/stages.d.ts` is generated.\n */\nexport type { SeedContext };\n\n/** One seed: what it is called, and what it does. */\nexport interface Seed {\n\tname: string;\n\trun: (db: Kysely<unknown>, context: SeedContext) => Promise<void>;\n}\n\n/**\n * The migration files in a folder, by name, without reading them.\n *\n * Also what `gkm dev` compares against a construct's history to report what\n * is pending, which is why it is separate from loading them.\n */\nexport async function migrationFiles(\n\tfolder: string,\n): Promise<Map<string, string>> {\n\tconst entries = await readdir(folder, { withFileTypes: true }).catch(\n\t\t(error: NodeJS.ErrnoException) => {\n\t\t\tif (error.code === 'ENOENT') return [];\n\t\t\tthrow error;\n\t\t},\n\t);\n\n\tconst files = new Map<string, string>();\n\tfor (const entry of entries) {\n\t\tif (!entry.isFile() || !isMigration(entry.name)) continue;\n\n\t\tconst name = entry.name.slice(0, -extname(entry.name).length);\n\t\tif (files.has(name)) throw new DuplicateMigration(folder, name);\n\n\t\tfiles.set(name, join(folder, entry.name));\n\t}\n\n\treturn files;\n}\n\n/** A Kysely provider over one construct's folder. */\nexport function folderProvider(\n\tfolder: string,\n\traw: typeof sql,\n): MigrationProvider {\n\treturn {\n\t\tasync getMigrations() {\n\t\t\tconst migrations: Record<string, Migration> = {};\n\n\t\t\tfor (const [name, file] of await migrationFiles(folder)) {\n\t\t\t\tmigrations[name] =\n\t\t\t\t\textname(file) === '.sql'\n\t\t\t\t\t\t? await sqlMigration(file, raw)\n\t\t\t\t\t\t: await scriptMigration(file);\n\t\t\t}\n\n\t\t\treturn migrations;\n\t\t},\n\t};\n}\n\n/**\n * The seeds in a folder, loaded, in name order.\n *\n * Every one runs on every pass — there is no history to consult — so the order\n * is the only thing between two seeds where one reads what another writes.\n * Name them to sort that way.\n */\nexport async function loadSeeds(\n\tfolder: string,\n\traw: typeof sql,\n): Promise<Seed[]> {\n\tconst files = [...(await migrationFiles(folder))].sort(([a], [b]) =>\n\t\ta < b ? -1 : a > b ? 1 : 0,\n\t);\n\n\tconst seeds: Seed[] = [];\n\tfor (const [name, file] of files) {\n\t\tseeds.push({\n\t\t\tname,\n\t\t\trun:\n\t\t\t\textname(file) === '.sql'\n\t\t\t\t\t? (await sqlMigration(file, raw)).up\n\t\t\t\t\t: await scriptSeed(file),\n\t\t});\n\t}\n\n\treturn seeds;\n}\n\n/**\n * A seed script, imported as it is on disk now.\n *\n * Keyed by its modification time: a seed is run again after it is edited, and\n * a process that has imported it once would otherwise run the old one.\n */\nasync function scriptSeed(file: string): Promise<Seed['run']> {\n\tconst url = pathToFileURL(file);\n\turl.searchParams.set('mtime', String((await stat(file)).mtimeMs));\n\tconst module = (await import(url.href)) as { seed?: unknown };\n\tif (typeof module.seed !== 'function') throw new SeedHasNoSeed(file);\n\n\treturn module.seed as Seed['run'];\n}\n\nfunction isMigration(file: string): boolean {\n\tif (file.endsWith('.d.ts')) return false;\n\tif (/\\.(spec|test)\\.[cm]?[jt]s$/.test(file)) return false;\n\n\tconst extension = extname(file);\n\treturn extension === '.sql' || SCRIPTS.has(extension);\n}\n\nasync function scriptMigration(file: string): Promise<Migration> {\n\tconst module = (await import(pathToFileURL(file).href)) as Partial<Migration>;\n\tif (typeof module.up !== 'function') throw new MigrationHasNoUp(file);\n\n\treturn {\n\t\tup: module.up,\n\t\t...(typeof module.down === 'function' ? { down: module.down } : {}),\n\t};\n}\n\n/**\n * A `.sql` file, run whole.\n *\n * One query rather than split on `;`: with no parameters, Postgres takes\n * several statements in one message, and splitting by hand breaks on the first\n * `;` inside a string or a function body. There is no `down` — a generated\n * schema is rolled back by a migration that says how, not by guessing.\n */\nasync function sqlMigration(file: string, raw: typeof sql): Promise<Migration> {\n\tconst statements = await readFile(file, 'utf-8');\n\n\treturn {\n\t\tup: async (db: Kysely<unknown>) => {\n\t\t\tawait raw.raw(statements).execute(db);\n\t\t},\n\t};\n}\n","/**\n * Bring each construct's schema up to its migrations folder.\n *\n * Which constructs, in what order, from which folder, is `migrationTargets`'\n * answer and nobody else's. What is decided here is only how one target is\n * run: as its owner, in its own schema, with a history of its own.\n */\n\nimport { readdir } from 'node:fs/promises';\nimport { createRequire } from 'node:module';\nimport { join } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport {\n\ttype ConstructManifest,\n\tcanonicalId,\n\tDATABASE_FOLDERS,\n\tMIGRATIONS_ROOT,\n\ttype MigrationTarget,\n\tmigrationTargets,\n\tprovideKey,\n} from '@geekmidas/manifest';\nimport type { Kysely } from 'kysely';\nimport type { Migrator } from 'kysely/migration';\nimport pg from 'pg';\nimport type { ConstructSource } from '../reconcile/discover';\nimport { folderProvider, loadSeeds, migrationFiles } from './provider';\n\nexport interface DatabasesOptions {\n\t/** The project root — where `db/` lives. */\n\troot: string;\n\tmanifest: ConstructManifest;\n\t/**\n\t * Where each construct was declared. A construct's migrations run with the\n\t * Kysely its own file resolves, which is the one its migrations import.\n\t */\n\tsources?: Readonly<Record<string, ConstructSource>>;\n\t/** The resolved environment: each construct's owner URL is read from it. */\n\tenv: Readonly<Record<string, string | undefined>>;\n\t/** One construct, by name. All of them when absent. */\n\tonly?: string;\n}\n\nexport interface MigrationRun {\n\ttarget: MigrationTarget;\n\t/** The migrations this run applied, in order. */\n\tapplied: string[];\n}\n\nexport interface SeedOptions extends DatabasesOptions {\n\t/** The stage being seeded, handed to every seed. */\n\tstage: string;\n}\n\nexport interface SeedRun {\n\ttarget: MigrationTarget;\n\t/** The seeds this run ran, in order — every one, every time. */\n\tseeded: string[];\n}\n\n/** The seeds a run would run for one construct, read without connecting. */\nexport interface PlannedSeeds {\n\ttarget: MigrationTarget;\n\t/** In the order they would run. */\n\tseeds: string[];\n}\n\nexport interface PendingMigrations {\n\ttarget: MigrationTarget;\n\tpending: string[];\n}\n\n/** A folder under `db/` that names no construct. */\nexport class UnknownMigrationFolder extends Error {\n\tconstructor(\n\t\treadonly folder: string,\n\t\treadonly known: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`${folder} names no database construct, so its migrations would never ` +\n\t\t\t\t`run. ${known.length > 0 ? `The folders that do: ${known.join(', ')}.` : 'This project declares no database.'} ` +\n\t\t\t\t`Rename the folder after the construct it belongs to.`,\n\t\t);\n\t\tthis.name = 'UnknownMigrationFolder';\n\t}\n}\n\n/** A construct was asked for by a name that takes no migrations. */\nexport class NoSuchMigrationTarget extends Error {\n\tconstructor(\n\t\treadonly construct: string,\n\t\treadonly known: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`'${construct}' is not a database construct. ` +\n\t\t\t\t(known.length > 0\n\t\t\t\t\t? `The ones with migrations: ${known.join(', ')}.`\n\t\t\t\t\t: 'This project declares no database.'),\n\t\t);\n\t\tthis.name = 'NoSuchMigrationTarget';\n\t}\n}\n\n/**\n * A migration left directly in a construct's folder, where nothing reads it.\n *\n * Migrations moved into `migrations/` so seeds could sit beside them. A file\n * still at the old level would otherwise never run, and nothing would say so.\n */\nexport class MigrationsOutsideFolder extends Error {\n\tconstructor(\n\t\treadonly folder: string,\n\t\treadonly files: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`${folder} has ${files.join(', ')} directly inside it, where nothing ` +\n\t\t\t\t`runs them. Migrations live in ${folder}/migrations/ and seeds in ` +\n\t\t\t\t`${folder}/seeds/ — move ${files.length === 1 ? 'it' : 'them'} there.`,\n\t\t);\n\t\tthis.name = 'MigrationsOutsideFolder';\n\t}\n}\n\n/** A folder inside a construct's folder that is neither `migrations` nor `seeds`. */\nexport class UnknownDatabaseFolder extends Error {\n\tconstructor(readonly folder: string) {\n\t\tsuper(\n\t\t\t`${folder} is neither migrations/ nor seeds/, so nothing in it would ` +\n\t\t\t\t`ever run. A construct's folder holds those two; rename or remove it.`,\n\t\t);\n\t\tthis.name = 'UnknownDatabaseFolder';\n\t}\n}\n\n/** A seed failed; its own writes are rolled back, the ones before it stay. */\nexport class SeedFailed extends Error {\n\tconstructor(\n\t\treadonly construct: string,\n\t\treadonly seed: string,\n\t\toverride readonly cause: unknown,\n\t) {\n\t\tsuper(\n\t\t\t`${construct}: seed '${seed}' failed — ` +\n\t\t\t\t`${cause instanceof Error ? cause.message : String(cause)}. ` +\n\t\t\t\t`Its writes were rolled back; fix it and run it again.`,\n\t\t);\n\t\tthis.name = 'SeedFailed';\n\t}\n}\n\n/** A construct with roles has no owner URL to migrate with. */\nexport class NoOwnerCredential extends Error {\n\tconstructor(\n\t\treadonly construct: string,\n\t\treadonly key: string,\n\t) {\n\t\tsuper(\n\t\t\t`${construct} has no ${key} to migrate with. Migrations run as the owner ` +\n\t\t\t\t`role — the runtime one cannot create a table — and nothing ` +\n\t\t\t\t`resolved its URL. Is the stage reconciled?`,\n\t\t);\n\t\tthis.name = 'NoOwnerCredential';\n\t}\n}\n\n/** A migration failed; the ones before it in the run stay applied. */\nexport class MigrationFailed extends Error {\n\tconstructor(\n\t\treadonly construct: string,\n\t\treadonly migration: string | undefined,\n\t\toverride readonly cause: unknown,\n\t) {\n\t\tsuper(\n\t\t\t`${construct}: migration ${migration ? `'${migration}' ` : ''}failed — ` +\n\t\t\t\t`${cause instanceof Error ? cause.message : String(cause)}`,\n\t\t);\n\t\tthis.name = 'MigrationFailed';\n\t}\n}\n\n/** The project's Kysely could not be found from where a database is declared. */\nexport class KyselyNotFound extends Error {\n\tconstructor(readonly from: string) {\n\t\tsuper(\n\t\t\t`Could not resolve 'kysely' from ${from}. Migrations run with the ` +\n\t\t\t\t`project's own Kysely — install it where the database is declared.`,\n\t\t);\n\t\tthis.name = 'KyselyNotFound';\n\t}\n}\n\n/** Apply every pending migration, construct by construct, parents first. */\nexport async function migrateDatabases(\n\toptions: DatabasesOptions,\n): Promise<MigrationRun[]> {\n\tconst runs: MigrationRun[] = [];\n\n\tfor (const target of await targetsFor(options)) {\n\t\t// No folder, or an empty one, is nothing to do — and no reason to\n\t\t// connect, which a construct nobody has written a migration for yet\n\t\t// should not need.\n\t\tconst files = await migrationFiles(join(options.root, target.migrations));\n\t\tif (files.size === 0) {\n\t\t\truns.push({ target, applied: [] });\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst applied = await withMigrator(options, target, async (migrator) => {\n\t\t\tconst { error, results = [] } = await migrator.migrateToLatest();\n\t\t\tif (error) {\n\t\t\t\tconst failed = results.find((result) => result.status === 'Error');\n\t\t\t\tthrow new MigrationFailed(target.id, failed?.migrationName, error);\n\t\t\t}\n\n\t\t\treturn results\n\t\t\t\t.filter((result) => result.status === 'Success')\n\t\t\t\t.map((result) => result.migrationName);\n\t\t});\n\n\t\truns.push({ target, applied });\n\t}\n\n\treturn runs;\n}\n\n/**\n * Run every construct's seeds, parents first — each one, every time, on every\n * stage, production included. Each is handed the stage, so a seed decides\n * what belongs where.\n *\n * Seeds are reference data kept up to date by being run again, so there is no\n * history: a seed is an upsert, and a changed one is applied by the next run.\n * Each runs in its own transaction, as the construct's owner, so a failure\n * leaves nothing half-written. Assumes the schema is migrated —\n * {@link migrateAndSeed} is what makes that true.\n */\nexport async function seedDatabases(options: SeedOptions): Promise<SeedRun[]> {\n\tconst runs: SeedRun[] = [];\n\n\tfor (const target of await targetsFor(options)) {\n\t\tconst folder = join(options.root, target.seeds);\n\t\tif ((await migrationFiles(folder)).size === 0) {\n\t\t\truns.push({ target, seeded: [] });\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst seeded = await withOwner(options, target, async (db, kysely) => {\n\t\t\tconst names: string[] = [];\n\t\t\tfor (const seed of await loadSeeds(folder, kysely.sql)) {\n\t\t\t\ttry {\n\t\t\t\t\tawait db\n\t\t\t\t\t\t.transaction()\n\t\t\t\t\t\t.execute((trx) => seed.run(trx, { stage: options.stage }));\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthrow new SeedFailed(target.id, seed.name, error);\n\t\t\t\t}\n\t\t\t\tnames.push(seed.name);\n\t\t\t}\n\t\t\treturn names;\n\t\t});\n\n\t\truns.push({ target, seeded });\n\t}\n\n\treturn runs;\n}\n\n/**\n * The seeds {@link seedDatabases} would run, construct by construct, parents\n * first, in the order it would run them — read from the folders alone, with\n * no connection. What a dry run lists. Constructs with none are left out.\n */\nexport async function plannedSeeds(\n\toptions: Pick<DatabasesOptions, 'root' | 'manifest' | 'only'>,\n): Promise<PlannedSeeds[]> {\n\tconst planned: PlannedSeeds[] = [];\n\tfor (const target of await targetsFor(options)) {\n\t\tconst files = await migrationFiles(join(options.root, target.seeds));\n\t\tif (files.size === 0) continue;\n\t\tplanned.push({ target, seeds: [...files.keys()].sort() });\n\t}\n\treturn planned;\n}\n\n/**\n * Migrate, then seed: a seed only ever runs against the schema it was written\n * for.\n *\n * With `only`, the constructs it lives in are migrated too — a tenant's seed\n * cannot run before the database that owns its schema exists — and only it is\n * seeded.\n */\nexport async function migrateAndSeed(options: SeedOptions): Promise<{\n\tmigrations: MigrationRun[];\n\tseeds: SeedRun[];\n}> {\n\tconst migrations: MigrationRun[] = [];\n\tfor (const target of await withParents(options)) {\n\t\tmigrations.push(\n\t\t\t...(await migrateDatabases({ ...options, only: target.id })),\n\t\t);\n\t}\n\n\treturn { migrations, seeds: await seedDatabases(options) };\n}\n\n/** The targets `only` names, after the ones it lives in; all without it. */\nasync function withParents(\n\toptions: DatabasesOptions,\n): Promise<MigrationTarget[]> {\n\tconst chosen = await targetsFor(options);\n\tif (!options.only) return chosen;\n\n\tconst all = migrationTargets(options.manifest);\n\tconst ids = new Set<string>();\n\tfor (const target of chosen) {\n\t\tlet current: MigrationTarget | undefined = target;\n\t\twhile (current) {\n\t\t\tids.add(current.id);\n\t\t\tconst parent: string | undefined = current.of;\n\t\t\tcurrent = parent ? all.find((t) => t.id === parent) : undefined;\n\t\t}\n\t}\n\n\treturn all.filter((target) => ids.has(target.id));\n}\n\n/**\n * What each construct's folder has that its history does not — without\n * applying any of it. What `gkm dev` reports.\n */\nexport async function pendingMigrations(\n\toptions: DatabasesOptions,\n): Promise<PendingMigrations[]> {\n\tconst found: PendingMigrations[] = [];\n\n\tfor (const target of await targetsFor(options)) {\n\t\tconst files = await migrationFiles(join(options.root, target.migrations));\n\t\tif (files.size === 0) continue;\n\n\t\tconst pending = await withMigrator(options, target, async (migrator) =>\n\t\t\t(await migrator.getMigrations())\n\t\t\t\t.filter((migration) => migration.executedAt === undefined)\n\t\t\t\t.map((migration) => migration.name),\n\t\t);\n\n\t\tif (pending.length > 0) found.push({ target, pending });\n\t}\n\n\treturn found;\n}\n\n/**\n * The targets this run covers, with every folder under `db/` accounted for.\n *\n * A folder that names no construct is an error rather than something to skip:\n * a typo in its name is otherwise a migration that silently never runs.\n */\nasync function targetsFor(\n\toptions: Pick<DatabasesOptions, 'root' | 'manifest' | 'only'>,\n): Promise<MigrationTarget[]> {\n\tconst targets = migrationTargets(options.manifest);\n\tconst folders = targets.map((target) => target.folder);\n\n\tfor (const folder of await foldersIn(join(options.root, MIGRATIONS_ROOT))) {\n\t\tconst relative = `${MIGRATIONS_ROOT}/${folder}`;\n\t\tif (!folders.includes(relative)) {\n\t\t\tthrow new UnknownMigrationFolder(relative, folders);\n\t\t}\n\t\tawait checkLayout(options.root, relative);\n\t}\n\n\tif (!options.only) return targets;\n\n\tconst id = canonicalId(options.only);\n\tconst chosen = targets.filter((target) => target.id === id);\n\tif (chosen.length === 0) {\n\t\tthrow new NoSuchMigrationTarget(\n\t\t\toptions.only,\n\t\t\ttargets.map((target) => target.id),\n\t\t);\n\t}\n\n\treturn chosen;\n}\n\n/**\n * A construct's folder holds `migrations/` and `seeds/`, and nothing else —\n * anything beside them is a file or folder nothing would ever run.\n */\nasync function checkLayout(root: string, folder: string): Promise<void> {\n\tconst entries = await readdir(join(root, folder), { withFileTypes: true });\n\n\tconst loose = entries\n\t\t.filter((entry) => entry.isFile() && !entry.name.startsWith('.'))\n\t\t.map((entry) => entry.name);\n\tif (loose.length > 0) throw new MigrationsOutsideFolder(folder, loose);\n\n\tfor (const entry of entries) {\n\t\tif (\n\t\t\tentry.isDirectory() &&\n\t\t\t!(DATABASE_FOLDERS as readonly string[]).includes(entry.name)\n\t\t) {\n\t\t\tthrow new UnknownDatabaseFolder(`${folder}/${entry.name}`);\n\t\t}\n\t}\n}\n\nasync function foldersIn(dir: string): Promise<string[]> {\n\tconst entries = await readdir(dir, { withFileTypes: true }).catch(\n\t\t(error: NodeJS.ErrnoException) => {\n\t\t\tif (error.code === 'ENOENT') return [];\n\t\t\tthrow error;\n\t\t},\n\t);\n\n\treturn entries\n\t\t.filter((entry) => entry.isDirectory())\n\t\t.map((entry) => entry.name);\n}\n\n/**\n * A migrator for one target, connected as its owner.\n *\n * No `migrationTableSchema`: the owner role's `search_path` is pinned to the\n * construct's schema, so the history lands there — `app.kysely_migration` for\n * the database, `auth_database.kysely_migration` for its tenant — and two\n * constructs in one Postgres never share one.\n *\n * Unordered migrations are allowed. Files are timestamped, so a branch merged\n * after another's migration ran carries an older one; refusing it would make\n * every such merge a manual repair.\n */\nasync function withMigrator<T>(\n\toptions: DatabasesOptions,\n\ttarget: MigrationTarget,\n\trun: (migrator: Migrator) => Promise<T>,\n): Promise<T> {\n\treturn withOwner(options, target, (db, { Migrator, sql }) =>\n\t\trun(\n\t\t\tnew Migrator({\n\t\t\t\tdb,\n\t\t\t\tprovider: folderProvider(join(options.root, target.migrations), sql),\n\t\t\t\tallowUnorderedMigrations: true,\n\t\t\t}),\n\t\t),\n\t);\n}\n\n/**\n * One target's database, connected as its owner and closed afterwards — what\n * migrations and seeds both run as.\n */\nasync function withOwner<T>(\n\toptions: DatabasesOptions,\n\ttarget: MigrationTarget,\n\trun: (\n\t\tdb: Kysely<unknown>,\n\t\tkysely: Awaited<ReturnType<typeof kyselyFrom>>,\n\t) => Promise<T>,\n): Promise<T> {\n\tconst from =\n\t\toptions.sources?.[target.id]?.file ??\n\t\t(target.of ? options.sources?.[target.of]?.file : undefined) ??\n\t\tjoin(options.root, 'package.json');\n\tconst kysely = await kyselyFrom(from);\n\n\tconst db: Kysely<unknown> = new kysely.Kysely<unknown>({\n\t\tdialect: new kysely.PostgresDialect({\n\t\t\tpool: new pg.Pool(poolConfig(ownerUrl(options, target))),\n\t\t}),\n\t});\n\n\ttry {\n\t\treturn await run(db, kysely);\n\t} finally {\n\t\tawait db.destroy();\n\t}\n}\n\n/**\n * The owner's URL, with no fallback to the runtime one.\n *\n * The runtime role can create nothing, so falling back would only move the\n * failure to the first `CREATE TABLE` and make it read like a permissions\n * bug. The one exception is a database declared `roles: false`, where there is\n * no owner — both URLs are the master credential by design.\n */\nfunction ownerUrl(options: DatabasesOptions, target: MigrationTarget): string {\n\tconst key = provideKey(target.id, 'ownerUrl');\n\tconst owner = options.env[key];\n\tif (owner) return owner;\n\n\tconst database = options.manifest[target.of ?? target.id];\n\tif (database?.kind === 'database' && database.roles === false) {\n\t\tconst url = options.env[provideKey(target.id, 'url')];\n\t\tif (url) return url;\n\t}\n\n\tthrow new NoOwnerCredential(target.id, key);\n}\n\n/**\n * Pool settings for a URL, with `?search_path=` as the startup option Postgres\n * reads. Only a `roles: false` URL carries it — a role pins its own.\n */\nfunction poolConfig(url: string): pg.PoolConfig {\n\tconst parsed = new URL(url);\n\tconst searchPath = parsed.searchParams.get('search_path');\n\tif (!searchPath) return { connectionString: url };\n\n\tparsed.searchParams.delete('search_path');\n\treturn {\n\t\tconnectionString: parsed.toString(),\n\t\toptions: `-c search_path=${searchPath}`,\n\t};\n}\n\n/** Kysely as the project resolves it from `from`. */\nasync function kyselyFrom(from: string) {\n\tconst require = createRequire(from);\n\n\ttry {\n\t\tconst [main, migration] = await Promise.all([\n\t\t\timport(pathToFileURL(require.resolve('kysely')).href),\n\t\t\timport(pathToFileURL(require.resolve('kysely/migration')).href),\n\t\t]);\n\n\t\treturn {\n\t\t\tKysely: main.Kysely as typeof import('kysely').Kysely,\n\t\t\tPostgresDialect:\n\t\t\t\tmain.PostgresDialect as typeof import('kysely').PostgresDialect,\n\t\t\tsql: main.sql as typeof import('kysely').sql,\n\t\t\tMigrator:\n\t\t\t\tmigration.Migrator as typeof import('kysely/migration').Migrator,\n\t\t};\n\t} catch {\n\t\tthrow new KyselyNotFound(from);\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"define-DR8xGYfL.mjs","names":[],"sources":["../src/target/builtins.ts","../src/target/define.ts"],"sourcesContent":["/**\n * The target names the CLI knows without being told, and how a name is\n * looked up.\n *\n * Plain data, so the config schema and `providerOf` — which `gkm dev` and\n * `gkm build` call synchronously on every start — can ask what a name means\n * without importing a deploy engine.\n */\n\nimport type { DeployRuntime, DeployTargetEntry } from './types';\n\n/**\n * - `available`: `gkm deploy` deploys through it.\n * - `external`: a valid `deploy.default` that builds and runs locally, but is\n * deployed by its own tool — `gkm deploy` refuses it and says which.\n * - `planned`: reserved, and refused everywhere.\n */\nexport type BuiltinStatus = 'available' | 'external' | 'planned';\n\nexport interface BuiltinTarget {\n\treadonly runtime: DeployRuntime;\n\treadonly status: BuiltinStatus;\n\t/** What to do instead, for a target `gkm deploy` cannot deploy through yet. */\n\treadonly instead?: (stage: string) => string;\n}\n\nexport const BUILTIN_TARGETS = {\n\tdokploy: { runtime: 'server', status: 'available' },\n\t// One Docker Compose stack behind Caddy, on the machine that runs it.\n\tcompose: { runtime: 'server', status: 'available' },\n\tsst: { runtime: 'aws', status: 'available' },\n\t// Neither runs containers the project controls, so both take the managed\n\t// defaults when they arrive.\n\tvercel: { runtime: 'aws', status: 'planned' },\n\tcloudflare: { runtime: 'aws', status: 'planned' },\n} as const satisfies Record<string, BuiltinTarget>;\n\nexport type BuiltinTargetName = keyof typeof BUILTIN_TARGETS;\n\n/** The built-in target called `name`, if one is. */\nexport function builtinTarget(name: string): BuiltinTarget | undefined {\n\treturn Object.hasOwn(BUILTIN_TARGETS, name)\n\t\t? BUILTIN_TARGETS[name as BuiltinTargetName]\n\t\t: undefined;\n}\n\n/** Built-in names a config may use. */\nexport function configurableBuiltins(): string[] {\n\treturn Object.entries(BUILTIN_TARGETS)\n\t\t.filter(([, target]) => target.status !== 'planned')\n\t\t.map(([name]) => name);\n}\n\n/** The `deploy.targets` entry called `name`, if the config has one. */\nexport function configuredTarget(\n\tdeploy: { targets?: Record<string, DeployTargetEntry> } | undefined,\n\tname: string,\n): DeployTargetEntry | undefined {\n\tconst targets = deploy?.targets;\n\treturn targets && Object.hasOwn(targets, name) ? targets[name] : undefined;\n}\n\n/** A target name that is neither built in nor in `deploy.targets`. */\nexport class UnknownDeployTarget extends Error {\n\tconstructor(\n\t\treadonly target: string,\n\t\t/** The names that would have resolved. */\n\t\treadonly known: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`Unknown deploy target \"${target}\". Use one of ${known.join(', ')}, or name the package that provides it in gkm.config.ts: deploy: { targets: { ${JSON.stringify(target)}: '<package>' } }.`,\n\t\t);\n\t\tthis.name = 'UnknownDeployTarget';\n\t}\n}\n\n/** A built-in target `gkm deploy` cannot deploy through yet. */\nexport class DeployTargetNotYetSupported extends Error {\n\tconstructor(\n\t\treadonly target: string,\n\t\t/** What to do instead, when there is something. */\n\t\treadonly instead?: string,\n\t) {\n\t\tsuper(\n\t\t\tinstead\n\t\t\t\t? `gkm deploy cannot deploy through \"${target}\" yet: ${instead}.`\n\t\t\t\t: `Deploy target \"${target}\" is not supported yet. Deploy with dokploy, or name a target package in deploy.targets.`,\n\t\t);\n\t\tthis.name = 'DeployTargetNotYetSupported';\n\t}\n}\n","import type { AnyDeployTarget, DeployTarget } from './types';\n\n/**\n * A deploy target, typed from what it is written with: the options from its\n * `options` schema, the run state from what `validate` returns. Nothing\n * happens at runtime — it returns the target as given.\n *\n * ```ts\n * export default defineTarget({\n * name: 'fly',\n * runtime: 'server',\n * capabilities: { rollback: true, migrations: 'target', images: true },\n * options: z.object({ org: z.string() }),\n * async validate(ctx) {\n * return { org: ctx.options.org }; // ctx.options: { org: string }\n * },\n * async release(ctx, run) { … }, // run: { org: string }\n * …\n * });\n * ```\n */\nexport function defineTarget<Options = undefined, Run = void>(\n\ttarget: DeployTarget<Options, Run>,\n): DeployTarget<Options, Run> {\n\treturn target;\n}\n\n/** Whether `value` has the shape of a target. */\nexport function isDeployTarget(value: unknown): value is AnyDeployTarget {\n\tif (!value || typeof value !== 'object') return false;\n\tconst candidate = value as Record<string, unknown>;\n\treturn (\n\t\ttypeof candidate.name === 'string' &&\n\t\t(candidate.runtime === 'server' || candidate.runtime === 'aws') &&\n\t\ttypeof candidate.capabilities === 'object' &&\n\t\tcandidate.capabilities !== null &&\n\t\t['validate', 'plan', 'release', 'result'].every(\n\t\t\t(phase) => typeof candidate[phase] === 'function',\n\t\t)\n\t);\n}\n"],"mappings":";AA0BA,MAAa,kBAAkB;CAC9B,SAAS;EAAE,SAAS;EAAU,QAAQ;CAAY;CAElD,SAAS;EAAE,SAAS;EAAU,QAAQ;CAAY;CAClD,KAAK;EAAE,SAAS;EAAO,QAAQ;CAAY;CAG3C,QAAQ;EAAE,SAAS;EAAO,QAAQ;CAAU;CAC5C,YAAY;EAAE,SAAS;EAAO,QAAQ;CAAU;AACjD;;AAKA,SAAgB,cAAc,MAAyC;CACtE,OAAO,OAAO,OAAO,iBAAiB,IAAI,IACvC,gBAAgB,QAChB,KAAA;AACJ;;AAGA,SAAgB,uBAAiC;CAChD,OAAO,OAAO,QAAQ,eAAe,CAAC,CACpC,QAAQ,GAAG,YAAY,OAAO,WAAW,SAAS,CAAC,CACnD,KAAK,CAAC,UAAU,IAAI;AACvB;;AAGA,SAAgB,iBACf,QACA,MACgC;CAChC,MAAM,UAAU,QAAQ;CACxB,OAAO,WAAW,OAAO,OAAO,SAAS,IAAI,IAAI,QAAQ,QAAQ,KAAA;AAClE;;AAGA,IAAa,sBAAb,cAAyC,MAAM;CAEpC;CAEA;CAHV,YACC,QAEA,OACC;EACD,MACC,0BAA0B,OAAO,gBAAgB,MAAM,KAAK,IAAI,EAAE,gFAAgF,KAAK,UAAU,MAAM,EAAE,mBAC1K;EANS,KAAA,SAAA;EAEA,KAAA,QAAA;EAKT,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,8BAAb,cAAiD,MAAM;CAE5C;CAEA;CAHV,YACC,QAEA,SACC;EACD,MACC,UACG,qCAAqC,OAAO,SAAS,QAAQ,KAC7D,kBAAkB,OAAO,yFAC7B;EARS,KAAA,SAAA;EAEA,KAAA,UAAA;EAOT,KAAK,OAAO;CACb;AACD;;;;;;;;;;;;;;;;;;;;;;ACrEA,SAAgB,aACf,QAC6B;CAC7B,OAAO;AACR;;AAGA,SAAgB,eAAe,OAA0C;CACxE,IAAI,CAAC,SAAS,OAAO,UAAU,UAAU,OAAO;CAChD,MAAM,YAAY;CAClB,OACC,OAAO,UAAU,SAAS,aACzB,UAAU,YAAY,YAAY,UAAU,YAAY,UACzD,OAAO,UAAU,iBAAiB,YAClC,UAAU,iBAAiB,QAC3B;EAAC;EAAY;EAAQ;EAAW;CAAQ,CAAC,CAAC,OACxC,UAAU,OAAO,UAAU,WAAW,UACxC;AAEF"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"define-DzRz8FgH.cjs","names":[],"sources":["../src/target/builtins.ts","../src/target/define.ts"],"sourcesContent":["/**\n * The target names the CLI knows without being told, and how a name is\n * looked up.\n *\n * Plain data, so the config schema and `providerOf` — which `gkm dev` and\n * `gkm build` call synchronously on every start — can ask what a name means\n * without importing a deploy engine.\n */\n\nimport type { DeployRuntime, DeployTargetEntry } from './types';\n\n/**\n * - `available`: `gkm deploy` deploys through it.\n * - `external`: a valid `deploy.default` that builds and runs locally, but is\n * deployed by its own tool — `gkm deploy` refuses it and says which.\n * - `planned`: reserved, and refused everywhere.\n */\nexport type BuiltinStatus = 'available' | 'external' | 'planned';\n\nexport interface BuiltinTarget {\n\treadonly runtime: DeployRuntime;\n\treadonly status: BuiltinStatus;\n\t/** What to do instead, for a target `gkm deploy` cannot deploy through yet. */\n\treadonly instead?: (stage: string) => string;\n}\n\nexport const BUILTIN_TARGETS = {\n\tdokploy: { runtime: 'server', status: 'available' },\n\t// One Docker Compose stack behind Caddy, on the machine that runs it.\n\tcompose: { runtime: 'server', status: 'available' },\n\tsst: { runtime: 'aws', status: 'available' },\n\t// Neither runs containers the project controls, so both take the managed\n\t// defaults when they arrive.\n\tvercel: { runtime: 'aws', status: 'planned' },\n\tcloudflare: { runtime: 'aws', status: 'planned' },\n} as const satisfies Record<string, BuiltinTarget>;\n\nexport type BuiltinTargetName = keyof typeof BUILTIN_TARGETS;\n\n/** The built-in target called `name`, if one is. */\nexport function builtinTarget(name: string): BuiltinTarget | undefined {\n\treturn Object.hasOwn(BUILTIN_TARGETS, name)\n\t\t? BUILTIN_TARGETS[name as BuiltinTargetName]\n\t\t: undefined;\n}\n\n/** Built-in names a config may use. */\nexport function configurableBuiltins(): string[] {\n\treturn Object.entries(BUILTIN_TARGETS)\n\t\t.filter(([, target]) => target.status !== 'planned')\n\t\t.map(([name]) => name);\n}\n\n/** The `deploy.targets` entry called `name`, if the config has one. */\nexport function configuredTarget(\n\tdeploy: { targets?: Record<string, DeployTargetEntry> } | undefined,\n\tname: string,\n): DeployTargetEntry | undefined {\n\tconst targets = deploy?.targets;\n\treturn targets && Object.hasOwn(targets, name) ? targets[name] : undefined;\n}\n\n/** A target name that is neither built in nor in `deploy.targets`. */\nexport class UnknownDeployTarget extends Error {\n\tconstructor(\n\t\treadonly target: string,\n\t\t/** The names that would have resolved. */\n\t\treadonly known: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`Unknown deploy target \"${target}\". Use one of ${known.join(', ')}, or name the package that provides it in gkm.config.ts: deploy: { targets: { ${JSON.stringify(target)}: '<package>' } }.`,\n\t\t);\n\t\tthis.name = 'UnknownDeployTarget';\n\t}\n}\n\n/** A built-in target `gkm deploy` cannot deploy through yet. */\nexport class DeployTargetNotYetSupported extends Error {\n\tconstructor(\n\t\treadonly target: string,\n\t\t/** What to do instead, when there is something. */\n\t\treadonly instead?: string,\n\t) {\n\t\tsuper(\n\t\t\tinstead\n\t\t\t\t? `gkm deploy cannot deploy through \"${target}\" yet: ${instead}.`\n\t\t\t\t: `Deploy target \"${target}\" is not supported yet. Deploy with dokploy, or name a target package in deploy.targets.`,\n\t\t);\n\t\tthis.name = 'DeployTargetNotYetSupported';\n\t}\n}\n","import type { AnyDeployTarget, DeployTarget } from './types';\n\n/**\n * A deploy target, typed from what it is written with: the options from its\n * `options` schema, the run state from what `validate` returns. Nothing\n * happens at runtime — it returns the target as given.\n *\n * ```ts\n * export default defineTarget({\n * name: 'fly',\n * runtime: 'server',\n * capabilities: { rollback: true, migrations: 'target', images: true },\n * options: z.object({ org: z.string() }),\n * async validate(ctx) {\n * return { org: ctx.options.org }; // ctx.options: { org: string }\n * },\n * async release(ctx, run) { … }, // run: { org: string }\n * …\n * });\n * ```\n */\nexport function defineTarget<Options = undefined, Run = void>(\n\ttarget: DeployTarget<Options, Run>,\n): DeployTarget<Options, Run> {\n\treturn target;\n}\n\n/** Whether `value` has the shape of a target. */\nexport function isDeployTarget(value: unknown): value is AnyDeployTarget {\n\tif (!value || typeof value !== 'object') return false;\n\tconst candidate = value as Record<string, unknown>;\n\treturn (\n\t\ttypeof candidate.name === 'string' &&\n\t\t(candidate.runtime === 'server' || candidate.runtime === 'aws') &&\n\t\ttypeof candidate.capabilities === 'object' &&\n\t\tcandidate.capabilities !== null &&\n\t\t['validate', 'plan', 'release', 'result'].every(\n\t\t\t(phase) => typeof candidate[phase] === 'function',\n\t\t)\n\t);\n}\n"],"mappings":";AA0BA,MAAa,kBAAkB;CAC9B,SAAS;EAAE,SAAS;EAAU,QAAQ;CAAY;CAElD,SAAS;EAAE,SAAS;EAAU,QAAQ;CAAY;CAClD,KAAK;EAAE,SAAS;EAAO,QAAQ;CAAY;CAG3C,QAAQ;EAAE,SAAS;EAAO,QAAQ;CAAU;CAC5C,YAAY;EAAE,SAAS;EAAO,QAAQ;CAAU;AACjD;;AAKA,SAAgB,cAAc,MAAyC;CACtE,OAAO,OAAO,OAAO,iBAAiB,IAAI,IACvC,gBAAgB,QAChB,KAAA;AACJ;;AAGA,SAAgB,uBAAiC;CAChD,OAAO,OAAO,QAAQ,eAAe,CAAC,CACpC,QAAQ,GAAG,YAAY,OAAO,WAAW,SAAS,CAAC,CACnD,KAAK,CAAC,UAAU,IAAI;AACvB;;AAGA,SAAgB,iBACf,QACA,MACgC;CAChC,MAAM,UAAU,QAAQ;CACxB,OAAO,WAAW,OAAO,OAAO,SAAS,IAAI,IAAI,QAAQ,QAAQ,KAAA;AAClE;;AAGA,IAAa,sBAAb,cAAyC,MAAM;CAEpC;CAEA;CAHV,YACC,QAEA,OACC;EACD,MACC,0BAA0B,OAAO,gBAAgB,MAAM,KAAK,IAAI,EAAE,gFAAgF,KAAK,UAAU,MAAM,EAAE,mBAC1K;EANS,KAAA,SAAA;EAEA,KAAA,QAAA;EAKT,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,8BAAb,cAAiD,MAAM;CAE5C;CAEA;CAHV,YACC,QAEA,SACC;EACD,MACC,UACG,qCAAqC,OAAO,SAAS,QAAQ,KAC7D,kBAAkB,OAAO,yFAC7B;EARS,KAAA,SAAA;EAEA,KAAA,UAAA;EAOT,KAAK,OAAO;CACb;AACD;;;;;;;;;;;;;;;;;;;;;;ACrEA,SAAgB,aACf,QAC6B;CAC7B,OAAO;AACR;;AAGA,SAAgB,eAAe,OAA0C;CACxE,IAAI,CAAC,SAAS,OAAO,UAAU,UAAU,OAAO;CAChD,MAAM,YAAY;CAClB,OACC,OAAO,UAAU,SAAS,aACzB,UAAU,YAAY,YAAY,UAAU,YAAY,UACzD,OAAO,UAAU,iBAAiB,YAClC,UAAU,iBAAiB,QAC3B;EAAC;EAAY;EAAQ;EAAW;CAAQ,CAAC,CAAC,OACxC,UAAU,OAAO,UAAU,WAAW,UACxC;AAEF"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"deploy-CuHasEA4.cjs","names":["secretsStoreFor","builtinTarget","eventError","resolveTarget","parseTargetOptions","DevServicesNeedServerTarget","discover","constructGlobs","usesTelemetry","derivedApps","deployIdentity","getAppBuildOrder","createStateStore","applicationName","storedCredentials","gkmHome","withOutput","LocalSandbox","findWorkspaceRoot","resolve","withSandbox","eventError","loadWorkspaceConfig"],"sources":["../src/target/secrets.ts","../src/deploy/orchestrate.ts","../src/deploy/deploy.ts"],"sourcesContent":["/**\n * A stage's secrets as a target sees them: read and written through the\n * stage's own store, and masked in everything the run prints once read.\n */\n\nimport { secretsStoreFor } from '../secrets/store';\nimport type { StageSecrets } from '../secrets/types';\nimport type { NormalizedWorkspace } from '../workspace/types';\nimport type { DeploySecrets } from './types';\n\n/**\n * Shorter values are left alone: what a progress line legitimately prints —\n * a port, a stage, an app name — would otherwise come out as `***` wherever\n * a secret happened to share it.\n */\nconst SHORTEST_MASKED = 6;\n\n/** The values a run must never print, and the rewrite that keeps it so. */\nexport class Redactor {\n\tprivate readonly values = new Set<string>();\n\n\tadd(value: string | undefined): void {\n\t\tif (value && value.length >= SHORTEST_MASKED) this.values.add(value);\n\t}\n\n\t/** `text` with every masked value replaced by `***`, longest first. */\n\tredact(text: string): string {\n\t\tif (this.values.size === 0) return text;\n\t\tlet out = text;\n\t\tconst longestFirst = [...this.values].sort((a, b) => b.length - a.length);\n\t\tfor (const value of longestFirst) out = out.replaceAll(value, '***');\n\t\treturn out;\n\t}\n}\n\n/** Every value in `secrets` that is a secret rather than a label. */\nfunction secretValues(secrets: StageSecrets): string[] {\n\treturn [\n\t\tsecrets.seed,\n\t\t...Object.values(secrets.custom ?? {}),\n\t\t...Object.values(secrets.urls ?? {}),\n\t\t...Object.values(secrets.services ?? {}).map(\n\t\t\t(service) => service?.password,\n\t\t),\n\t].filter((value): value is string => typeof value === 'string');\n}\n\n/**\n * The stage's secrets through the store `secrets.store` names for it. Opening\n * one builds a client and makes no request, so a target that never reads them\n * costs nothing.\n */\nexport async function deploySecrets(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n\tredactor: Redactor,\n\toptions: { home?: string } = {},\n): Promise<DeploySecrets> {\n\tconst store = await secretsStoreFor(workspace, stage, options);\n\tconst mask = (secrets: StageSecrets) => {\n\t\tfor (const value of secretValues(secrets)) redactor.add(value);\n\t};\n\n\treturn {\n\t\tstore: store.name,\n\t\tasync read() {\n\t\t\tconst secrets = await store.read(stage);\n\t\t\tif (secrets) mask(secrets);\n\t\t\treturn secrets;\n\t\t},\n\t\tasync write(secrets) {\n\t\t\tmask(secrets);\n\t\t\tawait store.write(stage, secrets);\n\t\t},\n\t\tmask: (value) => redactor.add(value),\n\t};\n}\n","/**\n * The run around a target: what every deploy does whatever it deploys\n * through.\n *\n * It loads the workspace, resolves the target, works out the apps and the\n * identity, takes the stage's lock, and calls the target's phases in order,\n * reporting each as it starts, finishes or fails. Everything that is about\n * *where* the stage goes is the target's.\n */\n\nimport { discover } from '../reconcile/discover.js';\nimport { constructGlobs } from '../reconcile/workspace.js';\nimport { builtinTarget } from '../target/builtins';\nimport {\n\tparseTargetOptions,\n\ttype ResolvedTarget,\n\tresolveTarget,\n} from '../target/resolve';\nimport { deploySecrets, Redactor } from '../target/secrets';\nimport type {\n\tAnyDeployTarget,\n\tDeployPhaseContext,\n\tDeployTargetLogger,\n} from '../target/types';\nimport { resolveStageTelemetry } from '../telemetry/config';\nimport { usesTelemetry } from '../telemetry/edges';\nimport { derivedApps } from '../workspace/derive.js';\nimport { getAppBuildOrder } from '../workspace/index.js';\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport type { CredentialProvider } from './credentials';\nimport { DevServicesNeedServerTarget } from './devServices';\nimport { type DeployEvent, type DeployPhase, eventError } from './events';\nimport { applicationName, deployIdentity } from './identity.js';\nimport { createStateStore } from './StateStore.js';\nimport type { DeployResult } from './types';\n\n/** What a run deploys, once the workspace is loaded. */\nexport interface DeployRequest {\n\t/** Deployment stage (e.g., 'production', 'staging') */\n\tstage: string;\n\t/** Image tag (default: stage-timestamp) */\n\ttag?: string;\n\t/** Specific apps to deploy (default: all) */\n\tapps?: string[];\n\t/** The target to deploy through. Defaults to `deploy.default`. */\n\ttarget?: string;\n\t/** On a failed release, roll back every app rather than the failed ones. */\n\tatomic?: boolean;\n\t/**\n\t * `--allow-dev-services`: a deployed stage runs the dev service for every\n\t * bucket and mail it does not account for. Server targets only.\n\t */\n\tallowDevServices?: boolean;\n\t/** `--skip-dns-check`: a server target checks no host's DNS. */\n\tskipDnsCheck?: boolean;\n}\n\n/**\n * What a run is handed by whoever started it: where its events go, where it\n * gets credentials, and whether it may change anything. Nothing in a run\n * reads a terminal or exits the process — that is its caller's business.\n */\nexport interface DeployContext {\n\temit: (event: DeployEvent) => void;\n\tcredentials: CredentialProvider;\n\tsignal?: AbortSignal;\n\t/** Look everything up, create, build and push nothing. */\n\tdryRun: boolean;\n\t/**\n\t * Validate and build — the target's images built, and pushed where it\n\t * pushes them — and nothing else: no lock, nothing provisioned, released,\n\t * verified or recorded. What a CI job runs before a server pulls.\n\t */\n\tbuildOnly?: boolean;\n\t/** The CLI's home, for the stage's keys. Defaults to `GKM_HOME`. */\n\thome?: string;\n\t/** Where spawned processes' output goes. Defaults to the terminal. */\n\tchildOutput?: 'inherit' | 'stderr' | 'ignore';\n\t/** The host's own targets, which win over every other by the same name. */\n\ttargets?: Record<string, AnyDeployTarget>;\n\t/**\n\t * What the run masks in what it prints. `deploy()` passes the one its\n\t * progress lines go through; a run without one masks only its events.\n\t */\n\tredactor?: Redactor;\n}\n\n/** Apps were asked for by name that the workspace does not have. */\nexport class UnknownDeployApps extends Error {\n\tconstructor(\n\t\treadonly apps: readonly string[],\n\t\treadonly available: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`Unknown apps: ${apps.join(', ')}\\n` +\n\t\t\t\t`Available apps: ${available.join(', ')}`,\n\t\t);\n\t\tthis.name = 'UnknownDeployApps';\n\t}\n}\n\n/** Every app asked for deploys through some other target. */\nexport class NoDeployableApps extends Error {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly target: string,\n\t) {\n\t\tsuper(\n\t\t\t`No apps to deploy through \"${target}\": every selected app deploys somewhere else. Pick the target with --target, or set each app's deploy in gkm.config.ts.`,\n\t\t);\n\t\tthis.name = 'NoDeployableApps';\n\t}\n}\n\n/** A build-only run through a target with no build phase. */\nexport class TargetBuildsNothing extends Error {\n\tconstructor(readonly target: string) {\n\t\tsuper(\n\t\t\t`\"${target}\" has no build phase, so a build-only run would do nothing. Deploy it in full, or build through a target that builds images (compose).`,\n\t\t);\n\t\tthis.name = 'TargetBuildsNothing';\n\t}\n}\n\n/** A failed rollback, after the failure that called for it. */\nexport class RollbackFailed extends Error {\n\tconstructor(\n\t\treadonly target: string,\n\t\t/** What failed first, and was being rolled back. */\n\t\treadonly failure: unknown,\n\t\tcause: unknown,\n\t) {\n\t\tsuper(\n\t\t\t`Deploying through \"${target}\" failed, and so did rolling it back: ${cause instanceof Error ? cause.message : String(cause)}. The stage may be part-released; deploy again, or roll it back by hand. The first failure: ${failure instanceof Error ? failure.message : String(failure)}`,\n\t\t\t{ cause },\n\t\t);\n\t\tthis.name = 'RollbackFailed';\n\t}\n}\n\n/**\n * Generate image tag from stage and timestamp\n */\nexport function generateTag(stage: string): string {\n\tconst timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);\n\treturn `${stage}-${timestamp}`;\n}\n\n/** Why an app that deploys through another target is left out. */\nfunction elsewhere(target: string, stage: string): string {\n\tconst instead = builtinTarget(target)?.instead?.(stage);\n\tif (instead) return `it deploys through \"${target}\" — ${instead}`;\n\treturn `it deploys through \"${target}\" — run \\`gkm deploy --target ${target} --stage ${stage}\\``;\n}\n\n/**\n * Deploy a workspace's stage through its target.\n *\n * The engine under `deploy()` and `gkm deploy`. It never prompts and never\n * exits: credentials come from `ctx.credentials`, progress goes to\n * `ctx.emit` and to `output`, and every failure is thrown.\n *\n * @internal\n */\nexport async function runDeploy(\n\tsource: NormalizedWorkspace | (() => Promise<NormalizedWorkspace>),\n\trequest: DeployRequest,\n\tctx: DeployContext,\n): Promise<DeployResult> {\n\tconst { stage } = request;\n\tconst redactor = ctx.redactor ?? new Redactor();\n\tconst emit = (event: DeployEvent) =>\n\t\tctx.emit(\n\t\t\tevent.type === 'log'\n\t\t\t\t? { ...event, message: redactor.redact(event.message) }\n\t\t\t\t: event,\n\t\t);\n\n\t/** Run one phase between its events. */\n\tconst phase = async <T>(name: DeployPhase, body: () => Promise<T>) => {\n\t\tctx.signal?.throwIfAborted();\n\t\temit({ type: 'phase.started', phase: name });\n\t\ttry {\n\t\t\tconst value = await body();\n\t\t\temit({ type: 'phase.finished', phase: name });\n\t\t\treturn value;\n\t\t} catch (error) {\n\t\t\temit({ type: 'phase.failed', phase: name, error: eventError(error) });\n\t\t\tthrow error;\n\t\t}\n\t};\n\n\tctx.signal?.throwIfAborted();\n\temit({ type: 'phase.started', phase: 'validate' });\n\n\tlet prepared: Prepared;\n\ttry {\n\t\tprepared = await prepare(source, request, ctx, emit, redactor);\n\t} catch (error) {\n\t\temit({ type: 'phase.failed', phase: 'validate', error: eventError(error) });\n\t\tthrow error;\n\t}\n\tconst { resolved, phaseCtx, store } = prepared;\n\tconst { target } = resolved;\n\n\t// A dry run takes no lock: it writes nothing a lock would protect, and it\n\t// should not stop a real deploy that starts while it is looking.\n\tif (ctx.dryRun) {\n\t\tconst run = await validate(target, phaseCtx, emit);\n\t\tawait phase('plan', () => target.plan(phaseCtx, run));\n\t\treturn target.result(phaseCtx, run);\n\t}\n\n\t// Building changes no stage — nothing is provisioned, released or\n\t// recorded — so it takes no lock: a CI job building the next release must\n\t// not fail because a server is deploying the last one.\n\tif (ctx.buildOnly) {\n\t\tif (!target.build) throw new TargetBuildsNothing(resolved.name);\n\t\tconst run = await validate(target, phaseCtx, emit);\n\t\tawait phase('build', () => target.build!(phaseCtx, run));\n\t\treturn target.result(phaseCtx, run);\n\t}\n\n\t// One deploy of a stage at a time. Taken before the target generates,\n\t// provisions or records anything, and held until the run ends however it\n\t// ends. A second run of the stage — another CI job, a laptop — gets\n\t// `StateLocked` naming this one instead of racing it; a run killed with\n\t// the lock held is released with `gkm state:unlock`.\n\tlet lock: Awaited<ReturnType<typeof store.lock>>;\n\ttry {\n\t\tlock = await store.lock(stage, { operation: 'deploy' });\n\t} catch (error) {\n\t\temit({ type: 'phase.failed', phase: 'validate', error: eventError(error) });\n\t\tthrow error;\n\t}\n\n\ttry {\n\t\tconst run = await validate(target, phaseCtx, emit);\n\t\tif (target.provision) {\n\t\t\tawait phase('provision', () => target.provision!(phaseCtx, run));\n\t\t}\n\t\tif (target.build) {\n\t\t\tawait phase('build', () => target.build!(phaseCtx, run));\n\t\t}\n\n\t\tlet releasing: 'release' | 'verify' = 'release';\n\t\ttry {\n\t\t\tawait phase('release', () => target.release(phaseCtx, run));\n\t\t\treleasing = 'verify';\n\t\t\tif (target.verify) {\n\t\t\t\tawait phase('verify', () => target.verify!(phaseCtx, run));\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// The caller stopping the run is not a release to undo.\n\t\t\tif (ctx.signal?.aborted || !target.capabilities.rollback) throw error;\n\t\t\tif (!target.rollback) throw error;\n\t\t\ttry {\n\t\t\t\tawait phase('rollback', () =>\n\t\t\t\t\ttarget.rollback!(phaseCtx, run, { phase: releasing, error }),\n\t\t\t\t);\n\t\t\t} catch (rollbackError) {\n\t\t\t\tthrow new RollbackFailed(resolved.name, error, rollbackError);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\n\t\treturn await target.result(phaseCtx, run);\n\t} finally {\n\t\tawait lock.release();\n\t}\n}\n\n/** `validate`, then the run's `deploy.started`, inside the validate phase. */\nasync function validate(\n\ttarget: AnyDeployTarget,\n\tphaseCtx: DeployPhaseContext<unknown>,\n\temit: (event: DeployEvent) => void,\n): Promise<unknown> {\n\ttry {\n\t\tconst run = await target.validate(phaseCtx);\n\t\tconst skipped = new Set(phaseCtx.skipped.map((s) => s.app));\n\t\temit({\n\t\t\ttype: 'deploy.started',\n\t\t\tstage: phaseCtx.stage,\n\t\t\ttarget: phaseCtx.target,\n\t\t\tidentity: phaseCtx.identity.key,\n\t\t\ttag: phaseCtx.tag,\n\t\t\tapps: phaseCtx.apps.filter((app) => !skipped.has(app)),\n\t\t\tdryRun: phaseCtx.dryRun,\n\t\t});\n\t\temit({ type: 'phase.finished', phase: 'validate' });\n\t\treturn run;\n\t} catch (error) {\n\t\temit({ type: 'phase.failed', phase: 'validate', error: eventError(error) });\n\t\tthrow error;\n\t}\n}\n\ninterface Prepared {\n\tresolved: ResolvedTarget;\n\tphaseCtx: DeployPhaseContext<unknown>;\n\tstore: Awaited<ReturnType<typeof createStateStore>>;\n}\n\n/** Everything the phases are handed, worked out before the first of them. */\nasync function prepare(\n\tsource: NormalizedWorkspace | (() => Promise<NormalizedWorkspace>),\n\trequest: DeployRequest,\n\tctx: DeployContext,\n\temit: (event: DeployEvent) => void,\n\tredactor: Redactor,\n): Promise<Prepared> {\n\tconst { stage } = request;\n\tconst configured = typeof source === 'function' ? await source() : source;\n\n\t// Before anything is provisioned: a typo'd stage would otherwise create a\n\t// whole second environment under the wrong name. The local stage is left\n\t// to the target, which says whether it can run it.\n\tconst local = stage === configured.stages.local;\n\tif (!local) assertDeployedStage(configured.stages, stage);\n\n\t// Before anything is discovered or derived: a target that cannot be found\n\t// — or a package that would not load — fails a deploy that has done\n\t// nothing yet.\n\tconst targetName = request.target ?? configured.deploy?.default ?? 'dokploy';\n\tconst resolved = await resolveTarget(targetName, {\n\t\tworkspace: configured,\n\t\tstage,\n\t\t...(ctx.targets ? { host: ctx.targets } : {}),\n\t});\n\tif (local && !resolved.target.capabilities.localStage) {\n\t\tassertDeployedStage(configured.stages, stage);\n\t}\n\tconst options = await parseTargetOptions(resolved);\n\n\t// Before anything is discovered: dev services asked of a target that runs\n\t// no containers fail a deploy that has done nothing.\n\tconst allowDevServices = request.allowDevServices === true;\n\tif (allowDevServices && resolved.target.runtime === 'aws') {\n\t\tthrow new DevServicesNeedServerTarget(targetName);\n\t}\n\n\t// What to deploy comes from the manifest.\n\t//\n\t// Discovered here rather than by the target, because the list of things\n\t// to deploy is the *declarations* — one `rest-api` is one server, one\n\t// `site` is one site — and the config only says how to run each. A project\n\t// that declares no surfaces keeps its configured apps, so adopting\n\t// constructs stays something you do a piece at a time.\n\tconst runnables: Record<string, string[]> = {};\n\tconst background: Record<string, string[]> = {};\n\tconst manifest = await discover({\n\t\tpatterns: constructGlobs(configured),\n\t\tcwd: configured.root,\n\t\trunnables,\n\t\tbackground,\n\t});\n\t// Where the stage's telemetry goes, settled before anything is built: a\n\t// stage that uses a `Telemetry` node on a target that cannot choose for it\n\t// — AWS has no self-hosted provider — fails here, naming the config.\n\tresolveStageTelemetry({\n\t\t...(configured.deploy?.telemetry\n\t\t\t? { telemetry: configured.deploy.telemetry }\n\t\t\t: {}),\n\t\tstage,\n\t\tlocal,\n\t\ttarget: targetName,\n\t\truntime: resolved.target.runtime,\n\t\tselfHosted: resolved.target.capabilities.selfHostedTelemetry === true,\n\t\tused: usesTelemetry(manifest),\n\t});\n\n\tconst units = derivedApps(manifest, configured);\n\tconst workspace: NormalizedWorkspace =\n\t\tObject.keys(units).length > 0 ? { ...configured, apps: units } : configured;\n\n\t// What every resource this deploy touches is named and claimed by —\n\t// resolved first, so a namespace that cannot be a name fails before\n\t// anything is built.\n\tconst identity = deployIdentity(workspace, stage);\n\tconst tag =\n\t\trequest.tag ??\n\t\t(resolved.target.tag\n\t\t\t? await resolved.target.tag({ cwd: workspace.root, stage })\n\t\t\t: generateTag(stage));\n\n\t// The apps asked for, in dependency order.\n\tconst buildOrder = getAppBuildOrder(workspace);\n\tlet selected = buildOrder;\n\tif (request.apps && request.apps.length > 0) {\n\t\tconst unknown = request.apps.filter((name) => !workspace.apps[name]);\n\t\tif (unknown.length > 0) {\n\t\t\tthrow new UnknownDeployApps(unknown, Object.keys(workspace.apps));\n\t\t}\n\t\tselected = buildOrder.filter((name) => request.apps!.includes(name));\n\t}\n\n\tconst skipped: { app: string; reason: string }[] = [];\n\tconst skip = (app: string, reason: string) => {\n\t\tskipped.push({ app, reason });\n\t\temit({ type: 'app.skipped', app, reason });\n\t};\n\n\t// An app that names another target deploys there, in a run of its own.\n\t// One on the default follows the run, so `--target` moves the stage\n\t// without every app having to be told.\n\tconst byDefault = configured.deploy?.default ?? 'dokploy';\n\tconst apps = selected.filter((name) => {\n\t\tconst own = workspace.apps[name]!.resolvedDeployTarget;\n\t\tconst deploysThrough = own === byDefault ? targetName : own;\n\t\tif (deploysThrough === targetName) return true;\n\t\tskip(name, elsewhere(deploysThrough, stage));\n\t\treturn false;\n\t});\n\tif (apps.length === 0) throw new NoDeployableApps(stage, targetName);\n\n\tconst store = await createStateStore({\n\t\tconfig: workspace.state,\n\t\tworkspaceRoot: workspace.root,\n\t\tworkspaceName: workspace.name,\n\t});\n\tconst secrets = await deploySecrets(workspace, stage, redactor, {\n\t\t...(ctx.home ? { home: ctx.home } : {}),\n\t});\n\n\tconst line =\n\t\t(level: 'info' | 'warn' | 'error') =>\n\t\t(message: string): void =>\n\t\t\temit({ type: 'log', level, message });\n\tconst logger: DeployTargetLogger = {\n\t\tinfo: line('info'),\n\t\twarn: line('warn'),\n\t\terror: line('error'),\n\t};\n\n\tconst phaseCtx: DeployPhaseContext<unknown> = {\n\t\ttarget: targetName,\n\t\tcwd: workspace.root,\n\t\tstage,\n\t\ttag,\n\t\ttagGiven: request.tag !== undefined,\n\t\tapps,\n\t\tskipped,\n\t\tidentity,\n\t\tworkspace,\n\t\tmanifest,\n\t\trunnables,\n\t\tbackground,\n\t\toptions,\n\t\tdryRun: ctx.dryRun,\n\t\tatomic: request.atomic ?? false,\n\t\tallowDevServices,\n\t\t...(request.skipDnsCheck ? { skipDnsCheck: true } : {}),\n\t\tcredentials: ctx.credentials,\n\t\tstate: store,\n\t\tsecrets,\n\t\tlogger,\n\t\tsignal: ctx.signal ?? new AbortController().signal,\n\t\tchildOutput: ctx.childOutput ?? 'inherit',\n\t\temit,\n\t\tskip,\n\t\tname: (resource) => applicationName(identity, resource),\n\t};\n\n\treturn { resolved, phaseCtx, store };\n}\n","/**\n * `deploy()`: a deploy as a library call.\n *\n * `gkm deploy` could only be driven from an interactive terminal: it prompted\n * for what it lacked and exited the process on failure, worked out of\n * `process.cwd()`, and reported progress by printing. Here the caller says\n * where the project is, hands over the credentials and a signal, and gets back\n * a run — events to iterate while it goes, and a structured result at the end.\n * Nothing below this asks a person anything or ends the process; `gkm deploy`\n * is a thin wrapper that does both.\n */\n\nimport { resolve } from 'node:path';\nimport { findWorkspaceRoot, loadWorkspaceConfig } from '../config';\nimport { gkmHome } from '../home';\nimport { type OutputLevel, withOutput } from '../output';\nimport { LocalSandbox } from '../sandbox/local';\nimport { type Sandbox, withSandbox } from '../sandbox/sandbox';\nimport { Redactor } from '../target/secrets';\nimport type { AnyDeployTarget } from '../target/types';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport { type CredentialProvider, storedCredentials } from './credentials';\nimport { type DeployEvent, eventError } from './events';\nimport { type DeployContext, runDeploy } from './orchestrate';\nimport type { DeployResult } from './types';\n\n/**\n * Where a run's progress lines go, besides its events. `@geekmidas/logger`'s\n * `Logger` is one.\n */\nexport interface DeployLogger {\n\tinfo(message: string): void;\n\twarn(message: string): void;\n\terror?(message: string): void;\n}\n\nexport interface DeployInput {\n\t/**\n\t * The project to deploy: the directory holding `gkm.config.ts`, or any\n\t * directory inside it. Never assumed to be `process.cwd()`.\n\t */\n\tcwd: string;\n\t/**\n\t * The stage to deploy — one of the config's deployed stages, or its local\n\t * stage through a target that runs one (`compose`).\n\t */\n\tstage: string;\n\t/**\n\t * The target to deploy through, by name. Defaults to `deploy.default`,\n\t * else `dokploy`.\n\t */\n\ttarget?: string;\n\t/**\n\t * The host's own targets, by name. Consulted before the built-ins and the\n\t * config's `deploy.targets`, so a host can deploy through an\n\t * implementation of its own — `{ dokploy: myDokploy }` included.\n\t */\n\ttargets?: Record<string, AnyDeployTarget>;\n\t/** The image tag. Defaults to `<stage>-<timestamp>`. */\n\ttag?: string;\n\t/** Deploy only these apps (in dependency order). Defaults to all. */\n\tapps?: string[];\n\t/**\n\t * Look everything up and report what would happen — as `resource.planned`\n\t * events — without taking the lock, writing state, generating secrets,\n\t * changing Dokploy or building and pushing images.\n\t */\n\tdryRun?: boolean;\n\t/**\n\t * Validate and build only: the target's images built — and pushed, where\n\t * it pushes them — with no lock taken and nothing provisioned, released,\n\t * verified or recorded. `gkm compose --build --push` runs this way.\n\t */\n\tbuildOnly?: boolean;\n\t/**\n\t * When a release or its verification fails, roll back every app the run\n\t * released rather than only the ones that failed — on a target that can\n\t * roll back. For apps that must move together.\n\t */\n\tatomic?: boolean;\n\t/**\n\t * Where credentials come from. Defaults to the environment, then the\n\t * logins `gkm login` stored. A credential no provider has stops the run\n\t * with `MissingCredential`; nothing prompts.\n\t */\n\tcredentials?: CredentialProvider;\n\t/**\n\t * On a server target, run a dev service for every construct a deployed\n\t * stage does not account for — no key in its secrets, and no provider\n\t * (`deploy.objects`) backing its kind: MinIO for a bucket, Mailpit for\n\t * mail. Neither is production-grade — Mailpit delivers no mail — and every\n\t * run that uses one emits `dev-service.used`. Without it, a deployed stage\n\t * missing a mail or storage key fails with `ExternalServicesNotConfigured`;\n\t * on an AWS target it fails with `DevServicesNeedServerTarget`.\n\t */\n\tallowDevServices?: boolean;\n\t/**\n\t * Do not check, on a server target, that each public host resolves to\n\t * the stage's server (`GKM_SERVER_IPV4`) before the stack starts — for a CDN\n\t * or proxy in front of the server.\n\t */\n\tskipDnsCheck?: boolean;\n\t/** Receives each progress line as it is written. Defaults to none. */\n\tlogger?: DeployLogger;\n\t/**\n\t * Stops the run: in-flight Dokploy requests and docker children are\n\t * cancelled, the stage's lock is released, and `result` rejects with the\n\t * signal's reason.\n\t */\n\tsignal?: AbortSignal;\n\t/**\n\t * The CLI's home: stage keys and stored logins. Defaults to `GKM_HOME`,\n\t * else `~/.gkm`.\n\t */\n\thome?: string;\n\t/**\n\t * Where docker's own output goes: the host's terminal (`inherit`, the\n\t * default), its stderr, or nowhere.\n\t */\n\tchildOutput?: 'inherit' | 'stderr' | 'ignore';\n\t/**\n\t * Where the project's own code runs: loading `gkm.config.ts`, discovering\n\t * its constructs, sniffing each app's environment. Defaults to a\n\t * `LocalSandbox` on the project — a child process here with an\n\t * allowlisted environment, for a project the host trusts.\n\t *\n\t * A host deploying repositories it does not trust passes one that\n\t * isolates — a container per build — and the config then reaches the\n\t * deploy only as data. Credentials never enter a sandbox: they go from\n\t * `credentials` to the steps that provision, push and release.\n\t */\n\tsandbox?: Sandbox;\n}\n\n/** A deploy in progress. */\nexport interface DeployRun extends AsyncIterable<DeployEvent> {\n\t/**\n\t * What the deploy did, once it is done. Rejects with the error that\n\t * stopped it — `MissingCredential`, `StateLocked`, `ProjectNotOwned`, … —\n\t * after a `deploy.failed` event carrying the same.\n\t */\n\treadonly result: Promise<DeployResult>;\n}\n\n/**\n * Every event a run emits, kept so that each iteration — however late it\n * starts — sees the run from its first event to its last.\n */\nclass EventLog {\n\tprivate readonly events: DeployEvent[] = [];\n\tprivate closed = false;\n\tprivate waiting: (() => void)[] = [];\n\n\tpush(event: DeployEvent): void {\n\t\tthis.events.push(event);\n\t\tthis.wake();\n\t}\n\n\tclose(): void {\n\t\tthis.closed = true;\n\t\tthis.wake();\n\t}\n\n\tprivate wake(): void {\n\t\tconst waiting = this.waiting;\n\t\tthis.waiting = [];\n\t\tfor (const resume of waiting) resume();\n\t}\n\n\tasync *iterate(): AsyncGenerator<DeployEvent> {\n\t\tlet next = 0;\n\t\twhile (true) {\n\t\t\tif (next < this.events.length) {\n\t\t\t\tyield this.events[next++]!;\n\t\t\t} else if (this.closed) {\n\t\t\t\treturn;\n\t\t\t} else {\n\t\t\t\tawait new Promise<void>((resume) => this.waiting.push(resume));\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction forward(\n\tlogger: DeployLogger | undefined,\n\tlevel: OutputLevel,\n\tmessage: string,\n): void {\n\tif (!logger) return;\n\tif (level === 'info') logger.info(message);\n\telse if (level === 'error' && logger.error) logger.error(message);\n\telse logger.warn(message);\n}\n\n/**\n * Deploy a project's stage.\n *\n * ```ts\n * import { deploy } from '@geekmidas/cli/deploy';\n *\n * const run = deploy({ cwd: '/srv/checkouts/shop', stage: 'production' });\n * for await (const event of run) {\n * if (event.type === 'app.deployed') console.log(event.app, event.url);\n * }\n * const result = await run.result;\n * ```\n *\n * The run starts at once; iterating it is optional, and an iteration started\n * late still sees every event from the first.\n */\nexport function deploy(input: DeployInput): DeployRun {\n\tconst log = new EventLog();\n\t// Every secret the run reads is masked in every line it reports — in the\n\t// events and in what the logger is handed.\n\tconst redactor = new Redactor();\n\tconst emit = (event: DeployEvent) => {\n\t\tconst masked =\n\t\t\tevent.type === 'log'\n\t\t\t\t? { ...event, message: redactor.redact(event.message) }\n\t\t\t\t: event;\n\t\tlog.push(masked);\n\t\tif (masked.type === 'log') {\n\t\t\tforward(input.logger, masked.level, masked.message);\n\t\t}\n\t};\n\n\tconst ctx: DeployContext = {\n\t\temit,\n\t\tcredentials:\n\t\t\tinput.credentials ?? storedCredentials({ home: input.home ?? gkmHome() }),\n\t\tdryRun: input.dryRun ?? false,\n\t\t...(input.buildOnly ? { buildOnly: true } : {}),\n\t\thome: input.home ?? gkmHome(),\n\t\tredactor,\n\t\t...(input.signal ? { signal: input.signal } : {}),\n\t\t...(input.childOutput ? { childOutput: input.childOutput } : {}),\n\t\t...(input.targets ? { targets: input.targets } : {}),\n\t};\n\n\t// Every line anything in the run writes through `output` — the deploy's\n\t// own, the Dockerfile generator's, discovery's warnings — becomes an event\n\t// of this run, and of no other.\n\tconst result = withOutput(\n\t\t(level, message) => emit({ type: 'log', level, message }),\n\t\tasync () => {\n\t\t\ttry {\n\t\t\t\tinput.signal?.throwIfAborted();\n\t\t\t\tconst sandbox =\n\t\t\t\t\tinput.sandbox ??\n\t\t\t\t\tnew LocalSandbox({ root: findWorkspaceRoot(resolve(input.cwd)) });\n\t\t\t\t// Every step of the run that executes the project's code — the\n\t\t\t\t// engine's own discoveries and sniffs included — runs in it.\n\t\t\t\tconst deployed = await withSandbox(sandbox, () =>\n\t\t\t\t\trunDeploy(\n\t\t\t\t\t\t() => load(input, sandbox),\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tstage: input.stage,\n\t\t\t\t\t\t\t...(input.target ? { target: input.target } : {}),\n\t\t\t\t\t\t\t...(input.tag ? { tag: input.tag } : {}),\n\t\t\t\t\t\t\t...(input.apps ? { apps: input.apps } : {}),\n\t\t\t\t\t\t\t...(input.atomic ? { atomic: true } : {}),\n\t\t\t\t\t\t\t...(input.allowDevServices\n\t\t\t\t\t\t\t\t? { allowDevServices: input.allowDevServices }\n\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t\t...(input.skipDnsCheck ? { skipDnsCheck: true } : {}),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tctx,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\temit({ type: 'deploy.finished', result: deployed });\n\t\t\t\treturn deployed;\n\t\t\t} catch (error) {\n\t\t\t\temit({ type: 'deploy.failed', error: eventError(error) });\n\t\t\t\tthrow error;\n\t\t\t} finally {\n\t\t\t\tlog.close();\n\t\t\t}\n\t\t},\n\t);\n\n\t// A caller that only iterates still learns of the failure from the\n\t// `deploy.failed` event; the rejection must not also crash its process as\n\t// unhandled. Awaiting `result` still rejects.\n\tresult.catch(() => {});\n\n\treturn {\n\t\tresult,\n\t\t[Symbol.asyncIterator]: () => log.iterate(),\n\t};\n}\n\n/**\n * The workspace at `input.cwd`. The stage is checked by the run, once it\n * knows the target: one that runs on this machine may take the local stage.\n */\nasync function load(\n\tinput: DeployInput,\n\tsandbox: Sandbox,\n): Promise<NormalizedWorkspace> {\n\tconst { workspace } = await loadWorkspaceConfig(resolve(input.cwd), {\n\t\tsandbox,\n\t});\n\treturn workspace;\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"deploy-QNtx_eWR.mjs","names":[],"sources":["../src/target/secrets.ts","../src/deploy/orchestrate.ts","../src/deploy/deploy.ts"],"sourcesContent":["/**\n * A stage's secrets as a target sees them: read and written through the\n * stage's own store, and masked in everything the run prints once read.\n */\n\nimport { secretsStoreFor } from '../secrets/store';\nimport type { StageSecrets } from '../secrets/types';\nimport type { NormalizedWorkspace } from '../workspace/types';\nimport type { DeploySecrets } from './types';\n\n/**\n * Shorter values are left alone: what a progress line legitimately prints —\n * a port, a stage, an app name — would otherwise come out as `***` wherever\n * a secret happened to share it.\n */\nconst SHORTEST_MASKED = 6;\n\n/** The values a run must never print, and the rewrite that keeps it so. */\nexport class Redactor {\n\tprivate readonly values = new Set<string>();\n\n\tadd(value: string | undefined): void {\n\t\tif (value && value.length >= SHORTEST_MASKED) this.values.add(value);\n\t}\n\n\t/** `text` with every masked value replaced by `***`, longest first. */\n\tredact(text: string): string {\n\t\tif (this.values.size === 0) return text;\n\t\tlet out = text;\n\t\tconst longestFirst = [...this.values].sort((a, b) => b.length - a.length);\n\t\tfor (const value of longestFirst) out = out.replaceAll(value, '***');\n\t\treturn out;\n\t}\n}\n\n/** Every value in `secrets` that is a secret rather than a label. */\nfunction secretValues(secrets: StageSecrets): string[] {\n\treturn [\n\t\tsecrets.seed,\n\t\t...Object.values(secrets.custom ?? {}),\n\t\t...Object.values(secrets.urls ?? {}),\n\t\t...Object.values(secrets.services ?? {}).map(\n\t\t\t(service) => service?.password,\n\t\t),\n\t].filter((value): value is string => typeof value === 'string');\n}\n\n/**\n * The stage's secrets through the store `secrets.store` names for it. Opening\n * one builds a client and makes no request, so a target that never reads them\n * costs nothing.\n */\nexport async function deploySecrets(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n\tredactor: Redactor,\n\toptions: { home?: string } = {},\n): Promise<DeploySecrets> {\n\tconst store = await secretsStoreFor(workspace, stage, options);\n\tconst mask = (secrets: StageSecrets) => {\n\t\tfor (const value of secretValues(secrets)) redactor.add(value);\n\t};\n\n\treturn {\n\t\tstore: store.name,\n\t\tasync read() {\n\t\t\tconst secrets = await store.read(stage);\n\t\t\tif (secrets) mask(secrets);\n\t\t\treturn secrets;\n\t\t},\n\t\tasync write(secrets) {\n\t\t\tmask(secrets);\n\t\t\tawait store.write(stage, secrets);\n\t\t},\n\t\tmask: (value) => redactor.add(value),\n\t};\n}\n","/**\n * The run around a target: what every deploy does whatever it deploys\n * through.\n *\n * It loads the workspace, resolves the target, works out the apps and the\n * identity, takes the stage's lock, and calls the target's phases in order,\n * reporting each as it starts, finishes or fails. Everything that is about\n * *where* the stage goes is the target's.\n */\n\nimport { discover } from '../reconcile/discover.js';\nimport { constructGlobs } from '../reconcile/workspace.js';\nimport { builtinTarget } from '../target/builtins';\nimport {\n\tparseTargetOptions,\n\ttype ResolvedTarget,\n\tresolveTarget,\n} from '../target/resolve';\nimport { deploySecrets, Redactor } from '../target/secrets';\nimport type {\n\tAnyDeployTarget,\n\tDeployPhaseContext,\n\tDeployTargetLogger,\n} from '../target/types';\nimport { resolveStageTelemetry } from '../telemetry/config';\nimport { usesTelemetry } from '../telemetry/edges';\nimport { derivedApps } from '../workspace/derive.js';\nimport { getAppBuildOrder } from '../workspace/index.js';\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport type { CredentialProvider } from './credentials';\nimport { DevServicesNeedServerTarget } from './devServices';\nimport { type DeployEvent, type DeployPhase, eventError } from './events';\nimport { applicationName, deployIdentity } from './identity.js';\nimport { createStateStore } from './StateStore.js';\nimport type { DeployResult } from './types';\n\n/** What a run deploys, once the workspace is loaded. */\nexport interface DeployRequest {\n\t/** Deployment stage (e.g., 'production', 'staging') */\n\tstage: string;\n\t/** Image tag (default: stage-timestamp) */\n\ttag?: string;\n\t/** Specific apps to deploy (default: all) */\n\tapps?: string[];\n\t/** The target to deploy through. Defaults to `deploy.default`. */\n\ttarget?: string;\n\t/** On a failed release, roll back every app rather than the failed ones. */\n\tatomic?: boolean;\n\t/**\n\t * `--allow-dev-services`: a deployed stage runs the dev service for every\n\t * bucket and mail it does not account for. Server targets only.\n\t */\n\tallowDevServices?: boolean;\n\t/** `--skip-dns-check`: a server target checks no host's DNS. */\n\tskipDnsCheck?: boolean;\n}\n\n/**\n * What a run is handed by whoever started it: where its events go, where it\n * gets credentials, and whether it may change anything. Nothing in a run\n * reads a terminal or exits the process — that is its caller's business.\n */\nexport interface DeployContext {\n\temit: (event: DeployEvent) => void;\n\tcredentials: CredentialProvider;\n\tsignal?: AbortSignal;\n\t/** Look everything up, create, build and push nothing. */\n\tdryRun: boolean;\n\t/**\n\t * Validate and build — the target's images built, and pushed where it\n\t * pushes them — and nothing else: no lock, nothing provisioned, released,\n\t * verified or recorded. What a CI job runs before a server pulls.\n\t */\n\tbuildOnly?: boolean;\n\t/** The CLI's home, for the stage's keys. Defaults to `GKM_HOME`. */\n\thome?: string;\n\t/** Where spawned processes' output goes. Defaults to the terminal. */\n\tchildOutput?: 'inherit' | 'stderr' | 'ignore';\n\t/** The host's own targets, which win over every other by the same name. */\n\ttargets?: Record<string, AnyDeployTarget>;\n\t/**\n\t * What the run masks in what it prints. `deploy()` passes the one its\n\t * progress lines go through; a run without one masks only its events.\n\t */\n\tredactor?: Redactor;\n}\n\n/** Apps were asked for by name that the workspace does not have. */\nexport class UnknownDeployApps extends Error {\n\tconstructor(\n\t\treadonly apps: readonly string[],\n\t\treadonly available: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`Unknown apps: ${apps.join(', ')}\\n` +\n\t\t\t\t`Available apps: ${available.join(', ')}`,\n\t\t);\n\t\tthis.name = 'UnknownDeployApps';\n\t}\n}\n\n/** Every app asked for deploys through some other target. */\nexport class NoDeployableApps extends Error {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly target: string,\n\t) {\n\t\tsuper(\n\t\t\t`No apps to deploy through \"${target}\": every selected app deploys somewhere else. Pick the target with --target, or set each app's deploy in gkm.config.ts.`,\n\t\t);\n\t\tthis.name = 'NoDeployableApps';\n\t}\n}\n\n/** A build-only run through a target with no build phase. */\nexport class TargetBuildsNothing extends Error {\n\tconstructor(readonly target: string) {\n\t\tsuper(\n\t\t\t`\"${target}\" has no build phase, so a build-only run would do nothing. Deploy it in full, or build through a target that builds images (compose).`,\n\t\t);\n\t\tthis.name = 'TargetBuildsNothing';\n\t}\n}\n\n/** A failed rollback, after the failure that called for it. */\nexport class RollbackFailed extends Error {\n\tconstructor(\n\t\treadonly target: string,\n\t\t/** What failed first, and was being rolled back. */\n\t\treadonly failure: unknown,\n\t\tcause: unknown,\n\t) {\n\t\tsuper(\n\t\t\t`Deploying through \"${target}\" failed, and so did rolling it back: ${cause instanceof Error ? cause.message : String(cause)}. The stage may be part-released; deploy again, or roll it back by hand. The first failure: ${failure instanceof Error ? failure.message : String(failure)}`,\n\t\t\t{ cause },\n\t\t);\n\t\tthis.name = 'RollbackFailed';\n\t}\n}\n\n/**\n * Generate image tag from stage and timestamp\n */\nexport function generateTag(stage: string): string {\n\tconst timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);\n\treturn `${stage}-${timestamp}`;\n}\n\n/** Why an app that deploys through another target is left out. */\nfunction elsewhere(target: string, stage: string): string {\n\tconst instead = builtinTarget(target)?.instead?.(stage);\n\tif (instead) return `it deploys through \"${target}\" — ${instead}`;\n\treturn `it deploys through \"${target}\" — run \\`gkm deploy --target ${target} --stage ${stage}\\``;\n}\n\n/**\n * Deploy a workspace's stage through its target.\n *\n * The engine under `deploy()` and `gkm deploy`. It never prompts and never\n * exits: credentials come from `ctx.credentials`, progress goes to\n * `ctx.emit` and to `output`, and every failure is thrown.\n *\n * @internal\n */\nexport async function runDeploy(\n\tsource: NormalizedWorkspace | (() => Promise<NormalizedWorkspace>),\n\trequest: DeployRequest,\n\tctx: DeployContext,\n): Promise<DeployResult> {\n\tconst { stage } = request;\n\tconst redactor = ctx.redactor ?? new Redactor();\n\tconst emit = (event: DeployEvent) =>\n\t\tctx.emit(\n\t\t\tevent.type === 'log'\n\t\t\t\t? { ...event, message: redactor.redact(event.message) }\n\t\t\t\t: event,\n\t\t);\n\n\t/** Run one phase between its events. */\n\tconst phase = async <T>(name: DeployPhase, body: () => Promise<T>) => {\n\t\tctx.signal?.throwIfAborted();\n\t\temit({ type: 'phase.started', phase: name });\n\t\ttry {\n\t\t\tconst value = await body();\n\t\t\temit({ type: 'phase.finished', phase: name });\n\t\t\treturn value;\n\t\t} catch (error) {\n\t\t\temit({ type: 'phase.failed', phase: name, error: eventError(error) });\n\t\t\tthrow error;\n\t\t}\n\t};\n\n\tctx.signal?.throwIfAborted();\n\temit({ type: 'phase.started', phase: 'validate' });\n\n\tlet prepared: Prepared;\n\ttry {\n\t\tprepared = await prepare(source, request, ctx, emit, redactor);\n\t} catch (error) {\n\t\temit({ type: 'phase.failed', phase: 'validate', error: eventError(error) });\n\t\tthrow error;\n\t}\n\tconst { resolved, phaseCtx, store } = prepared;\n\tconst { target } = resolved;\n\n\t// A dry run takes no lock: it writes nothing a lock would protect, and it\n\t// should not stop a real deploy that starts while it is looking.\n\tif (ctx.dryRun) {\n\t\tconst run = await validate(target, phaseCtx, emit);\n\t\tawait phase('plan', () => target.plan(phaseCtx, run));\n\t\treturn target.result(phaseCtx, run);\n\t}\n\n\t// Building changes no stage — nothing is provisioned, released or\n\t// recorded — so it takes no lock: a CI job building the next release must\n\t// not fail because a server is deploying the last one.\n\tif (ctx.buildOnly) {\n\t\tif (!target.build) throw new TargetBuildsNothing(resolved.name);\n\t\tconst run = await validate(target, phaseCtx, emit);\n\t\tawait phase('build', () => target.build!(phaseCtx, run));\n\t\treturn target.result(phaseCtx, run);\n\t}\n\n\t// One deploy of a stage at a time. Taken before the target generates,\n\t// provisions or records anything, and held until the run ends however it\n\t// ends. A second run of the stage — another CI job, a laptop — gets\n\t// `StateLocked` naming this one instead of racing it; a run killed with\n\t// the lock held is released with `gkm state:unlock`.\n\tlet lock: Awaited<ReturnType<typeof store.lock>>;\n\ttry {\n\t\tlock = await store.lock(stage, { operation: 'deploy' });\n\t} catch (error) {\n\t\temit({ type: 'phase.failed', phase: 'validate', error: eventError(error) });\n\t\tthrow error;\n\t}\n\n\ttry {\n\t\tconst run = await validate(target, phaseCtx, emit);\n\t\tif (target.provision) {\n\t\t\tawait phase('provision', () => target.provision!(phaseCtx, run));\n\t\t}\n\t\tif (target.build) {\n\t\t\tawait phase('build', () => target.build!(phaseCtx, run));\n\t\t}\n\n\t\tlet releasing: 'release' | 'verify' = 'release';\n\t\ttry {\n\t\t\tawait phase('release', () => target.release(phaseCtx, run));\n\t\t\treleasing = 'verify';\n\t\t\tif (target.verify) {\n\t\t\t\tawait phase('verify', () => target.verify!(phaseCtx, run));\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// The caller stopping the run is not a release to undo.\n\t\t\tif (ctx.signal?.aborted || !target.capabilities.rollback) throw error;\n\t\t\tif (!target.rollback) throw error;\n\t\t\ttry {\n\t\t\t\tawait phase('rollback', () =>\n\t\t\t\t\ttarget.rollback!(phaseCtx, run, { phase: releasing, error }),\n\t\t\t\t);\n\t\t\t} catch (rollbackError) {\n\t\t\t\tthrow new RollbackFailed(resolved.name, error, rollbackError);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\n\t\treturn await target.result(phaseCtx, run);\n\t} finally {\n\t\tawait lock.release();\n\t}\n}\n\n/** `validate`, then the run's `deploy.started`, inside the validate phase. */\nasync function validate(\n\ttarget: AnyDeployTarget,\n\tphaseCtx: DeployPhaseContext<unknown>,\n\temit: (event: DeployEvent) => void,\n): Promise<unknown> {\n\ttry {\n\t\tconst run = await target.validate(phaseCtx);\n\t\tconst skipped = new Set(phaseCtx.skipped.map((s) => s.app));\n\t\temit({\n\t\t\ttype: 'deploy.started',\n\t\t\tstage: phaseCtx.stage,\n\t\t\ttarget: phaseCtx.target,\n\t\t\tidentity: phaseCtx.identity.key,\n\t\t\ttag: phaseCtx.tag,\n\t\t\tapps: phaseCtx.apps.filter((app) => !skipped.has(app)),\n\t\t\tdryRun: phaseCtx.dryRun,\n\t\t});\n\t\temit({ type: 'phase.finished', phase: 'validate' });\n\t\treturn run;\n\t} catch (error) {\n\t\temit({ type: 'phase.failed', phase: 'validate', error: eventError(error) });\n\t\tthrow error;\n\t}\n}\n\ninterface Prepared {\n\tresolved: ResolvedTarget;\n\tphaseCtx: DeployPhaseContext<unknown>;\n\tstore: Awaited<ReturnType<typeof createStateStore>>;\n}\n\n/** Everything the phases are handed, worked out before the first of them. */\nasync function prepare(\n\tsource: NormalizedWorkspace | (() => Promise<NormalizedWorkspace>),\n\trequest: DeployRequest,\n\tctx: DeployContext,\n\temit: (event: DeployEvent) => void,\n\tredactor: Redactor,\n): Promise<Prepared> {\n\tconst { stage } = request;\n\tconst configured = typeof source === 'function' ? await source() : source;\n\n\t// Before anything is provisioned: a typo'd stage would otherwise create a\n\t// whole second environment under the wrong name. The local stage is left\n\t// to the target, which says whether it can run it.\n\tconst local = stage === configured.stages.local;\n\tif (!local) assertDeployedStage(configured.stages, stage);\n\n\t// Before anything is discovered or derived: a target that cannot be found\n\t// — or a package that would not load — fails a deploy that has done\n\t// nothing yet.\n\tconst targetName = request.target ?? configured.deploy?.default ?? 'dokploy';\n\tconst resolved = await resolveTarget(targetName, {\n\t\tworkspace: configured,\n\t\tstage,\n\t\t...(ctx.targets ? { host: ctx.targets } : {}),\n\t});\n\tif (local && !resolved.target.capabilities.localStage) {\n\t\tassertDeployedStage(configured.stages, stage);\n\t}\n\tconst options = await parseTargetOptions(resolved);\n\n\t// Before anything is discovered: dev services asked of a target that runs\n\t// no containers fail a deploy that has done nothing.\n\tconst allowDevServices = request.allowDevServices === true;\n\tif (allowDevServices && resolved.target.runtime === 'aws') {\n\t\tthrow new DevServicesNeedServerTarget(targetName);\n\t}\n\n\t// What to deploy comes from the manifest.\n\t//\n\t// Discovered here rather than by the target, because the list of things\n\t// to deploy is the *declarations* — one `rest-api` is one server, one\n\t// `site` is one site — and the config only says how to run each. A project\n\t// that declares no surfaces keeps its configured apps, so adopting\n\t// constructs stays something you do a piece at a time.\n\tconst runnables: Record<string, string[]> = {};\n\tconst background: Record<string, string[]> = {};\n\tconst manifest = await discover({\n\t\tpatterns: constructGlobs(configured),\n\t\tcwd: configured.root,\n\t\trunnables,\n\t\tbackground,\n\t});\n\t// Where the stage's telemetry goes, settled before anything is built: a\n\t// stage that uses a `Telemetry` node on a target that cannot choose for it\n\t// — AWS has no self-hosted provider — fails here, naming the config.\n\tresolveStageTelemetry({\n\t\t...(configured.deploy?.telemetry\n\t\t\t? { telemetry: configured.deploy.telemetry }\n\t\t\t: {}),\n\t\tstage,\n\t\tlocal,\n\t\ttarget: targetName,\n\t\truntime: resolved.target.runtime,\n\t\tselfHosted: resolved.target.capabilities.selfHostedTelemetry === true,\n\t\tused: usesTelemetry(manifest),\n\t});\n\n\tconst units = derivedApps(manifest, configured);\n\tconst workspace: NormalizedWorkspace =\n\t\tObject.keys(units).length > 0 ? { ...configured, apps: units } : configured;\n\n\t// What every resource this deploy touches is named and claimed by —\n\t// resolved first, so a namespace that cannot be a name fails before\n\t// anything is built.\n\tconst identity = deployIdentity(workspace, stage);\n\tconst tag =\n\t\trequest.tag ??\n\t\t(resolved.target.tag\n\t\t\t? await resolved.target.tag({ cwd: workspace.root, stage })\n\t\t\t: generateTag(stage));\n\n\t// The apps asked for, in dependency order.\n\tconst buildOrder = getAppBuildOrder(workspace);\n\tlet selected = buildOrder;\n\tif (request.apps && request.apps.length > 0) {\n\t\tconst unknown = request.apps.filter((name) => !workspace.apps[name]);\n\t\tif (unknown.length > 0) {\n\t\t\tthrow new UnknownDeployApps(unknown, Object.keys(workspace.apps));\n\t\t}\n\t\tselected = buildOrder.filter((name) => request.apps!.includes(name));\n\t}\n\n\tconst skipped: { app: string; reason: string }[] = [];\n\tconst skip = (app: string, reason: string) => {\n\t\tskipped.push({ app, reason });\n\t\temit({ type: 'app.skipped', app, reason });\n\t};\n\n\t// An app that names another target deploys there, in a run of its own.\n\t// One on the default follows the run, so `--target` moves the stage\n\t// without every app having to be told.\n\tconst byDefault = configured.deploy?.default ?? 'dokploy';\n\tconst apps = selected.filter((name) => {\n\t\tconst own = workspace.apps[name]!.resolvedDeployTarget;\n\t\tconst deploysThrough = own === byDefault ? targetName : own;\n\t\tif (deploysThrough === targetName) return true;\n\t\tskip(name, elsewhere(deploysThrough, stage));\n\t\treturn false;\n\t});\n\tif (apps.length === 0) throw new NoDeployableApps(stage, targetName);\n\n\tconst store = await createStateStore({\n\t\tconfig: workspace.state,\n\t\tworkspaceRoot: workspace.root,\n\t\tworkspaceName: workspace.name,\n\t});\n\tconst secrets = await deploySecrets(workspace, stage, redactor, {\n\t\t...(ctx.home ? { home: ctx.home } : {}),\n\t});\n\n\tconst line =\n\t\t(level: 'info' | 'warn' | 'error') =>\n\t\t(message: string): void =>\n\t\t\temit({ type: 'log', level, message });\n\tconst logger: DeployTargetLogger = {\n\t\tinfo: line('info'),\n\t\twarn: line('warn'),\n\t\terror: line('error'),\n\t};\n\n\tconst phaseCtx: DeployPhaseContext<unknown> = {\n\t\ttarget: targetName,\n\t\tcwd: workspace.root,\n\t\tstage,\n\t\ttag,\n\t\ttagGiven: request.tag !== undefined,\n\t\tapps,\n\t\tskipped,\n\t\tidentity,\n\t\tworkspace,\n\t\tmanifest,\n\t\trunnables,\n\t\tbackground,\n\t\toptions,\n\t\tdryRun: ctx.dryRun,\n\t\tatomic: request.atomic ?? false,\n\t\tallowDevServices,\n\t\t...(request.skipDnsCheck ? { skipDnsCheck: true } : {}),\n\t\tcredentials: ctx.credentials,\n\t\tstate: store,\n\t\tsecrets,\n\t\tlogger,\n\t\tsignal: ctx.signal ?? new AbortController().signal,\n\t\tchildOutput: ctx.childOutput ?? 'inherit',\n\t\temit,\n\t\tskip,\n\t\tname: (resource) => applicationName(identity, resource),\n\t};\n\n\treturn { resolved, phaseCtx, store };\n}\n","/**\n * `deploy()`: a deploy as a library call.\n *\n * `gkm deploy` could only be driven from an interactive terminal: it prompted\n * for what it lacked and exited the process on failure, worked out of\n * `process.cwd()`, and reported progress by printing. Here the caller says\n * where the project is, hands over the credentials and a signal, and gets back\n * a run — events to iterate while it goes, and a structured result at the end.\n * Nothing below this asks a person anything or ends the process; `gkm deploy`\n * is a thin wrapper that does both.\n */\n\nimport { resolve } from 'node:path';\nimport { findWorkspaceRoot, loadWorkspaceConfig } from '../config';\nimport { gkmHome } from '../home';\nimport { type OutputLevel, withOutput } from '../output';\nimport { LocalSandbox } from '../sandbox/local';\nimport { type Sandbox, withSandbox } from '../sandbox/sandbox';\nimport { Redactor } from '../target/secrets';\nimport type { AnyDeployTarget } from '../target/types';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport { type CredentialProvider, storedCredentials } from './credentials';\nimport { type DeployEvent, eventError } from './events';\nimport { type DeployContext, runDeploy } from './orchestrate';\nimport type { DeployResult } from './types';\n\n/**\n * Where a run's progress lines go, besides its events. `@geekmidas/logger`'s\n * `Logger` is one.\n */\nexport interface DeployLogger {\n\tinfo(message: string): void;\n\twarn(message: string): void;\n\terror?(message: string): void;\n}\n\nexport interface DeployInput {\n\t/**\n\t * The project to deploy: the directory holding `gkm.config.ts`, or any\n\t * directory inside it. Never assumed to be `process.cwd()`.\n\t */\n\tcwd: string;\n\t/**\n\t * The stage to deploy — one of the config's deployed stages, or its local\n\t * stage through a target that runs one (`compose`).\n\t */\n\tstage: string;\n\t/**\n\t * The target to deploy through, by name. Defaults to `deploy.default`,\n\t * else `dokploy`.\n\t */\n\ttarget?: string;\n\t/**\n\t * The host's own targets, by name. Consulted before the built-ins and the\n\t * config's `deploy.targets`, so a host can deploy through an\n\t * implementation of its own — `{ dokploy: myDokploy }` included.\n\t */\n\ttargets?: Record<string, AnyDeployTarget>;\n\t/** The image tag. Defaults to `<stage>-<timestamp>`. */\n\ttag?: string;\n\t/** Deploy only these apps (in dependency order). Defaults to all. */\n\tapps?: string[];\n\t/**\n\t * Look everything up and report what would happen — as `resource.planned`\n\t * events — without taking the lock, writing state, generating secrets,\n\t * changing Dokploy or building and pushing images.\n\t */\n\tdryRun?: boolean;\n\t/**\n\t * Validate and build only: the target's images built — and pushed, where\n\t * it pushes them — with no lock taken and nothing provisioned, released,\n\t * verified or recorded. `gkm compose --build --push` runs this way.\n\t */\n\tbuildOnly?: boolean;\n\t/**\n\t * When a release or its verification fails, roll back every app the run\n\t * released rather than only the ones that failed — on a target that can\n\t * roll back. For apps that must move together.\n\t */\n\tatomic?: boolean;\n\t/**\n\t * Where credentials come from. Defaults to the environment, then the\n\t * logins `gkm login` stored. A credential no provider has stops the run\n\t * with `MissingCredential`; nothing prompts.\n\t */\n\tcredentials?: CredentialProvider;\n\t/**\n\t * On a server target, run a dev service for every construct a deployed\n\t * stage does not account for — no key in its secrets, and no provider\n\t * (`deploy.objects`) backing its kind: MinIO for a bucket, Mailpit for\n\t * mail. Neither is production-grade — Mailpit delivers no mail — and every\n\t * run that uses one emits `dev-service.used`. Without it, a deployed stage\n\t * missing a mail or storage key fails with `ExternalServicesNotConfigured`;\n\t * on an AWS target it fails with `DevServicesNeedServerTarget`.\n\t */\n\tallowDevServices?: boolean;\n\t/**\n\t * Do not check, on a server target, that each public host resolves to\n\t * the stage's server (`GKM_SERVER_IPV4`) before the stack starts — for a CDN\n\t * or proxy in front of the server.\n\t */\n\tskipDnsCheck?: boolean;\n\t/** Receives each progress line as it is written. Defaults to none. */\n\tlogger?: DeployLogger;\n\t/**\n\t * Stops the run: in-flight Dokploy requests and docker children are\n\t * cancelled, the stage's lock is released, and `result` rejects with the\n\t * signal's reason.\n\t */\n\tsignal?: AbortSignal;\n\t/**\n\t * The CLI's home: stage keys and stored logins. Defaults to `GKM_HOME`,\n\t * else `~/.gkm`.\n\t */\n\thome?: string;\n\t/**\n\t * Where docker's own output goes: the host's terminal (`inherit`, the\n\t * default), its stderr, or nowhere.\n\t */\n\tchildOutput?: 'inherit' | 'stderr' | 'ignore';\n\t/**\n\t * Where the project's own code runs: loading `gkm.config.ts`, discovering\n\t * its constructs, sniffing each app's environment. Defaults to a\n\t * `LocalSandbox` on the project — a child process here with an\n\t * allowlisted environment, for a project the host trusts.\n\t *\n\t * A host deploying repositories it does not trust passes one that\n\t * isolates — a container per build — and the config then reaches the\n\t * deploy only as data. Credentials never enter a sandbox: they go from\n\t * `credentials` to the steps that provision, push and release.\n\t */\n\tsandbox?: Sandbox;\n}\n\n/** A deploy in progress. */\nexport interface DeployRun extends AsyncIterable<DeployEvent> {\n\t/**\n\t * What the deploy did, once it is done. Rejects with the error that\n\t * stopped it — `MissingCredential`, `StateLocked`, `ProjectNotOwned`, … —\n\t * after a `deploy.failed` event carrying the same.\n\t */\n\treadonly result: Promise<DeployResult>;\n}\n\n/**\n * Every event a run emits, kept so that each iteration — however late it\n * starts — sees the run from its first event to its last.\n */\nclass EventLog {\n\tprivate readonly events: DeployEvent[] = [];\n\tprivate closed = false;\n\tprivate waiting: (() => void)[] = [];\n\n\tpush(event: DeployEvent): void {\n\t\tthis.events.push(event);\n\t\tthis.wake();\n\t}\n\n\tclose(): void {\n\t\tthis.closed = true;\n\t\tthis.wake();\n\t}\n\n\tprivate wake(): void {\n\t\tconst waiting = this.waiting;\n\t\tthis.waiting = [];\n\t\tfor (const resume of waiting) resume();\n\t}\n\n\tasync *iterate(): AsyncGenerator<DeployEvent> {\n\t\tlet next = 0;\n\t\twhile (true) {\n\t\t\tif (next < this.events.length) {\n\t\t\t\tyield this.events[next++]!;\n\t\t\t} else if (this.closed) {\n\t\t\t\treturn;\n\t\t\t} else {\n\t\t\t\tawait new Promise<void>((resume) => this.waiting.push(resume));\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction forward(\n\tlogger: DeployLogger | undefined,\n\tlevel: OutputLevel,\n\tmessage: string,\n): void {\n\tif (!logger) return;\n\tif (level === 'info') logger.info(message);\n\telse if (level === 'error' && logger.error) logger.error(message);\n\telse logger.warn(message);\n}\n\n/**\n * Deploy a project's stage.\n *\n * ```ts\n * import { deploy } from '@geekmidas/cli/deploy';\n *\n * const run = deploy({ cwd: '/srv/checkouts/shop', stage: 'production' });\n * for await (const event of run) {\n * if (event.type === 'app.deployed') console.log(event.app, event.url);\n * }\n * const result = await run.result;\n * ```\n *\n * The run starts at once; iterating it is optional, and an iteration started\n * late still sees every event from the first.\n */\nexport function deploy(input: DeployInput): DeployRun {\n\tconst log = new EventLog();\n\t// Every secret the run reads is masked in every line it reports — in the\n\t// events and in what the logger is handed.\n\tconst redactor = new Redactor();\n\tconst emit = (event: DeployEvent) => {\n\t\tconst masked =\n\t\t\tevent.type === 'log'\n\t\t\t\t? { ...event, message: redactor.redact(event.message) }\n\t\t\t\t: event;\n\t\tlog.push(masked);\n\t\tif (masked.type === 'log') {\n\t\t\tforward(input.logger, masked.level, masked.message);\n\t\t}\n\t};\n\n\tconst ctx: DeployContext = {\n\t\temit,\n\t\tcredentials:\n\t\t\tinput.credentials ?? storedCredentials({ home: input.home ?? gkmHome() }),\n\t\tdryRun: input.dryRun ?? false,\n\t\t...(input.buildOnly ? { buildOnly: true } : {}),\n\t\thome: input.home ?? gkmHome(),\n\t\tredactor,\n\t\t...(input.signal ? { signal: input.signal } : {}),\n\t\t...(input.childOutput ? { childOutput: input.childOutput } : {}),\n\t\t...(input.targets ? { targets: input.targets } : {}),\n\t};\n\n\t// Every line anything in the run writes through `output` — the deploy's\n\t// own, the Dockerfile generator's, discovery's warnings — becomes an event\n\t// of this run, and of no other.\n\tconst result = withOutput(\n\t\t(level, message) => emit({ type: 'log', level, message }),\n\t\tasync () => {\n\t\t\ttry {\n\t\t\t\tinput.signal?.throwIfAborted();\n\t\t\t\tconst sandbox =\n\t\t\t\t\tinput.sandbox ??\n\t\t\t\t\tnew LocalSandbox({ root: findWorkspaceRoot(resolve(input.cwd)) });\n\t\t\t\t// Every step of the run that executes the project's code — the\n\t\t\t\t// engine's own discoveries and sniffs included — runs in it.\n\t\t\t\tconst deployed = await withSandbox(sandbox, () =>\n\t\t\t\t\trunDeploy(\n\t\t\t\t\t\t() => load(input, sandbox),\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tstage: input.stage,\n\t\t\t\t\t\t\t...(input.target ? { target: input.target } : {}),\n\t\t\t\t\t\t\t...(input.tag ? { tag: input.tag } : {}),\n\t\t\t\t\t\t\t...(input.apps ? { apps: input.apps } : {}),\n\t\t\t\t\t\t\t...(input.atomic ? { atomic: true } : {}),\n\t\t\t\t\t\t\t...(input.allowDevServices\n\t\t\t\t\t\t\t\t? { allowDevServices: input.allowDevServices }\n\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t\t...(input.skipDnsCheck ? { skipDnsCheck: true } : {}),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tctx,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\temit({ type: 'deploy.finished', result: deployed });\n\t\t\t\treturn deployed;\n\t\t\t} catch (error) {\n\t\t\t\temit({ type: 'deploy.failed', error: eventError(error) });\n\t\t\t\tthrow error;\n\t\t\t} finally {\n\t\t\t\tlog.close();\n\t\t\t}\n\t\t},\n\t);\n\n\t// A caller that only iterates still learns of the failure from the\n\t// `deploy.failed` event; the rejection must not also crash its process as\n\t// unhandled. Awaiting `result` still rejects.\n\tresult.catch(() => {});\n\n\treturn {\n\t\tresult,\n\t\t[Symbol.asyncIterator]: () => log.iterate(),\n\t};\n}\n\n/**\n * The workspace at `input.cwd`. The stage is checked by the run, once it\n * knows the target: one that runs on this machine may take the local stage.\n */\nasync function load(\n\tinput: DeployInput,\n\tsandbox: Sandbox,\n): Promise<NormalizedWorkspace> {\n\tconst { workspace } = await loadWorkspaceConfig(resolve(input.cwd), {\n\t\tsandbox,\n\t});\n\treturn workspace;\n}\n"],"mappings":"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"}
|