@geekmidas/cli 10.0.0-alpha.11 → 10.0.0-alpha.111
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/README.md +315 -414
- package/bin/adjacent-tsconfig.mjs +46 -8
- package/bin/gkm.mjs +11 -3
- package/bin/owning-tsconfig-jsx-register.mjs +18 -0
- package/bin/owning-tsconfig-jsx.mjs +108 -0
- package/dist/EndpointGenerator-BwbYTMtB.cjs +1090 -0
- package/dist/EndpointGenerator-BwbYTMtB.cjs.map +1 -0
- package/dist/EndpointGenerator-CvLX8bpx.mjs +1031 -0
- package/dist/EndpointGenerator-CvLX8bpx.mjs.map +1 -0
- package/dist/GoDaddyProvider-CPabc3XW.mjs +388 -0
- package/dist/GoDaddyProvider-CPabc3XW.mjs.map +1 -0
- package/dist/GoDaddyProvider-DWrDIXzQ.cjs +388 -0
- package/dist/GoDaddyProvider-DWrDIXzQ.cjs.map +1 -0
- package/dist/{HostingerProvider-BcyFRbqV.mjs → HostingerProvider-BNpS9aRJ.mjs} +29 -25
- package/dist/HostingerProvider-BNpS9aRJ.mjs.map +1 -0
- package/dist/{HostingerProvider-BOvsgATO.cjs → HostingerProvider-DR1iws1P.cjs} +29 -25
- package/dist/HostingerProvider-DR1iws1P.cjs.map +1 -0
- package/dist/LegacyStateStore-BdXoAlYY.mjs +124 -0
- package/dist/LegacyStateStore-BdXoAlYY.mjs.map +1 -0
- package/dist/LegacyStateStore-fsyPGBWu.cjs +124 -0
- package/dist/LegacyStateStore-fsyPGBWu.cjs.map +1 -0
- package/dist/LocalStateStore-AtviUBky.cjs +178 -0
- package/dist/LocalStateStore-AtviUBky.cjs.map +1 -0
- package/dist/LocalStateStore-BrAX5yy7.mjs +167 -0
- package/dist/LocalStateStore-BrAX5yy7.mjs.map +1 -0
- package/dist/OpenApiTsGenerator-BD6ZOQ51.cjs +541 -0
- package/dist/OpenApiTsGenerator-BD6ZOQ51.cjs.map +1 -0
- package/dist/OpenApiTsGenerator-DXXFXSbO.mjs +536 -0
- package/dist/OpenApiTsGenerator-DXXFXSbO.mjs.map +1 -0
- package/dist/{Route53Provider-CH9XH7bz.cjs → Route53Provider-Bllus2NR.cjs} +21 -22
- package/dist/Route53Provider-Bllus2NR.cjs.map +1 -0
- package/dist/{Route53Provider-C0xmer8U.mjs → Route53Provider-QXxjkZO1.mjs} +13 -13
- package/dist/Route53Provider-QXxjkZO1.mjs.map +1 -0
- package/dist/S3StateStore-Cx7njkBz.mjs +223 -0
- package/dist/S3StateStore-Cx7njkBz.mjs.map +1 -0
- package/dist/S3StateStore-tn5g80Sx.cjs +222 -0
- package/dist/S3StateStore-tn5g80Sx.cjs.map +1 -0
- package/dist/SSMStateStore-CAFde-Fq.cjs +204 -0
- package/dist/SSMStateStore-CAFde-Fq.cjs.map +1 -0
- package/dist/SSMStateStore-sqZ3oDmn.mjs +205 -0
- package/dist/SSMStateStore-sqZ3oDmn.mjs.map +1 -0
- package/dist/SubscriberGenerator-CeH7wbYt.cjs +571 -0
- package/dist/SubscriberGenerator-CeH7wbYt.cjs.map +1 -0
- package/dist/SubscriberGenerator-DHnIYwUU.mjs +518 -0
- package/dist/SubscriberGenerator-DHnIYwUU.mjs.map +1 -0
- package/dist/add-Cs8yT5iH.cjs +719 -0
- package/dist/add-Cs8yT5iH.cjs.map +1 -0
- package/dist/add-DJE7zqJ4.mjs +717 -0
- package/dist/add-DJE7zqJ4.mjs.map +1 -0
- package/dist/appPorts-BoXMwolc.mjs +249 -0
- package/dist/appPorts-BoXMwolc.mjs.map +1 -0
- package/dist/appPorts-IW-OZmRi.cjs +296 -0
- package/dist/appPorts-IW-OZmRi.cjs.map +1 -0
- package/dist/aws-8QQ8n-t6.mjs +89 -0
- package/dist/aws-8QQ8n-t6.mjs.map +1 -0
- package/dist/aws-DY9NqQIT.cjs +100 -0
- package/dist/aws-DY9NqQIT.cjs.map +1 -0
- package/dist/awsStore-Bx5nCFnS.mjs +121 -0
- package/dist/awsStore-Bx5nCFnS.mjs.map +1 -0
- package/dist/awsStore-u3ehhMoY.cjs +138 -0
- package/dist/awsStore-u3ehhMoY.cjs.map +1 -0
- package/dist/{backup-provisioner-DXXg6FIL.mjs → backup-provisioner-C8DnCmxK.mjs} +11 -8
- package/dist/backup-provisioner-C8DnCmxK.mjs.map +1 -0
- package/dist/{backup-provisioner-cE2PuCTb.cjs → backup-provisioner-oDEsPj02.cjs} +22 -20
- package/dist/backup-provisioner-oDEsPj02.cjs.map +1 -0
- package/dist/backups/runner.cjs +519 -0
- package/dist/backups/runner.cjs.map +1 -0
- package/dist/backups/runner.d.cts +102 -0
- package/dist/backups/runner.d.cts.map +1 -0
- package/dist/backups/runner.d.mts +102 -0
- package/dist/backups/runner.d.mts.map +1 -0
- package/dist/backups/runner.mjs +506 -0
- package/dist/backups/runner.mjs.map +1 -0
- package/dist/{bundler-S3ek7ea-.mjs → bundler-DrySVbXF.mjs} +36 -32
- package/dist/bundler-DrySVbXF.mjs.map +1 -0
- package/dist/{bundler-CaTGtiaO.cjs → bundler-NrtItVBp.cjs} +38 -35
- package/dist/bundler-NrtItVBp.cjs.map +1 -0
- package/dist/cli-Br5ApIgR.mjs +71 -0
- package/dist/cli-Br5ApIgR.mjs.map +1 -0
- package/dist/cli-CHzpFBh-.cjs +71 -0
- package/dist/cli-CHzpFBh-.cjs.map +1 -0
- package/dist/compose-ByIbIrzs.cjs +3345 -0
- package/dist/compose-ByIbIrzs.cjs.map +1 -0
- package/dist/compose-CDC2bCme.mjs +3202 -0
- package/dist/compose-CDC2bCme.mjs.map +1 -0
- package/dist/config-D48erO2j.d.mts +269 -0
- package/dist/config-D48erO2j.d.mts.map +1 -0
- package/dist/config-DHDtsQrB.d.cts +269 -0
- package/dist/config-DHDtsQrB.d.cts.map +1 -0
- package/dist/config.cjs +421 -11
- package/dist/config.cjs.map +1 -0
- package/dist/config.d.cts +4 -120
- package/dist/config.d.mts +4 -120
- package/dist/config.mjs +405 -4
- package/dist/config.mjs.map +1 -0
- package/dist/credentialSchemas-Cf5Pf8VE.mjs +237 -0
- package/dist/credentialSchemas-Cf5Pf8VE.mjs.map +1 -0
- package/dist/credentialSchemas-D_VUCDUh.cjs +272 -0
- package/dist/credentialSchemas-D_VUCDUh.cjs.map +1 -0
- package/dist/credentials-B5N5yKk6.mjs +267 -0
- package/dist/credentials-B5N5yKk6.mjs.map +1 -0
- package/dist/credentials-Zqr3QQYB.cjs +350 -0
- package/dist/credentials-Zqr3QQYB.cjs.map +1 -0
- package/dist/databases-BeGP5eDh.mjs +495 -0
- package/dist/databases-BeGP5eDh.mjs.map +1 -0
- package/dist/databases-DGALFDG3.cjs +598 -0
- package/dist/databases-DGALFDG3.cjs.map +1 -0
- package/dist/define-6poNqm5O.mjs +106 -0
- package/dist/define-6poNqm5O.mjs.map +1 -0
- package/dist/define-crmMcXFS.cjs +153 -0
- package/dist/define-crmMcXFS.cjs.map +1 -0
- package/dist/deploy/public.cjs +212 -0
- package/dist/deploy/public.cjs.map +1 -0
- package/dist/deploy/public.d.cts +653 -0
- package/dist/deploy/public.d.cts.map +1 -0
- package/dist/deploy/public.d.mts +653 -0
- package/dist/deploy/public.d.mts.map +1 -0
- package/dist/deploy/public.mjs +145 -0
- package/dist/deploy/public.mjs.map +1 -0
- package/dist/deploy/sniffer-envkit-patch.cjs +18 -10
- package/dist/deploy/sniffer-envkit-patch.cjs.map +1 -1
- package/dist/deploy/sniffer-envkit-patch.d.cts +0 -2
- package/dist/deploy/sniffer-envkit-patch.d.cts.map +1 -1
- package/dist/deploy/sniffer-envkit-patch.d.mts +0 -2
- package/dist/deploy/sniffer-envkit-patch.d.mts.map +1 -1
- package/dist/deploy/sniffer-envkit-patch.mjs +10 -2
- package/dist/deploy/sniffer-envkit-patch.mjs.map +1 -1
- package/dist/deploy/sniffer-envparser-worker.cjs +54 -0
- package/dist/deploy/sniffer-envparser-worker.cjs.map +1 -0
- package/dist/deploy/sniffer-envparser-worker.d.cts +1 -0
- package/dist/deploy/sniffer-envparser-worker.d.mts +1 -0
- package/dist/deploy/sniffer-envparser-worker.mjs +55 -0
- package/dist/deploy/sniffer-envparser-worker.mjs.map +1 -0
- package/dist/deploy/sniffer-hooks.cjs +13 -7
- package/dist/deploy/sniffer-hooks.cjs.map +1 -1
- package/dist/deploy/sniffer-hooks.d.cts +1 -2
- package/dist/deploy/sniffer-hooks.d.cts.map +1 -1
- package/dist/deploy/sniffer-hooks.d.mts +1 -2
- package/dist/deploy/sniffer-hooks.d.mts.map +1 -1
- package/dist/deploy/sniffer-hooks.mjs +10 -3
- package/dist/deploy/sniffer-hooks.mjs.map +1 -1
- package/dist/deploy/sniffer-loader.cjs +16 -7
- package/dist/deploy/sniffer-loader.cjs.map +1 -1
- package/dist/deploy/sniffer-loader.d.cts +1 -1
- package/dist/deploy/sniffer-loader.d.mts +1 -1
- package/dist/deploy/sniffer-loader.mjs +14 -3
- package/dist/deploy/sniffer-loader.mjs.map +1 -1
- package/dist/deploy/sniffer-routes-worker.cjs +11 -11
- package/dist/deploy/sniffer-routes-worker.cjs.map +1 -1
- package/dist/deploy/sniffer-routes-worker.d.cts +1 -1
- package/dist/deploy/sniffer-routes-worker.d.mts +1 -1
- package/dist/deploy/sniffer-routes-worker.mjs +4 -4
- package/dist/deploy/sniffer-routes-worker.mjs.map +1 -1
- package/dist/deploy/sniffer-worker.cjs +15 -6
- package/dist/deploy/sniffer-worker.cjs.map +1 -1
- package/dist/deploy/sniffer-worker.d.cts +1 -3
- package/dist/deploy/sniffer-worker.d.cts.map +1 -1
- package/dist/deploy/sniffer-worker.d.mts +1 -3
- package/dist/deploy/sniffer-worker.d.mts.map +1 -1
- package/dist/deploy/sniffer-worker.mjs +15 -4
- package/dist/deploy/sniffer-worker.mjs.map +1 -1
- package/dist/deploy-DLq9-m6H.mjs +2317 -0
- package/dist/deploy-DLq9-m6H.mjs.map +1 -0
- package/dist/deploy-Dl_NavYE.cjs +2390 -0
- package/dist/deploy-Dl_NavYE.cjs.map +1 -0
- package/dist/derive-B4vDpzgP.mjs +150 -0
- package/dist/derive-B4vDpzgP.mjs.map +1 -0
- package/dist/derive-DAbo49sm.cjs +161 -0
- package/dist/derive-DAbo49sm.cjs.map +1 -0
- package/dist/discover-Bsr7HjqJ.cjs +616 -0
- package/dist/discover-Bsr7HjqJ.cjs.map +1 -0
- package/dist/discover-Ca8U75dl.mjs +537 -0
- package/dist/discover-Ca8U75dl.mjs.map +1 -0
- package/dist/discover-CvTtV5ZD.d.cts +108 -0
- package/dist/discover-CvTtV5ZD.d.cts.map +1 -0
- package/dist/discover-W2kab9Lc.d.mts +108 -0
- package/dist/discover-W2kab9Lc.d.mts.map +1 -0
- package/dist/dns-C2TGXUF4.cjs +1057 -0
- package/dist/dns-C2TGXUF4.cjs.map +1 -0
- package/dist/dns-pzftumxg.mjs +926 -0
- package/dist/dns-pzftumxg.mjs.map +1 -0
- package/dist/dokploy-B7-OwY2s.mjs +38 -0
- package/dist/dokploy-B7-OwY2s.mjs.map +1 -0
- package/dist/dokploy-OqBh8G4g.cjs +38 -0
- package/dist/dokploy-OqBh8G4g.cjs.map +1 -0
- package/dist/drivers-D11JHxrD.mjs +177 -0
- package/dist/drivers-D11JHxrD.mjs.map +1 -0
- package/dist/drivers-GKaA6ITM.cjs +200 -0
- package/dist/drivers-GKaA6ITM.cjs.map +1 -0
- package/dist/eas-6ezdJtry.mjs +316 -0
- package/dist/eas-6ezdJtry.mjs.map +1 -0
- package/dist/eas-BkLA60P2.cjs +319 -0
- package/dist/eas-BkLA60P2.cjs.map +1 -0
- package/dist/{emit-CIlvnDvV.mjs → emit-C1e8qD08.mjs} +13 -26
- package/dist/emit-C1e8qD08.mjs.map +1 -0
- package/dist/{emit-CUD1BWls.cjs → emit-Dmc1nvdS.cjs} +31 -57
- package/dist/emit-Dmc1nvdS.cjs.map +1 -0
- package/dist/encryption-DJv_jEXi.mjs +89 -0
- package/dist/encryption-DJv_jEXi.mjs.map +1 -0
- package/dist/encryption-DoVnVU82.cjs +90 -0
- package/dist/encryption-DoVnVU82.cjs.map +1 -0
- package/dist/engine-CJhQb50j.cjs +3196 -0
- package/dist/engine-CJhQb50j.cjs.map +1 -0
- package/dist/engine-Db71-OZL.mjs +3083 -0
- package/dist/engine-Db71-OZL.mjs.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-D2eABlxM.mjs +829 -0
- package/dist/github-D2eABlxM.mjs.map +1 -0
- package/dist/github-DDOHjdS9.cjs +829 -0
- package/dist/github-DDOHjdS9.cjs.map +1 -0
- package/dist/googlePlay-DNgybe5t.cjs +931 -0
- package/dist/googlePlay-DNgybe5t.cjs.map +1 -0
- package/dist/googlePlay-DY9vZQ1p.mjs +931 -0
- package/dist/googlePlay-DY9vZQ1p.mjs.map +1 -0
- package/dist/identity-CBXr4wNi.mjs +129 -0
- package/dist/identity-CBXr4wNi.mjs.map +1 -0
- package/dist/identity-CbLfgQNT.cjs +182 -0
- package/dist/identity-CbLfgQNT.cjs.map +1 -0
- package/dist/{index-Ayxg_DQY.d.mts → index-8OfpOtSI.d.cts} +39 -24
- package/dist/index-8OfpOtSI.d.cts.map +1 -0
- package/dist/{index-CR5urhWX.d.cts → index-Dpji6Bxq.d.mts} +39 -24
- package/dist/index-Dpji6Bxq.d.mts.map +1 -0
- package/dist/index.cjs +13571 -12366
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +14106 -12914
- package/dist/index.mjs.map +1 -1
- package/dist/local-BwC2v2Y8.mjs +224 -0
- package/dist/local-BwC2v2Y8.mjs.map +1 -0
- package/dist/local-DNhnkugT.cjs +241 -0
- package/dist/local-DNhnkugT.cjs.map +1 -0
- package/dist/migrate-B9GswIAr.mjs +266 -0
- package/dist/migrate-B9GswIAr.mjs.map +1 -0
- package/dist/migrate-CQUGbptv.mjs +181 -0
- package/dist/migrate-CQUGbptv.mjs.map +1 -0
- package/dist/migrate-GLBZlrlJ.cjs +181 -0
- package/dist/migrate-GLBZlrlJ.cjs.map +1 -0
- package/dist/migrate-bDQzFvBN.cjs +271 -0
- package/dist/migrate-bDQzFvBN.cjs.map +1 -0
- package/dist/openapi.cjs +214 -10
- package/dist/openapi.cjs.map +1 -0
- package/dist/openapi.d.cts +38 -17
- package/dist/openapi.d.cts.map +1 -1
- package/dist/openapi.d.mts +38 -17
- package/dist/openapi.d.mts.map +1 -1
- package/dist/openapi.mjs +208 -5
- package/dist/openapi.mjs.map +1 -0
- package/dist/projectBucket-BBbQ1lHF.cjs +263 -0
- package/dist/projectBucket-BBbQ1lHF.cjs.map +1 -0
- package/dist/projectBucket-C_DN4WzF.mjs +222 -0
- package/dist/projectBucket-C_DN4WzF.mjs.map +1 -0
- package/dist/provider-BqpjG9QM.cjs +57 -0
- package/dist/provider-BqpjG9QM.cjs.map +1 -0
- package/dist/provider-CEOKk8NS.mjs +46 -0
- package/dist/provider-CEOKk8NS.mjs.map +1 -0
- package/dist/publish-B-EIjfQ0.mjs +123 -0
- package/dist/publish-B-EIjfQ0.mjs.map +1 -0
- package/dist/publish-CPgSbtcs.cjs +124 -0
- package/dist/publish-CPgSbtcs.cjs.map +1 -0
- package/dist/ready-BMFjxBYA.cjs +404 -0
- package/dist/ready-BMFjxBYA.cjs.map +1 -0
- package/dist/ready-CIt5SOtg.mjs +375 -0
- package/dist/ready-CIt5SOtg.mjs.map +1 -0
- package/dist/reconcile/public.cjs +3 -5
- package/dist/reconcile/public.d.cts +11 -87
- package/dist/reconcile/public.d.cts.map +1 -1
- package/dist/reconcile/public.d.mts +11 -87
- package/dist/reconcile/public.d.mts.map +1 -1
- package/dist/reconcile/public.mjs +3 -4
- package/dist/{reconcile-C13ERt1U.mjs → reconcile--I7V4Klj.mjs} +4 -7
- package/dist/reconcile--I7V4Klj.mjs.map +1 -0
- package/dist/{reconcile-CYMhFSYy.cjs → reconcile-CuCQBa5-.cjs} +5 -8
- package/dist/reconcile-CuCQBa5-.cjs.map +1 -0
- package/dist/resolve-CxDqDmCD.mjs +263 -0
- package/dist/resolve-CxDqDmCD.mjs.map +1 -0
- package/dist/resolve-wG49Woex.cjs +316 -0
- package/dist/resolve-wG49Woex.cjs.map +1 -0
- package/dist/rollback-BQgEdr_p.mjs +136 -0
- package/dist/rollback-BQgEdr_p.mjs.map +1 -0
- package/dist/rollback-Cw-Z0mWn.cjs +165 -0
- package/dist/rollback-Cw-Z0mWn.cjs.map +1 -0
- package/dist/rolldown-runtime-BhDjJH2R.mjs +15 -0
- package/dist/{chunk-CUT6urMc.cjs → rolldown-runtime-C0BPl7ul.cjs} +23 -10
- package/dist/run-BJLIerMM.mjs +133 -0
- package/dist/run-BJLIerMM.mjs.map +1 -0
- package/dist/run-fVZ-rLnc.cjs +174 -0
- package/dist/run-fVZ-rLnc.cjs.map +1 -0
- package/dist/runtime-ACdfK59n.d.cts +82 -0
- package/dist/runtime-ACdfK59n.d.cts.map +1 -0
- package/dist/runtime-YyQuekxi.d.mts +82 -0
- package/dist/runtime-YyQuekxi.d.mts.map +1 -0
- package/dist/s3-CrPoA89T.cjs +215 -0
- package/dist/s3-CrPoA89T.cjs.map +1 -0
- package/dist/s3-DQXZdmCM.mjs +215 -0
- package/dist/s3-DQXZdmCM.mjs.map +1 -0
- package/dist/sandbox/config-worker.cjs +43 -0
- package/dist/sandbox/config-worker.cjs.map +1 -0
- package/dist/sandbox/config-worker.d.cts +1 -0
- package/dist/sandbox/config-worker.d.mts +1 -0
- package/dist/sandbox/config-worker.mjs +44 -0
- package/dist/sandbox/config-worker.mjs.map +1 -0
- package/dist/sandbox/credentials-worker.cjs +54 -0
- package/dist/sandbox/credentials-worker.cjs.map +1 -0
- package/dist/sandbox/credentials-worker.d.cts +1 -0
- package/dist/sandbox/credentials-worker.d.mts +1 -0
- package/dist/sandbox/credentials-worker.mjs +55 -0
- package/dist/sandbox/credentials-worker.mjs.map +1 -0
- package/dist/sandbox/discover-worker.cjs +47 -0
- package/dist/sandbox/discover-worker.cjs.map +1 -0
- package/dist/sandbox/discover-worker.d.cts +1 -0
- package/dist/sandbox/discover-worker.d.mts +1 -0
- package/dist/sandbox/discover-worker.mjs +48 -0
- package/dist/sandbox/discover-worker.mjs.map +1 -0
- package/dist/sandbox/migrate-worker.cjs +75 -0
- package/dist/sandbox/migrate-worker.cjs.map +1 -0
- package/dist/sandbox/migrate-worker.d.cts +1 -0
- package/dist/sandbox/migrate-worker.d.mts +1 -0
- package/dist/sandbox/migrate-worker.mjs +76 -0
- package/dist/sandbox/migrate-worker.mjs.map +1 -0
- package/dist/sandbox-C4g1eG31.d.mts +104 -0
- package/dist/sandbox-C4g1eG31.d.mts.map +1 -0
- package/dist/sandbox-CHv3OYXg.cjs +165 -0
- package/dist/sandbox-CHv3OYXg.cjs.map +1 -0
- package/dist/sandbox-CbVB7uOD.mjs +118 -0
- package/dist/sandbox-CbVB7uOD.mjs.map +1 -0
- package/dist/sandbox-CdKgQ3XU.d.cts +104 -0
- package/dist/sandbox-CdKgQ3XU.d.cts.map +1 -0
- package/dist/{workspace-C-a3G_U0.cjs → schemas-BCwKKA42.mjs} +958 -1868
- package/dist/schemas-BCwKKA42.mjs.map +1 -0
- package/dist/{workspace-DaEX7FEg.mjs → schemas-BQ_v0lEX.cjs} +1048 -1678
- package/dist/schemas-BQ_v0lEX.cjs.map +1 -0
- package/dist/secretsManager-CQORubp9.mjs +97 -0
- package/dist/secretsManager-CQORubp9.mjs.map +1 -0
- package/dist/secretsManager-RAGcEDX1.cjs +108 -0
- package/dist/secretsManager-RAGcEDX1.cjs.map +1 -0
- package/dist/seeds-CIk0l4SC.mjs +8197 -0
- package/dist/seeds-CIk0l4SC.mjs.map +1 -0
- package/dist/seeds-Dk8YkLkM.cjs +9176 -0
- package/dist/seeds-Dk8YkLkM.cjs.map +1 -0
- package/dist/sniffer-2aK7cB2u.cjs +1998 -0
- package/dist/sniffer-2aK7cB2u.cjs.map +1 -0
- package/dist/sniffer-CABMO-Bh.mjs +1825 -0
- package/dist/sniffer-CABMO-Bh.mjs.map +1 -0
- package/dist/sst-B-Uz4cvm.cjs +464 -0
- package/dist/sst-B-Uz4cvm.cjs.map +1 -0
- package/dist/sst-C1pxbFEd.mjs +441 -0
- package/dist/sst-C1pxbFEd.mjs.map +1 -0
- package/dist/stage-DjxR_7C0.mjs +302 -0
- package/dist/stage-DjxR_7C0.mjs.map +1 -0
- package/dist/stage-DrCS0vKD.cjs +313 -0
- package/dist/stage-DrCS0vKD.cjs.map +1 -0
- package/dist/stages-DJrOYLZ-.mjs +176 -0
- package/dist/stages-DJrOYLZ-.mjs.map +1 -0
- package/dist/stages-_3RVNG83.cjs +176 -0
- package/dist/stages-_3RVNG83.cjs.map +1 -0
- package/dist/target/public.cjs +15 -0
- package/dist/target/public.d.cts +28 -0
- package/dist/target/public.d.cts.map +1 -0
- package/dist/target/public.d.mts +28 -0
- package/dist/target/public.d.mts.map +1 -0
- package/dist/target/public.mjs +4 -0
- package/dist/terminal-D_NRPW4W.cjs +85 -0
- package/dist/terminal-D_NRPW4W.cjs.map +1 -0
- package/dist/terminal-mXM-Lqc6.mjs +85 -0
- package/dist/terminal-mXM-Lqc6.mjs.map +1 -0
- package/dist/types-B6UXlEkf.d.cts +7650 -0
- package/dist/types-B6UXlEkf.d.cts.map +1 -0
- package/dist/types-HqVPddRK.d.mts +7649 -0
- package/dist/types-HqVPddRK.d.mts.map +1 -0
- package/dist/utils-D2Nk1Nv2.cjs +205 -0
- package/dist/utils-D2Nk1Nv2.cjs.map +1 -0
- package/dist/utils-D_fxUqvU.mjs +156 -0
- package/dist/utils-D_fxUqvU.mjs.map +1 -0
- package/dist/vitest.cjs +65 -0
- package/dist/vitest.cjs.map +1 -0
- package/dist/vitest.d.cts +29 -0
- package/dist/vitest.d.cts.map +1 -0
- package/dist/vitest.d.mts +29 -0
- package/dist/vitest.d.mts.map +1 -0
- package/dist/vitest.mjs +65 -0
- package/dist/vitest.mjs.map +1 -0
- package/dist/workerRuntime-BXfhbe13.cjs +108 -0
- package/dist/workerRuntime-BXfhbe13.cjs.map +1 -0
- package/dist/workerRuntime-CsOyBRx2.mjs +79 -0
- package/dist/workerRuntime-CsOyBRx2.mjs.map +1 -0
- package/dist/workspace/index.cjs +8 -4
- package/dist/workspace/index.d.cts +3 -3
- package/dist/workspace/index.d.mts +3 -3
- package/dist/workspace/index.mjs +2 -3
- package/dist/workspace-BwqQnool.cjs +2806 -0
- package/dist/workspace-BwqQnool.cjs.map +1 -0
- package/dist/workspace-D1QbNd55.mjs +2394 -0
- package/dist/workspace-D1QbNd55.mjs.map +1 -0
- package/package.json +54 -22
- package/dist/CachedStateProvider-BEBl2wQT.cjs +0 -60
- package/dist/CachedStateProvider-BEBl2wQT.cjs.map +0 -1
- package/dist/CachedStateProvider-C-8wtlto.mjs +0 -3
- package/dist/CachedStateProvider-Co4n5Wvs.mjs +0 -54
- package/dist/CachedStateProvider-Co4n5Wvs.mjs.map +0 -1
- package/dist/CachedStateProvider-DqhGpzsm.cjs +0 -3
- package/dist/HostingerProvider-BOvsgATO.cjs.map +0 -1
- package/dist/HostingerProvider-BcyFRbqV.mjs.map +0 -1
- package/dist/LocalStateProvider-Bq-0C0sg.mjs +0 -42
- package/dist/LocalStateProvider-Bq-0C0sg.mjs.map +0 -1
- package/dist/LocalStateProvider-yjXZXZDM.cjs +0 -43
- package/dist/LocalStateProvider-yjXZXZDM.cjs.map +0 -1
- package/dist/Route53Provider-C0xmer8U.mjs.map +0 -1
- package/dist/Route53Provider-CH9XH7bz.cjs.map +0 -1
- package/dist/SSMStateProvider-CXtNgPfy.cjs +0 -66
- package/dist/SSMStateProvider-CXtNgPfy.cjs.map +0 -1
- package/dist/SSMStateProvider-DOZifZfE.mjs +0 -66
- package/dist/SSMStateProvider-DOZifZfE.mjs.map +0 -1
- package/dist/backup-provisioner-DXXg6FIL.mjs.map +0 -1
- package/dist/backup-provisioner-cE2PuCTb.cjs.map +0 -1
- package/dist/bundler-CaTGtiaO.cjs.map +0 -1
- package/dist/bundler-S3ek7ea-.mjs.map +0 -1
- package/dist/chunk-Duj1WY3L.mjs +0 -7
- package/dist/config-B0L2tvvC.mjs +0 -402
- package/dist/config-B0L2tvvC.mjs.map +0 -1
- package/dist/config-HhDpJmLO.cjs +0 -462
- package/dist/config-HhDpJmLO.cjs.map +0 -1
- package/dist/config.d.cts.map +0 -1
- package/dist/config.d.mts.map +0 -1
- package/dist/credentials-B2Pyh3bJ.cjs +0 -2681
- package/dist/credentials-B2Pyh3bJ.cjs.map +0 -1
- package/dist/credentials-B5zRHdAl.mjs +0 -141
- package/dist/credentials-B5zRHdAl.mjs.map +0 -1
- package/dist/credentials-BapzjyV5.cjs +0 -195
- package/dist/credentials-BapzjyV5.cjs.map +0 -1
- package/dist/credentials-CZrys9C5.mjs +0 -2549
- package/dist/credentials-CZrys9C5.mjs.map +0 -1
- package/dist/discover-CAlFgYec.mjs +0 -229
- package/dist/discover-CAlFgYec.mjs.map +0 -1
- package/dist/discover-DTmFeloL.cjs +0 -271
- package/dist/discover-DTmFeloL.cjs.map +0 -1
- package/dist/dokploy-api-5cK2InBm.cjs +0 -3
- package/dist/dokploy-api-D2Ny4Jrc.mjs +0 -3
- package/dist/dokploy-api-pdaBryoY.mjs +0 -706
- package/dist/dokploy-api-pdaBryoY.mjs.map +0 -1
- package/dist/dokploy-api-zqKe9lHH.cjs +0 -718
- package/dist/dokploy-api-zqKe9lHH.cjs.map +0 -1
- package/dist/emit-CIlvnDvV.mjs.map +0 -1
- package/dist/emit-CUD1BWls.cjs.map +0 -1
- package/dist/encryption-D2pL3tHH.cjs +0 -54
- package/dist/encryption-D2pL3tHH.cjs.map +0 -1
- package/dist/encryption-YDsXXi6w.mjs +0 -3
- package/dist/encryption-_4zwjOAR.mjs +0 -42
- package/dist/encryption-_4zwjOAR.mjs.map +0 -1
- package/dist/encryption-sQ2Oxx0d.cjs +0 -4
- package/dist/fullstack-secrets-D6MyHhQP.cjs +0 -111
- package/dist/fullstack-secrets-D6MyHhQP.cjs.map +0 -1
- package/dist/fullstack-secrets-METaRLS1.mjs +0 -87
- package/dist/fullstack-secrets-METaRLS1.mjs.map +0 -1
- package/dist/generator-CNzBw7g5.cjs +0 -289
- package/dist/generator-CNzBw7g5.cjs.map +0 -1
- package/dist/generator-VE2JrFlY.mjs +0 -253
- package/dist/generator-VE2JrFlY.mjs.map +0 -1
- package/dist/github-BrvaPkFf.cjs +0 -197
- package/dist/github-BrvaPkFf.cjs.map +0 -1
- package/dist/github-CrpsXSfV.mjs +0 -196
- package/dist/github-CrpsXSfV.mjs.map +0 -1
- package/dist/index-Ayxg_DQY.d.mts.map +0 -1
- package/dist/index-CR5urhWX.d.cts.map +0 -1
- package/dist/keystore-C9rint2E.cjs +0 -101
- package/dist/keystore-C9rint2E.cjs.map +0 -1
- package/dist/keystore-TxeYM7an.mjs +0 -83
- package/dist/keystore-TxeYM7an.mjs.map +0 -1
- package/dist/openapi-DaKlTKxz.cjs +0 -2075
- package/dist/openapi-DaKlTKxz.cjs.map +0 -1
- package/dist/openapi-react-query-BKH334Ug.cjs +0 -173
- package/dist/openapi-react-query-BKH334Ug.cjs.map +0 -1
- package/dist/openapi-react-query-DoR5CmzQ.mjs +0 -167
- package/dist/openapi-react-query-DoR5CmzQ.mjs.map +0 -1
- package/dist/openapi-react-query.cjs +0 -4
- package/dist/openapi-react-query.d.cts +0 -11
- package/dist/openapi-react-query.d.cts.map +0 -1
- package/dist/openapi-react-query.d.mts +0 -11
- package/dist/openapi-react-query.d.mts.map +0 -1
- package/dist/openapi-react-query.mjs +0 -4
- package/dist/openapi-y1d7Kalt.mjs +0 -2021
- package/dist/openapi-y1d7Kalt.mjs.map +0 -1
- package/dist/reconcile-C13ERt1U.mjs.map +0 -1
- package/dist/reconcile-CYMhFSYy.cjs.map +0 -1
- package/dist/setup-BWgMs4gg.mjs +0 -12
- package/dist/setup-DOv92pIu.cjs +0 -473
- package/dist/setup-DOv92pIu.cjs.map +0 -1
- package/dist/setup-DQo_h_yK.mjs +0 -443
- package/dist/setup-DQo_h_yK.mjs.map +0 -1
- package/dist/setup-lCx_DcUo.cjs +0 -12
- package/dist/storage-B020InPW.mjs +0 -222
- package/dist/storage-B020InPW.mjs.map +0 -1
- package/dist/storage-CSNplaJn.cjs +0 -282
- package/dist/storage-CSNplaJn.cjs.map +0 -1
- package/dist/storage-DGBzMLZa.mjs +0 -4
- package/dist/storage-DlVol_oj.cjs +0 -8
- package/dist/sync-0ILCb-1S.cjs +0 -93
- package/dist/sync-0ILCb-1S.cjs.map +0 -1
- package/dist/sync-CzHXiR9c.mjs +0 -76
- package/dist/sync-CzHXiR9c.mjs.map +0 -1
- package/dist/sync-DYENfOt4.cjs +0 -4
- package/dist/sync-HAau6h_R.mjs +0 -3
- package/dist/types-CL7jPFM2.d.mts +0 -2220
- package/dist/types-CL7jPFM2.d.mts.map +0 -1
- package/dist/types-Y3fKQSAK.d.cts +0 -2220
- package/dist/types-Y3fKQSAK.d.cts.map +0 -1
- package/dist/workspace-C-a3G_U0.cjs.map +0 -1
- package/dist/workspace-DQxxs7Jk.cjs +0 -10
- package/dist/workspace-DaEX7FEg.mjs.map +0 -1
- package/dist/workspace-DbI580qS.mjs +0 -9
|
@@ -0,0 +1,2806 @@
|
|
|
1
|
+
const require_errors = require("./errors-DMC4Umsb.cjs");
|
|
2
|
+
const require_schemas = require("./schemas-BQ_v0lEX.cjs");
|
|
3
|
+
const require_define = require("./define-crmMcXFS.cjs");
|
|
4
|
+
let node_path = require("node:path");
|
|
5
|
+
let node_net = require("node:net");
|
|
6
|
+
//#region src/compose/dnsConfig.ts
|
|
7
|
+
/**
|
|
8
|
+
* `dns['<root domain>'].records` — how a domain's hosts point at a compose
|
|
9
|
+
* stage's server — checked and read. Pure, and kept apart from the DNS step
|
|
10
|
+
* so the workspace schema checks a config with the same rules and the same
|
|
11
|
+
* named errors the step applies.
|
|
12
|
+
*/
|
|
13
|
+
/** The stage-secret key holding the server's public IPv4 address. */
|
|
14
|
+
const SERVER_IPV4_KEY = "GKM_SERVER_IPV4";
|
|
15
|
+
/** The stage-secret key holding the server's public IPv6 address, if any. */
|
|
16
|
+
const SERVER_IPV6_KEY = "GKM_SERVER_IPV6";
|
|
17
|
+
/**
|
|
18
|
+
* Whether a stage secret is gkm's own — the server's address — and so never
|
|
19
|
+
* written into an app's or a worker's environment.
|
|
20
|
+
*/
|
|
21
|
+
function isReservedStageKey(key) {
|
|
22
|
+
return key.startsWith("GKM_SERVER_");
|
|
23
|
+
}
|
|
24
|
+
/** A DNS hostname: dot-separated labels of letters, digits and inner '-'. */
|
|
25
|
+
const HOSTNAME = /^(?=.{1,253}$)([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)(\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+$/i;
|
|
26
|
+
/** Whether `value` is a DNS hostname with at least two labels. */
|
|
27
|
+
function isHostname(value) {
|
|
28
|
+
return typeof value === "string" && HOSTNAME.test(value);
|
|
29
|
+
}
|
|
30
|
+
/** A CNAME target that is not a hostname under its own domain. */
|
|
31
|
+
var DnsTargetInvalid = class extends require_errors.GkmError {
|
|
32
|
+
domain;
|
|
33
|
+
target;
|
|
34
|
+
stage;
|
|
35
|
+
constructor(domain, target, stage) {
|
|
36
|
+
super(`dns['${domain}'].records.target${stage ? ` for '${stage}'` : ""} is ${JSON.stringify(target)}, which is not a hostname under ${domain}. Every host's CNAME points at it and gkm writes its A record, so it must be a name in the same zone — like 'server.${domain}'.`);
|
|
37
|
+
this.domain = domain;
|
|
38
|
+
this.target = target;
|
|
39
|
+
this.stage = stage;
|
|
40
|
+
this.name = "DnsTargetInvalid";
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
/** The CNAME target names under one domain, by the stage they are for, checked. */
|
|
44
|
+
function targets(records) {
|
|
45
|
+
const { target } = records;
|
|
46
|
+
if (target && typeof target === "object" && !Array.isArray(target)) return Object.entries(target);
|
|
47
|
+
return [[void 0, target]];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* One domain's records mode checked.
|
|
51
|
+
*
|
|
52
|
+
* @throws {DnsTargetInvalid} for a target that is not a hostname under it
|
|
53
|
+
*/
|
|
54
|
+
function checkDnsRecordsMode(domain, records) {
|
|
55
|
+
if (!records || typeof records !== "object") return;
|
|
56
|
+
const mode = records;
|
|
57
|
+
if (mode.mode !== "cname") return;
|
|
58
|
+
for (const [stage, target] of targets(mode)) {
|
|
59
|
+
const name = typeof target === "string" ? target.toLowerCase() : target;
|
|
60
|
+
if (!isHostname(name) || !name.endsWith(`.${domain.toLowerCase()}`)) throw new DnsTargetInvalid(domain, target, stage);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The CNAME target a domain's hosts point at on `stage`, or `undefined` for
|
|
65
|
+
* A records — including a per-stage map that names no target for it.
|
|
66
|
+
*/
|
|
67
|
+
function cnameTarget(records, stage) {
|
|
68
|
+
if (!records || records === "a") return void 0;
|
|
69
|
+
return (typeof records.target === "string" ? records.target : records.target[stage])?.toLowerCase();
|
|
70
|
+
}
|
|
71
|
+
/** Where an `every` schedule is counted from: 02:00 UTC. */
|
|
72
|
+
const EVERY_ANCHOR_SECONDS = 7200;
|
|
73
|
+
/** An `every` that is not an interval, or is shorter than an hour. */
|
|
74
|
+
var BackupIntervalInvalid = class extends require_errors.GkmError {
|
|
75
|
+
stage;
|
|
76
|
+
value;
|
|
77
|
+
reason;
|
|
78
|
+
constructor(stage, value, reason) {
|
|
79
|
+
super(`deploy.backups.${stage}.every is '${value}', which ${reason}. Give a number and a unit — '90m', '6h', '1d' — of at least an hour, or a five-field cron expression in UTC: { cron: '0 */6 * * *' }.`);
|
|
80
|
+
this.stage = stage;
|
|
81
|
+
this.value = value;
|
|
82
|
+
this.reason = reason;
|
|
83
|
+
this.name = "BackupIntervalInvalid";
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
/** A `cron` that is not a five-field expression, or runs more than hourly. */
|
|
87
|
+
var BackupCronInvalid = class extends require_errors.GkmError {
|
|
88
|
+
stage;
|
|
89
|
+
value;
|
|
90
|
+
reason;
|
|
91
|
+
constructor(stage, value, reason) {
|
|
92
|
+
super(`deploy.backups.${stage}.cron is '${value}', which ${reason}. Give five fields in UTC — minute hour day-of-month month day-of-week, e.g. '0 */6 * * *' — that run at most once an hour.`);
|
|
93
|
+
this.stage = stage;
|
|
94
|
+
this.value = value;
|
|
95
|
+
this.reason = reason;
|
|
96
|
+
this.name = "BackupCronInvalid";
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
/** A `keep` that is not a number of days. */
|
|
100
|
+
var BackupRetentionInvalid = class extends require_errors.GkmError {
|
|
101
|
+
stage;
|
|
102
|
+
value;
|
|
103
|
+
constructor(stage, value) {
|
|
104
|
+
super(`deploy.backups.${stage}.keep is '${value}', which is not a number of days. Give one of at least a day: keep: '30d'.`);
|
|
105
|
+
this.stage = stage;
|
|
106
|
+
this.value = value;
|
|
107
|
+
this.name = "BackupRetentionInvalid";
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
/** An entry that is neither `false` nor `{ every | cron, keep }`. */
|
|
111
|
+
var BackupsEntryInvalid = class extends require_errors.GkmError {
|
|
112
|
+
stage;
|
|
113
|
+
value;
|
|
114
|
+
reason;
|
|
115
|
+
constructor(stage, value, reason) {
|
|
116
|
+
super(`deploy.backups.${stage} is ${JSON.stringify(value)}, which ${reason}. It takes { every: '6h', keep: '30d' }, { cron: '0 */6 * * *', keep: '30d' }, or false to take no backups.`);
|
|
117
|
+
this.stage = stage;
|
|
118
|
+
this.value = value;
|
|
119
|
+
this.reason = reason;
|
|
120
|
+
this.name = "BackupsEntryInvalid";
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
const UNITS = {
|
|
124
|
+
m: 60,
|
|
125
|
+
h: 3600,
|
|
126
|
+
d: 86400
|
|
127
|
+
};
|
|
128
|
+
/** `'6h'` as seconds. */
|
|
129
|
+
function parseInterval(stage, value) {
|
|
130
|
+
const match = /^(\d+)\s*([mhd])$/.exec(value.trim());
|
|
131
|
+
if (!match) throw new BackupIntervalInvalid(stage, value, "is not an interval");
|
|
132
|
+
const seconds = Number(match[1]) * UNITS[match[2]];
|
|
133
|
+
if (seconds < 3600) throw new BackupIntervalInvalid(stage, value, "is shorter than an hour, the shortest interval backups take");
|
|
134
|
+
return seconds;
|
|
135
|
+
}
|
|
136
|
+
/** `'30d'` as days. */
|
|
137
|
+
function parseRetention(stage, value) {
|
|
138
|
+
const match = /^(\d+)\s*d$/.exec(value.trim());
|
|
139
|
+
const days = match ? Number(match[1]) : 0;
|
|
140
|
+
if (!match || days < 1) throw new BackupRetentionInvalid(stage, value);
|
|
141
|
+
return days;
|
|
142
|
+
}
|
|
143
|
+
const CRON_FIELDS = [
|
|
144
|
+
{
|
|
145
|
+
name: "minute",
|
|
146
|
+
min: 0,
|
|
147
|
+
max: 59
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
name: "hour",
|
|
151
|
+
min: 0,
|
|
152
|
+
max: 23
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
name: "day of month",
|
|
156
|
+
min: 1,
|
|
157
|
+
max: 31
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
name: "month",
|
|
161
|
+
min: 1,
|
|
162
|
+
max: 12
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
name: "day of week",
|
|
166
|
+
min: 0,
|
|
167
|
+
max: 7
|
|
168
|
+
}
|
|
169
|
+
];
|
|
170
|
+
/** One cron field's values, or `null` for `*`. */
|
|
171
|
+
function cronField(field, spec) {
|
|
172
|
+
if (field === "*") return null;
|
|
173
|
+
const values = /* @__PURE__ */ new Set();
|
|
174
|
+
for (const part of field.split(",")) {
|
|
175
|
+
const match = /^(\*|(\d+)(?:-(\d+))?)(?:\/(\d+))?$/.exec(part);
|
|
176
|
+
if (!match) return `has '${part}' in its ${spec.name} field`;
|
|
177
|
+
const step = match[4] ? Number(match[4]) : 1;
|
|
178
|
+
if (step < 1) return `steps by 0 in its ${spec.name} field`;
|
|
179
|
+
const from = match[1] === "*" ? spec.min : Number(match[2]);
|
|
180
|
+
const to = match[1] === "*" ? spec.max : match[3] !== void 0 ? Number(match[3]) : match[4] ? spec.max : from;
|
|
181
|
+
if (from < spec.min || to > spec.max || from > to) return `has '${part}' in its ${spec.name} field, outside ${spec.min}-${spec.max}`;
|
|
182
|
+
for (let value = from; value <= to; value += step) values.add(value);
|
|
183
|
+
}
|
|
184
|
+
return [...values].sort((a, b) => a - b);
|
|
185
|
+
}
|
|
186
|
+
/** A five-field cron expression, expanded. */
|
|
187
|
+
function parseCron(stage, expression) {
|
|
188
|
+
const fields = expression.trim().split(/\s+/);
|
|
189
|
+
if (fields.length !== 5) throw new BackupCronInvalid(stage, expression, `has ${fields.length} field${fields.length === 1 ? "" : "s"}, not five`);
|
|
190
|
+
const parsed = fields.map((field, i) => cronField(field, CRON_FIELDS[i]));
|
|
191
|
+
const problem = parsed.find((p) => typeof p === "string");
|
|
192
|
+
if (typeof problem === "string") throw new BackupCronInvalid(stage, expression, problem);
|
|
193
|
+
const [minutes, hours, days, months, weekdays] = parsed;
|
|
194
|
+
return {
|
|
195
|
+
kind: "cron",
|
|
196
|
+
expression: fields.join(" "),
|
|
197
|
+
minutes: minutes ?? range(0, 59),
|
|
198
|
+
hours: hours ?? range(0, 23),
|
|
199
|
+
days: days ?? null,
|
|
200
|
+
months: months ?? null,
|
|
201
|
+
weekdays: weekdays ? [...new Set(weekdays.map((d) => d % 7))].sort((a, b) => a - b) : null
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function range(from, to) {
|
|
205
|
+
return Array.from({ length: to - from + 1 }, (_, i) => from + i);
|
|
206
|
+
}
|
|
207
|
+
/** Whether a cron schedule runs on the UTC day of `date`. */
|
|
208
|
+
function cronDay(schedule, date) {
|
|
209
|
+
if (schedule.months && !schedule.months.includes(date.getUTCMonth() + 1)) return false;
|
|
210
|
+
const dom = schedule.days?.includes(date.getUTCDate());
|
|
211
|
+
const dow = schedule.weekdays?.includes(date.getUTCDay());
|
|
212
|
+
if (schedule.days && schedule.weekdays) return Boolean(dom || dow);
|
|
213
|
+
if (schedule.days) return Boolean(dom);
|
|
214
|
+
if (schedule.weekdays) return Boolean(dow);
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
/** How far ahead a cron schedule is looked for its next run: four years. */
|
|
218
|
+
const CRON_HORIZON_DAYS = 1464;
|
|
219
|
+
/**
|
|
220
|
+
* The first time after `after` (to the minute) the schedule runs, or `null`
|
|
221
|
+
* when it does not within four years.
|
|
222
|
+
*
|
|
223
|
+
* The container's runner (`runner.ts`) keeps a copy of this, since it ships
|
|
224
|
+
* alone; a test holds the two to the same answers.
|
|
225
|
+
*/
|
|
226
|
+
function nextRun(schedule, after) {
|
|
227
|
+
const from = Math.floor(after.getTime() / 6e4) * 6e4 + 6e4;
|
|
228
|
+
if (schedule.kind === "every") {
|
|
229
|
+
const interval = schedule.seconds * 1e3;
|
|
230
|
+
const offset = schedule.offset * 1e3;
|
|
231
|
+
const k = Math.ceil((from - offset) / interval);
|
|
232
|
+
return new Date(offset + k * interval);
|
|
233
|
+
}
|
|
234
|
+
const start = new Date(from);
|
|
235
|
+
const day = Date.UTC(start.getUTCFullYear(), start.getUTCMonth(), start.getUTCDate());
|
|
236
|
+
for (let i = 0; i < CRON_HORIZON_DAYS; i++) {
|
|
237
|
+
const date = new Date(day + i * 864e5);
|
|
238
|
+
if (!cronDay(schedule, date)) continue;
|
|
239
|
+
for (const hour of schedule.hours) for (const minute of schedule.minutes) {
|
|
240
|
+
const at = date.getTime() + hour * 36e5 + minute * 6e4;
|
|
241
|
+
if (at >= from) return new Date(at);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* The shortest and longest a schedule goes between two runs, in seconds,
|
|
248
|
+
* over the four years from 2026 — leap days and month ends included — or
|
|
249
|
+
* `null` when it does not run twice in them.
|
|
250
|
+
*/
|
|
251
|
+
function scheduleGaps(schedule) {
|
|
252
|
+
if (schedule.kind === "every") return {
|
|
253
|
+
shortest: schedule.seconds,
|
|
254
|
+
longest: schedule.seconds
|
|
255
|
+
};
|
|
256
|
+
const start = Date.UTC(2026, 0, 1);
|
|
257
|
+
const end = start + CRON_HORIZON_DAYS * 864e5;
|
|
258
|
+
let previous;
|
|
259
|
+
let longest = 0;
|
|
260
|
+
let shortest = Number.POSITIVE_INFINITY;
|
|
261
|
+
let at = /* @__PURE__ */ new Date(start - 6e4);
|
|
262
|
+
while (at && at.getTime() < end) {
|
|
263
|
+
at = nextRun(schedule, at);
|
|
264
|
+
if (!at) break;
|
|
265
|
+
if (previous !== void 0) {
|
|
266
|
+
longest = Math.max(longest, at.getTime() - previous);
|
|
267
|
+
shortest = Math.min(shortest, at.getTime() - previous);
|
|
268
|
+
if (shortest < 36e5) break;
|
|
269
|
+
}
|
|
270
|
+
previous = at.getTime();
|
|
271
|
+
}
|
|
272
|
+
if (longest === 0) return null;
|
|
273
|
+
return {
|
|
274
|
+
shortest: shortest / 1e3,
|
|
275
|
+
longest: longest / 1e3
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
/** `21600` as `6h`, `86400` as `1d`. */
|
|
279
|
+
function duration(seconds) {
|
|
280
|
+
if (seconds % 86400 === 0) return `${seconds / 86400}d`;
|
|
281
|
+
if (seconds % 3600 === 0) return `${seconds / 3600}h`;
|
|
282
|
+
return `${seconds / 60}m`;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* One stage's entry, checked and resolved: `false`, or its schedule and
|
|
286
|
+
* retention — the defaults filling what it leaves out.
|
|
287
|
+
*
|
|
288
|
+
* @throws {BackupsEntryInvalid} for an entry of the wrong shape
|
|
289
|
+
* @throws {BackupIntervalInvalid} for an `every` that is not an interval of an hour or more
|
|
290
|
+
* @throws {BackupCronInvalid} for a `cron` that is not five fields, or runs more than hourly
|
|
291
|
+
* @throws {BackupRetentionInvalid} for a `keep` that is not days
|
|
292
|
+
*/
|
|
293
|
+
function checkStageBackups(stage, value) {
|
|
294
|
+
if (value === false) return false;
|
|
295
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new BackupsEntryInvalid(stage, value, "is not an entry");
|
|
296
|
+
const entry = value;
|
|
297
|
+
const unknown = Object.keys(entry).filter((key) => ![
|
|
298
|
+
"every",
|
|
299
|
+
"cron",
|
|
300
|
+
"keep"
|
|
301
|
+
].includes(key));
|
|
302
|
+
if (unknown.length > 0) throw new BackupsEntryInvalid(stage, value, `has ${unknown.map((k) => `'${k}'`).join(", ")}, which it does not take`);
|
|
303
|
+
for (const key of [
|
|
304
|
+
"every",
|
|
305
|
+
"cron",
|
|
306
|
+
"keep"
|
|
307
|
+
]) if (entry[key] !== void 0 && typeof entry[key] !== "string") throw new BackupsEntryInvalid(stage, value, `has a ${key} that is not text`);
|
|
308
|
+
if (entry.every !== void 0 && entry.cron !== void 0) throw new BackupsEntryInvalid(stage, value, "gives both every and cron; give one");
|
|
309
|
+
const keepDays = parseRetention(stage, entry.keep ?? "30d");
|
|
310
|
+
if (typeof entry.cron === "string") {
|
|
311
|
+
const schedule = parseCron(stage, entry.cron);
|
|
312
|
+
const gaps = scheduleGaps(schedule);
|
|
313
|
+
if (!gaps) throw new BackupCronInvalid(stage, entry.cron, "does not run twice in four years");
|
|
314
|
+
if (gaps.shortest < 3600) throw new BackupCronInvalid(stage, entry.cron, `runs ${duration(gaps.shortest)} apart at its closest, more often than once an hour`);
|
|
315
|
+
return {
|
|
316
|
+
schedule,
|
|
317
|
+
keepDays,
|
|
318
|
+
maxGapSeconds: gaps.longest,
|
|
319
|
+
describe: `cron '${schedule.expression}' (UTC), kept ${keepDays} days`
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
const seconds = parseInterval(stage, entry.every ?? "1d");
|
|
323
|
+
return {
|
|
324
|
+
schedule: {
|
|
325
|
+
kind: "every",
|
|
326
|
+
seconds,
|
|
327
|
+
offset: EVERY_ANCHOR_SECONDS
|
|
328
|
+
},
|
|
329
|
+
keepDays,
|
|
330
|
+
maxGapSeconds: seconds,
|
|
331
|
+
describe: `every ${duration(seconds)} from 02:00 UTC, kept ${keepDays} days`
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
//#endregion
|
|
335
|
+
//#region src/secrets/providers.ts
|
|
336
|
+
/**
|
|
337
|
+
* The secrets stores gkm ships, by name — with no imports, so the config
|
|
338
|
+
* loader can check `secrets.store` without loading any store.
|
|
339
|
+
*/
|
|
340
|
+
/** The stores this package ships, by the name `secrets.store` gives them. */
|
|
341
|
+
const SECRETS_STORE_PROVIDERS = [
|
|
342
|
+
"file",
|
|
343
|
+
"ssm",
|
|
344
|
+
"secrets-manager",
|
|
345
|
+
"s3"
|
|
346
|
+
];
|
|
347
|
+
/**
|
|
348
|
+
* `secrets.store` names a provider this package does not ship — a typo such
|
|
349
|
+
* as `'secretsmanager'` or `'ssm-parameter'`. Refused rather than read as the
|
|
350
|
+
* file, which would leave a deployed stage's secrets on one machine.
|
|
351
|
+
*/
|
|
352
|
+
var UnknownSecretsStoreProvider = class extends require_errors.GkmError {
|
|
353
|
+
provider;
|
|
354
|
+
constructor(provider) {
|
|
355
|
+
super(`secrets.store names the provider ${JSON.stringify(provider)}, which is not one gkm ships. Use 'file', { provider: 's3' }, { provider: 'ssm', region }, { provider: 'secrets-manager', region }, or { provider: <an object with name, read() and write()> } in gkm.config.ts.`);
|
|
356
|
+
this.provider = provider;
|
|
357
|
+
this.name = "UnknownSecretsStoreProvider";
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
/**
|
|
361
|
+
* Refuse a `secrets.store` naming a provider that is not one, before the
|
|
362
|
+
* schema reduces it to a generic validation failure.
|
|
363
|
+
*/
|
|
364
|
+
function assertKnownSecretsStore(store) {
|
|
365
|
+
if (store === void 0) return;
|
|
366
|
+
const provider = typeof store === "object" && store !== null && "provider" in store ? store.provider : store;
|
|
367
|
+
if (typeof provider === "string" && !SECRETS_STORE_PROVIDERS.includes(provider)) throw new UnknownSecretsStoreProvider(provider);
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* An `s3` secrets store names no region, and the deploy state is not in S3 to
|
|
371
|
+
* take one from.
|
|
372
|
+
*/
|
|
373
|
+
var S3SecretsStoreNeedsRegion = class extends require_errors.GkmError {
|
|
374
|
+
constructor() {
|
|
375
|
+
super("secrets.store is { provider: 's3' } with no region, and the deploy state is not in S3 to take one from. Name it — secrets: { store: { provider: 's3', region: '<region>' } } — or keep the state beside it: state: { provider: 's3', region: '<region>' }.");
|
|
376
|
+
this.name = "S3SecretsStoreNeedsRegion";
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
/**
|
|
380
|
+
* Where an `s3` store keeps a stage's secrets. With no bucket named it is the
|
|
381
|
+
* project bucket, beside the deploy state; region and prefix default to the
|
|
382
|
+
* state's when that is in S3 too, so the two are written once.
|
|
383
|
+
*
|
|
384
|
+
* @throws {S3SecretsStoreNeedsRegion} when no region is named or inherited
|
|
385
|
+
*/
|
|
386
|
+
function s3SecretsLocation(store, state) {
|
|
387
|
+
const s3State = typeof state === "object" && state !== null && state.provider === "s3" ? state : void 0;
|
|
388
|
+
const region = store.region ?? s3State?.region;
|
|
389
|
+
if (!region) throw new S3SecretsStoreNeedsRegion();
|
|
390
|
+
return {
|
|
391
|
+
bucket: store.bucket,
|
|
392
|
+
region,
|
|
393
|
+
prefix: (store.prefix ?? s3State?.prefix ?? "gkm").replace(/\/+$/, "")
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
/** The key a stage's secrets are kept at: `<prefix>/<project>/<stage>/secrets.json`. */
|
|
397
|
+
function s3SecretsKey(prefix, project, stage) {
|
|
398
|
+
const path = `${project}/${stage}/secrets.json`;
|
|
399
|
+
return prefix ? `${prefix}/${path}` : path;
|
|
400
|
+
}
|
|
401
|
+
//#endregion
|
|
402
|
+
//#region src/compose/proxy.ts
|
|
403
|
+
/**
|
|
404
|
+
* Which proxy a stage's stack is served by, and with whose certificate —
|
|
405
|
+
* `deploy.compose.proxy` and `deploy.compose.tls`, resolved for one stage.
|
|
406
|
+
*
|
|
407
|
+
* Pure, and kept apart from the stack so the workspace schema can check a
|
|
408
|
+
* config with the same rules — and the same named errors — the stack applies.
|
|
409
|
+
*/
|
|
410
|
+
/** A per-stage setting keyed by a stage the workspace does not have. */
|
|
411
|
+
var ComposeStageUnknown = class extends require_errors.GkmError {
|
|
412
|
+
setting;
|
|
413
|
+
stage;
|
|
414
|
+
stages;
|
|
415
|
+
constructor(setting, stage, stages) {
|
|
416
|
+
super(`deploy.compose.${setting} names the stage '${stage}', which is not one of this workspace's (${stages.join(", ")}). Use a stage from stages.local or stages.deployed.`);
|
|
417
|
+
this.setting = setting;
|
|
418
|
+
this.stage = stage;
|
|
419
|
+
this.stages = stages;
|
|
420
|
+
this.name = "ComposeStageUnknown";
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
/**
|
|
424
|
+
* A certificate for the local stage, whose certificates are always Caddy's
|
|
425
|
+
* own CA — the one `gkm trust` installs.
|
|
426
|
+
*/
|
|
427
|
+
var ComposeTlsOnLocalStage = class extends require_errors.GkmError {
|
|
428
|
+
stage;
|
|
429
|
+
constructor(stage) {
|
|
430
|
+
super(`deploy.compose.tls sets a certificate for '${stage}', the local stage, whose certificates always come from Caddy's local CA (\`gkm trust\` installs it). Remove it, or set it for a deployed stage.`);
|
|
431
|
+
this.stage = stage;
|
|
432
|
+
this.name = "ComposeTlsOnLocalStage";
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
/**
|
|
436
|
+
* A server for the local stage, which always runs on this machine's Docker.
|
|
437
|
+
*/
|
|
438
|
+
var ComposeServerOnLocalStage = class extends require_errors.GkmError {
|
|
439
|
+
stage;
|
|
440
|
+
constructor(stage) {
|
|
441
|
+
super(`deploy.compose.server names '${stage}', the local stage, which always runs on this machine's Docker. Remove it, or set it for a deployed stage.`);
|
|
442
|
+
this.stage = stage;
|
|
443
|
+
this.name = "ComposeServerOnLocalStage";
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
/** The stage's certificate or key file is not there to read. */
|
|
447
|
+
var ComposeTlsFileMissing = class extends require_errors.GkmError {
|
|
448
|
+
stage;
|
|
449
|
+
file;
|
|
450
|
+
constructor(stage, file) {
|
|
451
|
+
super(`deploy.compose.tls for '${stage}' names ${file}, and there is no file there. Point certFile and keyFile at the PEM certificate (with its chain) and key — relative to the workspace root, or absolute.`);
|
|
452
|
+
this.stage = stage;
|
|
453
|
+
this.file = file;
|
|
454
|
+
this.name = "ComposeTlsFileMissing";
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
/**
|
|
458
|
+
* The proxy a stage's stack is served by. The local stage is always Caddy —
|
|
459
|
+
* its internal CA and `gkm trust` — whatever is configured.
|
|
460
|
+
*/
|
|
461
|
+
function proxyFor(config, stage, local) {
|
|
462
|
+
if (local) return "caddy";
|
|
463
|
+
const proxy = config?.proxy;
|
|
464
|
+
if (!proxy) return "caddy";
|
|
465
|
+
if (typeof proxy === "string") return proxy;
|
|
466
|
+
return proxy[stage] ?? "caddy";
|
|
467
|
+
}
|
|
468
|
+
/** A deployed stage's own certificate, its paths absolute, when it sets one. */
|
|
469
|
+
function certificateFor(config, stage, root) {
|
|
470
|
+
const tls = config?.tls?.[stage];
|
|
471
|
+
if (!tls) return void 0;
|
|
472
|
+
const absolute = (file) => (0, node_path.isAbsolute)(file) ? file : (0, node_path.resolve)(root, file);
|
|
473
|
+
return {
|
|
474
|
+
certFile: absolute(tls.certFile),
|
|
475
|
+
keyFile: absolute(tls.keyFile)
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Throws the named error for the first thing wrong with the per-stage
|
|
480
|
+
* settings: a stage the workspace does not have, or a certificate for the
|
|
481
|
+
* local stage.
|
|
482
|
+
*/
|
|
483
|
+
function checkComposeStages(config, stages) {
|
|
484
|
+
const known = [stages.local, ...stages.deployed];
|
|
485
|
+
if (config?.proxy && typeof config.proxy === "object") {
|
|
486
|
+
for (const stage of Object.keys(config.proxy)) if (!known.includes(stage)) throw new ComposeStageUnknown("proxy", stage, known);
|
|
487
|
+
}
|
|
488
|
+
for (const stage of Object.keys(config?.tls ?? {})) {
|
|
489
|
+
if (stage === stages.local) throw new ComposeTlsOnLocalStage(stage);
|
|
490
|
+
if (!known.includes(stage)) throw new ComposeStageUnknown("tls", stage, known);
|
|
491
|
+
}
|
|
492
|
+
for (const stage of Object.keys(config?.server ?? {})) {
|
|
493
|
+
if (stage === stages.local) throw new ComposeServerOnLocalStage(stage);
|
|
494
|
+
if (!known.includes(stage)) throw new ComposeStageUnknown("server", stage, known);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
//#endregion
|
|
498
|
+
//#region src/providers/s3/errors.ts
|
|
499
|
+
/** What the `s3` provider refuses, and how each is put right. */
|
|
500
|
+
/** An `objects` entry for `s3` with a field it does not take or cannot use. */
|
|
501
|
+
var S3ProviderConfigInvalid = class extends require_errors.GkmError {
|
|
502
|
+
stage;
|
|
503
|
+
field;
|
|
504
|
+
value;
|
|
505
|
+
expected;
|
|
506
|
+
constructor(stage, field, value, expected) {
|
|
507
|
+
super(`deploy.objects.${stage}.${field} is ${JSON.stringify(value)}; ${expected}.`);
|
|
508
|
+
this.stage = stage;
|
|
509
|
+
this.field = field;
|
|
510
|
+
this.value = value;
|
|
511
|
+
this.expected = expected;
|
|
512
|
+
this.name = "S3ProviderConfigInvalid";
|
|
513
|
+
}
|
|
514
|
+
};
|
|
515
|
+
/** No region anywhere to create the stage's buckets in. */
|
|
516
|
+
var S3RegionRequired = class extends require_errors.GkmError {
|
|
517
|
+
stage;
|
|
518
|
+
constructor(stage) {
|
|
519
|
+
super(`The s3 provider has no region for the stage '${stage}'. Name one in gkm.config.ts: deploy: { objects: { ${stage}: { provider: 's3', region: 'eu-west-1' } } } — or set AWS_REGION.`);
|
|
520
|
+
this.stage = stage;
|
|
521
|
+
this.name = "S3RegionRequired";
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
/** The good name and every suffixed one are taken by other accounts. */
|
|
525
|
+
var BucketNameUnavailable = class extends require_errors.GkmError {
|
|
526
|
+
id;
|
|
527
|
+
tried;
|
|
528
|
+
constructor(id, tried) {
|
|
529
|
+
super(`Every name tried for the bucket '${id}' is taken by another AWS account: ${tried.join(", ")}. Set deploy.namespace in gkm.config.ts to something more particular to you, and run the provision again.`);
|
|
530
|
+
this.id = id;
|
|
531
|
+
this.tried = tried;
|
|
532
|
+
this.name = "BucketNameUnavailable";
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
/** A bucket in this account carries another project's tag. */
|
|
536
|
+
var BucketOwnedByAnotherProject = class extends require_errors.GkmError {
|
|
537
|
+
bucket;
|
|
538
|
+
owner;
|
|
539
|
+
project;
|
|
540
|
+
constructor(bucket, owner, project) {
|
|
541
|
+
super(`The bucket '${bucket}' is in this account and tagged gkm:project=${owner}, not ${project}: another project's. gkm never adopts it. Set deploy.namespace in gkm.config.ts so this project names its buckets differently.`);
|
|
542
|
+
this.bucket = bucket;
|
|
543
|
+
this.owner = owner;
|
|
544
|
+
this.project = project;
|
|
545
|
+
this.name = "BucketOwnedByAnotherProject";
|
|
546
|
+
}
|
|
547
|
+
};
|
|
548
|
+
/** The bucket the stage's state names exists, and this account cannot reach it. */
|
|
549
|
+
var RecordedBucketUnreachable = class extends require_errors.GkmError {
|
|
550
|
+
id;
|
|
551
|
+
bucket;
|
|
552
|
+
constructor(id, bucket) {
|
|
553
|
+
super(`The stage's state records '${bucket}' as the bucket for '${id}', and this account cannot reach it — it is in another account, or the credentials are not the stage account's. Run with the account that created it (--profile).`);
|
|
554
|
+
this.id = id;
|
|
555
|
+
this.bucket = bucket;
|
|
556
|
+
this.name = "RecordedBucketUnreachable";
|
|
557
|
+
}
|
|
558
|
+
};
|
|
559
|
+
/** An IAM user by gkm's name exists, and gkm did not create it for this stage. */
|
|
560
|
+
var IamUserNotOwned = class extends require_errors.GkmError {
|
|
561
|
+
user;
|
|
562
|
+
project;
|
|
563
|
+
stage;
|
|
564
|
+
constructor(user, project, stage) {
|
|
565
|
+
super(`The IAM user '${user}' exists and is not tagged gkm:project=${project}, gkm:stage=${stage}, so gkm leaves it alone. Rename or delete it, or set deploy.namespace in gkm.config.ts so gkm names its users differently.`);
|
|
566
|
+
this.user = user;
|
|
567
|
+
this.project = project;
|
|
568
|
+
this.stage = stage;
|
|
569
|
+
this.name = "IamUserNotOwned";
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
/** The user already has the two access keys IAM allows. */
|
|
573
|
+
var AccessKeyLimit = class extends require_errors.GkmError {
|
|
574
|
+
user;
|
|
575
|
+
keys;
|
|
576
|
+
constructor(user, keys) {
|
|
577
|
+
super(`The IAM user '${user}' already has two access keys (${keys.join(", ")}), the most IAM allows, and gkm needs to issue one. Delete the one the stage does not use (aws iam delete-access-key --user-name ${user} --access-key-id …), then run the provision again.`);
|
|
578
|
+
this.user = user;
|
|
579
|
+
this.keys = keys;
|
|
580
|
+
this.name = "AccessKeyLimit";
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
/** `--rotate-keys` while the last rotation's old key is still active. */
|
|
584
|
+
var RotationInProgress = class extends require_errors.GkmError {
|
|
585
|
+
id;
|
|
586
|
+
previous;
|
|
587
|
+
stage;
|
|
588
|
+
constructor(id, previous, stage) {
|
|
589
|
+
super(`The bucket '${id}' is mid-rotation: its old key ${previous} is still active until '${stage}' is deployed with the new one: deploy it (gkm deploy --stage ${stage}) — the next deploy after that deletes the old key — or pass --retire-old-keys to delete it now, before rotating again.`);
|
|
590
|
+
this.id = id;
|
|
591
|
+
this.previous = previous;
|
|
592
|
+
this.stage = stage;
|
|
593
|
+
this.name = "RotationInProgress";
|
|
594
|
+
}
|
|
595
|
+
};
|
|
596
|
+
/** The stage's secrets point the bucket's key at a bucket gkm did not provision. */
|
|
597
|
+
var BucketKeySetElsewhere = class extends require_errors.GkmError {
|
|
598
|
+
key;
|
|
599
|
+
bucket;
|
|
600
|
+
found;
|
|
601
|
+
stage;
|
|
602
|
+
constructor(key, bucket, found, stage) {
|
|
603
|
+
super(`${key} in the stage '${stage}' points at the bucket '${found}', and the s3 provider manages '${bucket}' for it. Remove the key (gkm secrets:unset ${key} --stage ${stage}) to have the provider write its own, or set deploy.objects.${stage} to 'external' to keep yours.`);
|
|
604
|
+
this.key = key;
|
|
605
|
+
this.bucket = bucket;
|
|
606
|
+
this.found = found;
|
|
607
|
+
this.stage = stage;
|
|
608
|
+
this.name = "BucketKeySetElsewhere";
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
/** `verify()`: a provisioned bucket the stage's key cannot reach. */
|
|
612
|
+
var ProvisionedBucketUnreachable = class extends require_errors.GkmError {
|
|
613
|
+
id;
|
|
614
|
+
bucket;
|
|
615
|
+
stage;
|
|
616
|
+
reason;
|
|
617
|
+
constructor(id, bucket, stage, reason) {
|
|
618
|
+
super(`The bucket '${bucket}' (${id}) cannot be reached with the key in the stage '${stage}''s secrets: ${reason}. Deploy '${stage}' with the stage account's credentials (gkm deploy --stage ${stage}), which creates or repairs it.`);
|
|
619
|
+
this.id = id;
|
|
620
|
+
this.bucket = bucket;
|
|
621
|
+
this.stage = stage;
|
|
622
|
+
this.reason = reason;
|
|
623
|
+
this.name = "ProvisionedBucketUnreachable";
|
|
624
|
+
}
|
|
625
|
+
};
|
|
626
|
+
/**
|
|
627
|
+
* `verify()`: the bucket answers from another region than the one its URL
|
|
628
|
+
* names — S3 redirects (301) or refuses the signature (400) and says where
|
|
629
|
+
* the bucket is.
|
|
630
|
+
*/
|
|
631
|
+
var BucketRegionMismatch = class extends require_errors.GkmError {
|
|
632
|
+
id;
|
|
633
|
+
bucket;
|
|
634
|
+
stage;
|
|
635
|
+
key;
|
|
636
|
+
bucketRegion;
|
|
637
|
+
urlRegion;
|
|
638
|
+
askedRegion;
|
|
639
|
+
answer;
|
|
640
|
+
constructor(id, bucket, stage, key, bucketRegion, urlRegion, askedRegion, answer) {
|
|
641
|
+
const says = urlRegion ? `says ${urlRegion}` : `names no region, so ${askedRegion} was asked`;
|
|
642
|
+
super(`The bucket '${bucket}' (${id}) is in ${bucketRegion}, but ${key} in the stage '${stage}''s secrets ${says} (${answer}). Deploy '${stage}' with the stage account's credentials (gkm deploy --stage ${stage}): the s3 provider reads the bucket's region and rewrites ${key} with it. Set deploy.objects.${stage}.region to '${bucketRegion}' in gkm.config.ts so the stage's config says where its buckets are.`);
|
|
643
|
+
this.id = id;
|
|
644
|
+
this.bucket = bucket;
|
|
645
|
+
this.stage = stage;
|
|
646
|
+
this.key = key;
|
|
647
|
+
this.bucketRegion = bucketRegion;
|
|
648
|
+
this.urlRegion = urlRegion;
|
|
649
|
+
this.askedRegion = askedRegion;
|
|
650
|
+
this.answer = answer;
|
|
651
|
+
this.name = "BucketRegionMismatch";
|
|
652
|
+
}
|
|
653
|
+
};
|
|
654
|
+
/** Neither `HeadBucket` nor `GetBucketLocation` says where a bucket is. */
|
|
655
|
+
var BucketRegionUnknown = class extends require_errors.GkmError {
|
|
656
|
+
bucket;
|
|
657
|
+
head;
|
|
658
|
+
location;
|
|
659
|
+
constructor(bucket, head, location) {
|
|
660
|
+
super(`gkm cannot tell which region the bucket '${bucket}' is in: HeadBucket answered ${head}, and GetBucketLocation ${location}. Check that the provisioning credentials are the stage account's and may call s3:GetBucketLocation on it, then run the deploy again.`);
|
|
661
|
+
this.bucket = bucket;
|
|
662
|
+
this.head = head;
|
|
663
|
+
this.location = location;
|
|
664
|
+
this.name = "BucketRegionUnknown";
|
|
665
|
+
}
|
|
666
|
+
};
|
|
667
|
+
/** `HeadBucket` answered neither yes, no, nor "another account's". */
|
|
668
|
+
var BucketProbeFailed = class extends require_errors.GkmError {
|
|
669
|
+
bucket;
|
|
670
|
+
answer;
|
|
671
|
+
constructor(bucket, answer) {
|
|
672
|
+
super(`gkm asked S3 whether the bucket '${bucket}' exists and got ${answer}. Check the provisioning credentials and the stage's region, then run the deploy again.`);
|
|
673
|
+
this.bucket = bucket;
|
|
674
|
+
this.answer = answer;
|
|
675
|
+
this.name = "BucketProbeFailed";
|
|
676
|
+
}
|
|
677
|
+
};
|
|
678
|
+
//#endregion
|
|
679
|
+
//#region src/providers/s3/config.ts
|
|
680
|
+
const FIELDS = [
|
|
681
|
+
"provider",
|
|
682
|
+
"region",
|
|
683
|
+
"versioning"
|
|
684
|
+
];
|
|
685
|
+
/** An AWS region code: `eu-west-1`, `us-gov-west-1`, `ap-southeast-4`. */
|
|
686
|
+
const REGION = /^[a-z]{2}(-[a-z]+)+-\d+$/;
|
|
687
|
+
function checkS3Config(stage, config) {
|
|
688
|
+
const entry = config;
|
|
689
|
+
for (const field of Object.keys(entry)) if (!FIELDS.includes(field)) throw new S3ProviderConfigInvalid(stage, field, entry[field], `the s3 provider takes ${FIELDS.slice(1).join(" and ")} only`);
|
|
690
|
+
if (entry.region !== void 0) {
|
|
691
|
+
if (typeof entry.region !== "string" || !REGION.test(entry.region)) throw new S3ProviderConfigInvalid(stage, "region", entry.region, "set it to an AWS region code, like 'eu-west-1'");
|
|
692
|
+
}
|
|
693
|
+
if (entry.versioning !== void 0 && typeof entry.versioning !== "boolean") throw new S3ProviderConfigInvalid(stage, "versioning", entry.versioning, "set it to true to keep previous versions of every object, or leave it out");
|
|
694
|
+
return entry;
|
|
695
|
+
}
|
|
696
|
+
/** How the s3 provider's provisioning credentials are supplied. */
|
|
697
|
+
const S3_PROVISIONING = {
|
|
698
|
+
env: [
|
|
699
|
+
"AWS_PROFILE",
|
|
700
|
+
"AWS_ACCESS_KEY_ID",
|
|
701
|
+
"AWS_SECRET_ACCESS_KEY",
|
|
702
|
+
"AWS_SESSION_TOKEN"
|
|
703
|
+
],
|
|
704
|
+
describe: "the stage's AWS account — AWS_PROFILE, or AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY (in CI, what aws-actions/configure-aws-credentials exports)"
|
|
705
|
+
};
|
|
706
|
+
//#endregion
|
|
707
|
+
//#region src/providers/config.ts
|
|
708
|
+
/**
|
|
709
|
+
* `deploy.<kind>.<stage>`, read: what backs one kind of construct on a stage.
|
|
710
|
+
*
|
|
711
|
+
* Kept free of the providers themselves — of the AWS SDK and the graph — so
|
|
712
|
+
* the workspace schema can check an entry with the words a deploy would use,
|
|
713
|
+
* the way `checkStageTelemetry` does for `deploy.telemetry`.
|
|
714
|
+
*/
|
|
715
|
+
/** Each provider's config check, by kind and name. */
|
|
716
|
+
const CHECKS = { objects: { s3: checkS3Config } };
|
|
717
|
+
/** How each provider's provisioning credentials are supplied, by kind and name. */
|
|
718
|
+
const PROVISIONING = { objects: { s3: S3_PROVISIONING } };
|
|
719
|
+
/** How `name`'s provisioning credentials are supplied, for messages. */
|
|
720
|
+
function provisioningOf(kind, name) {
|
|
721
|
+
return Object.hasOwn(PROVISIONING[kind], name) ? PROVISIONING[kind][name] : void 0;
|
|
722
|
+
}
|
|
723
|
+
/** The `provider:` names a kind accepts. */
|
|
724
|
+
function providerNamesOf(kind) {
|
|
725
|
+
return Object.keys(CHECKS[kind]).sort();
|
|
726
|
+
}
|
|
727
|
+
/** What `deploy.<kind>.<stage>` may be, for messages. */
|
|
728
|
+
function accepted(kind) {
|
|
729
|
+
return [
|
|
730
|
+
"'external'",
|
|
731
|
+
...providerNamesOf(kind).map((name) => `{ provider: '${name}' }`),
|
|
732
|
+
"false"
|
|
733
|
+
].join(", ");
|
|
734
|
+
}
|
|
735
|
+
/** An entry that names no provider gkm has, or is not an entry at all. */
|
|
736
|
+
var UnknownStageProvider = class extends require_errors.GkmError {
|
|
737
|
+
kind;
|
|
738
|
+
stage;
|
|
739
|
+
value;
|
|
740
|
+
constructor(kind, stage, value) {
|
|
741
|
+
super(`deploy.${kind}.${stage} is ${JSON.stringify(value)}, which is not a provider gkm has for ${kind}. It takes ${accepted(kind)}.`);
|
|
742
|
+
this.kind = kind;
|
|
743
|
+
this.stage = stage;
|
|
744
|
+
this.value = value;
|
|
745
|
+
this.name = "UnknownStageProvider";
|
|
746
|
+
}
|
|
747
|
+
};
|
|
748
|
+
/**
|
|
749
|
+
* One stage's entry checked: `'external'`, `false`, or a provider's object
|
|
750
|
+
* that its own check accepts.
|
|
751
|
+
*
|
|
752
|
+
* @throws {UnknownStageProvider} for anything else — `'minio'` included
|
|
753
|
+
*/
|
|
754
|
+
function checkStageProvider(kind, stage, value) {
|
|
755
|
+
if (value === "external" || value === false) return value;
|
|
756
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
757
|
+
const name = value.provider;
|
|
758
|
+
const check = typeof name === "string" && Object.hasOwn(CHECKS[kind], name) ? CHECKS[kind][name] : void 0;
|
|
759
|
+
if (check) return check(stage, value);
|
|
760
|
+
}
|
|
761
|
+
throw new UnknownStageProvider(kind, stage, value);
|
|
762
|
+
}
|
|
763
|
+
/**
|
|
764
|
+
* What backs `kind` on `stage`: the local stage is always `external` here —
|
|
765
|
+
* `gkm dev` runs its own — and a deployed stage that names nothing is too.
|
|
766
|
+
*/
|
|
767
|
+
function stageProvider(workspace, kind, stage) {
|
|
768
|
+
if (stage === workspace.stages?.local) return { mode: "external" };
|
|
769
|
+
const value = workspace.deploy?.[kind]?.[stage];
|
|
770
|
+
if (value === void 0) return { mode: "external" };
|
|
771
|
+
const entry = checkStageProvider(kind, stage, value);
|
|
772
|
+
if (entry === "external") return { mode: "external" };
|
|
773
|
+
if (entry === false) return { mode: "none" };
|
|
774
|
+
return {
|
|
775
|
+
mode: "provider",
|
|
776
|
+
name: entry.provider,
|
|
777
|
+
config: entry
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
//#endregion
|
|
781
|
+
//#region src/compose/logsConfig.ts
|
|
782
|
+
/**
|
|
783
|
+
* The self-hosted telemetry provider's options, read: how the stack's
|
|
784
|
+
* OpenObserve is run and reached — `deploy.telemetry.<stage>` with
|
|
785
|
+
* `provider: 'self-hosted'`.
|
|
786
|
+
*
|
|
787
|
+
* Kept apart from the stack so the workspace schema can check a config with
|
|
788
|
+
* the same rules — and the same named errors — the stack applies.
|
|
789
|
+
*/
|
|
790
|
+
/** The port OpenObserve listens on, and the one it is published on by default. */
|
|
791
|
+
const LOGS_PORT = 5080;
|
|
792
|
+
/** `public` with no address to allow: nobody could reach it. */
|
|
793
|
+
var LogsAllowEmpty = class extends require_errors.GkmError {
|
|
794
|
+
stage;
|
|
795
|
+
constructor(stage) {
|
|
796
|
+
super(`deploy.telemetry.${stage}.public.allow is empty, so the logs site would answer every request with 403. List the addresses that may reach it (public: { allow: ['203.0.113.7', '10.0.0.0/8'] }), or remove public and reach it through an SSH tunnel.`);
|
|
797
|
+
this.stage = stage;
|
|
798
|
+
this.name = "LogsAllowEmpty";
|
|
799
|
+
}
|
|
800
|
+
};
|
|
801
|
+
/** An `allow` entry that is not an IP address or a CIDR range. */
|
|
802
|
+
var LogsAllowEntryInvalid = class extends require_errors.GkmError {
|
|
803
|
+
stage;
|
|
804
|
+
entry;
|
|
805
|
+
constructor(stage, entry) {
|
|
806
|
+
super(`deploy.telemetry.${stage}.public.allow has '${entry}', which is not an IP address or a CIDR range. Write one like '203.0.113.7', '10.0.0.0/8' or '2001:db8::/32'.`);
|
|
807
|
+
this.stage = stage;
|
|
808
|
+
this.entry = entry;
|
|
809
|
+
this.name = "LogsAllowEntryInvalid";
|
|
810
|
+
}
|
|
811
|
+
};
|
|
812
|
+
/** A retention OpenObserve would refuse to start with. */
|
|
813
|
+
var LogsRetentionInvalid = class extends require_errors.GkmError {
|
|
814
|
+
stage;
|
|
815
|
+
retentionDays;
|
|
816
|
+
constructor(stage, retentionDays) {
|
|
817
|
+
super(`deploy.telemetry.${stage}.retentionDays is ${String(retentionDays)}; it must be a whole number of days, at least 3 — OpenObserve refuses to start with fewer. Leave it out for 30.`);
|
|
818
|
+
this.stage = stage;
|
|
819
|
+
this.retentionDays = retentionDays;
|
|
820
|
+
this.name = "LogsRetentionInvalid";
|
|
821
|
+
}
|
|
822
|
+
};
|
|
823
|
+
/** A port that is not one. */
|
|
824
|
+
var LogsPortInvalid = class extends require_errors.GkmError {
|
|
825
|
+
stage;
|
|
826
|
+
port;
|
|
827
|
+
constructor(stage, port) {
|
|
828
|
+
super(`deploy.telemetry.${stage}.port is ${String(port)}; set it to a number between 1 and 65535, or leave it out for ${LOGS_PORT}.`);
|
|
829
|
+
this.stage = stage;
|
|
830
|
+
this.port = port;
|
|
831
|
+
this.name = "LogsPortInvalid";
|
|
832
|
+
}
|
|
833
|
+
};
|
|
834
|
+
/** Whether `entry` is an IP address, or an address and a prefix length. */
|
|
835
|
+
function isAddressOrRange(entry) {
|
|
836
|
+
const [address = "", prefix, ...rest] = entry.split("/");
|
|
837
|
+
if (rest.length > 0) return false;
|
|
838
|
+
const family = (0, node_net.isIP)(address);
|
|
839
|
+
if (family === 0) return false;
|
|
840
|
+
if (prefix === void 0) return true;
|
|
841
|
+
if (!/^\d{1,3}$/.test(prefix)) return false;
|
|
842
|
+
return Number(prefix) <= (family === 4 ? 32 : 128);
|
|
843
|
+
}
|
|
844
|
+
/**
|
|
845
|
+
* The self-hosted provider's options with their defaults — `{}` for all of
|
|
846
|
+
* them. Throws the named error for the first thing wrong with them.
|
|
847
|
+
*/
|
|
848
|
+
function resolveSelfHosted(config, stage) {
|
|
849
|
+
const port = config.port ?? 5080;
|
|
850
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) throw new LogsPortInvalid(stage, config.port);
|
|
851
|
+
const retentionDays = config.retentionDays ?? 30;
|
|
852
|
+
if (!Number.isInteger(retentionDays) || retentionDays < 3) throw new LogsRetentionInvalid(stage, config.retentionDays);
|
|
853
|
+
if (config.public) {
|
|
854
|
+
const allow = config.public.allow ?? [];
|
|
855
|
+
if (allow.length === 0) throw new LogsAllowEmpty(stage);
|
|
856
|
+
for (const entry of allow) if (!isAddressOrRange(entry)) throw new LogsAllowEntryInvalid(stage, entry);
|
|
857
|
+
return {
|
|
858
|
+
port,
|
|
859
|
+
retentionDays,
|
|
860
|
+
public: { allow: [...allow] }
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
return {
|
|
864
|
+
port,
|
|
865
|
+
retentionDays
|
|
866
|
+
};
|
|
867
|
+
}
|
|
868
|
+
//#endregion
|
|
869
|
+
//#region src/telemetry/config.ts
|
|
870
|
+
/**
|
|
871
|
+
* `deploy.telemetry`, read: where one stage's telemetry goes, and how much.
|
|
872
|
+
*
|
|
873
|
+
* The application says what it emits (the `Telemetry` construct). This says
|
|
874
|
+
* where it is sent, per stage, and is the only place that does:
|
|
875
|
+
*
|
|
876
|
+
* - **self-hosted** — the target runs OpenObserve beside the apps (#189's
|
|
877
|
+
* stack service). A server target that can run it uses it when a stage
|
|
878
|
+
* names nothing.
|
|
879
|
+
* - **otlp** — any OTLP/HTTP endpoint, with its headers.
|
|
880
|
+
* - **false** — nothing is sent.
|
|
881
|
+
*
|
|
882
|
+
* Vendor presets (a known endpoint and header shape, with the stage secret
|
|
883
|
+
* `TELEMETRY_CREDENTIALS`) are one more entry in {@link PROVIDERS}: a
|
|
884
|
+
* resolver from the stage's config to a {@link ResolvedTelemetry}, which
|
|
885
|
+
* every target already consumes through {@link telemetryEnv}.
|
|
886
|
+
*
|
|
887
|
+
* The local stage ignores all of it: `gkm dev` and `gkm compose` on it run
|
|
888
|
+
* OpenObserve and keep every trace.
|
|
889
|
+
*/
|
|
890
|
+
/** A deployed stage uses telemetry and its target cannot choose for it. */
|
|
891
|
+
var TelemetryProviderRequired = class extends require_errors.GkmError {
|
|
892
|
+
stage;
|
|
893
|
+
target;
|
|
894
|
+
constructor(stage, target) {
|
|
895
|
+
super(`The stage '${stage}' deploys through ${target}, which cannot run telemetry itself, and a process uses a Telemetry construct. Say where it goes in gkm.config.ts: deploy: { telemetry: { ${stage}: { provider: 'otlp', endpoint: 'https://…' } } } — or deploy: { telemetry: { ${stage}: false } } to send nothing.`);
|
|
896
|
+
this.stage = stage;
|
|
897
|
+
this.target = target;
|
|
898
|
+
this.name = "TelemetryProviderRequired";
|
|
899
|
+
}
|
|
900
|
+
};
|
|
901
|
+
/** A stage asks for the self-hosted provider on a target that cannot run it. */
|
|
902
|
+
var SelfHostedTelemetryUnavailable = class extends require_errors.GkmError {
|
|
903
|
+
stage;
|
|
904
|
+
target;
|
|
905
|
+
constructor(stage, target) {
|
|
906
|
+
super(`deploy.telemetry.${stage} is 'self-hosted', and ${target} does not run OpenObserve beside the apps — the compose target does. Deploy '${stage}' through compose, or send its telemetry elsewhere: deploy: { telemetry: { ${stage}: { provider: 'otlp', endpoint: 'https://…' } } }.`);
|
|
907
|
+
this.stage = stage;
|
|
908
|
+
this.target = target;
|
|
909
|
+
this.name = "SelfHostedTelemetryUnavailable";
|
|
910
|
+
}
|
|
911
|
+
};
|
|
912
|
+
/** A sample rate that is not a fraction. */
|
|
913
|
+
var TelemetrySampleRateInvalid = class extends require_errors.GkmError {
|
|
914
|
+
stage;
|
|
915
|
+
sampleRate;
|
|
916
|
+
constructor(stage, sampleRate) {
|
|
917
|
+
super(`deploy.telemetry.${stage}.sampleRate is ${String(sampleRate)}; set it to a number from 0 to 1 — the fraction of traces kept — or leave it out to keep every one.`);
|
|
918
|
+
this.stage = stage;
|
|
919
|
+
this.sampleRate = sampleRate;
|
|
920
|
+
this.name = "TelemetrySampleRateInvalid";
|
|
921
|
+
}
|
|
922
|
+
};
|
|
923
|
+
/** An OTLP endpoint that is not an http(s) URL. */
|
|
924
|
+
var TelemetryEndpointInvalid = class extends require_errors.GkmError {
|
|
925
|
+
stage;
|
|
926
|
+
endpoint;
|
|
927
|
+
constructor(stage, endpoint) {
|
|
928
|
+
super(`deploy.telemetry.${stage}.endpoint is ${JSON.stringify(endpoint)}; set it to the collector's OTLP/HTTP base URL, like 'https://otlp.example.com' — /v1/traces and /v1/logs are added to it.`);
|
|
929
|
+
this.stage = stage;
|
|
930
|
+
this.endpoint = endpoint;
|
|
931
|
+
this.name = "TelemetryEndpointInvalid";
|
|
932
|
+
}
|
|
933
|
+
};
|
|
934
|
+
/** The sample rate a stage keeps, checked. */
|
|
935
|
+
function sampleRateOf(stage, sampleRate) {
|
|
936
|
+
const rate = sampleRate ?? 1;
|
|
937
|
+
if (!Number.isFinite(rate) || rate < 0 || rate > 1) throw new TelemetrySampleRateInvalid(stage, sampleRate);
|
|
938
|
+
return rate;
|
|
939
|
+
}
|
|
940
|
+
/**
|
|
941
|
+
* Each provider's resolver: a stage's config to what the targets consume.
|
|
942
|
+
* A vendor preset is one more entry here.
|
|
943
|
+
*/
|
|
944
|
+
const PROVIDERS = {
|
|
945
|
+
"self-hosted": (config, stage) => {
|
|
946
|
+
const { provider: _, sampleRate, ...options } = config;
|
|
947
|
+
return {
|
|
948
|
+
provider: "self-hosted",
|
|
949
|
+
selfHosted: resolveSelfHosted(options, stage),
|
|
950
|
+
sampleRate: sampleRateOf(stage, sampleRate)
|
|
951
|
+
};
|
|
952
|
+
},
|
|
953
|
+
otlp: (config, stage) => {
|
|
954
|
+
if (typeof config.endpoint !== "string" || !isHttpUrl(config.endpoint)) throw new TelemetryEndpointInvalid(stage, config.endpoint);
|
|
955
|
+
return {
|
|
956
|
+
provider: "otlp",
|
|
957
|
+
endpoint: config.endpoint.replace(/\/+$/, ""),
|
|
958
|
+
headers: { ...config.headers },
|
|
959
|
+
sampleRate: sampleRateOf(stage, config.sampleRate)
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
};
|
|
963
|
+
function isHttpUrl(value) {
|
|
964
|
+
try {
|
|
965
|
+
const url = new URL(value);
|
|
966
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
967
|
+
} catch {
|
|
968
|
+
return false;
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
/**
|
|
972
|
+
* One stage's entry, checked on its own — what the workspace schema runs, so
|
|
973
|
+
* a bad config fails to load with the words a deploy would fail with.
|
|
974
|
+
*/
|
|
975
|
+
function checkStageTelemetry(stage, config) {
|
|
976
|
+
if (config === false) return void 0;
|
|
977
|
+
const entry = config === "self-hosted" ? { provider: "self-hosted" } : config;
|
|
978
|
+
const resolve = PROVIDERS[entry.provider];
|
|
979
|
+
return resolve(entry, stage);
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* Where a stage's telemetry goes, or undefined when nothing is sent — no
|
|
983
|
+
* process uses a `Telemetry` node, or the stage opted out with `false`.
|
|
984
|
+
*
|
|
985
|
+
* The local stage always runs the self-hosted provider with its defaults and
|
|
986
|
+
* keeps every trace, whatever `deploy.telemetry` says. A deployed stage that
|
|
987
|
+
* names nothing gets the self-hosted provider where its target runs one, and
|
|
988
|
+
* {@link TelemetryProviderRequired} where it cannot — AWS has none.
|
|
989
|
+
*/
|
|
990
|
+
function resolveStageTelemetry(input) {
|
|
991
|
+
const { stage } = input;
|
|
992
|
+
if (!input.used) return void 0;
|
|
993
|
+
if (input.local) return {
|
|
994
|
+
provider: "self-hosted",
|
|
995
|
+
selfHosted: resolveSelfHosted({}, stage),
|
|
996
|
+
sampleRate: 1
|
|
997
|
+
};
|
|
998
|
+
const config = input.telemetry?.[stage];
|
|
999
|
+
if (config === void 0) {
|
|
1000
|
+
if (input.runtime === "server" && input.selfHosted) return checkStageTelemetry(stage, "self-hosted");
|
|
1001
|
+
throw new TelemetryProviderRequired(stage, input.target);
|
|
1002
|
+
}
|
|
1003
|
+
const resolved = checkStageTelemetry(stage, config);
|
|
1004
|
+
if (resolved?.provider === "self-hosted" && !input.selfHosted) throw new SelfHostedTelemetryUnavailable(stage, input.target);
|
|
1005
|
+
return resolved;
|
|
1006
|
+
}
|
|
1007
|
+
/** The sampler every process with the edge runs, per the stage's rate. */
|
|
1008
|
+
const TRACES_SAMPLER = "parentbased_traceidratio";
|
|
1009
|
+
/**
|
|
1010
|
+
* `OTEL_EXPORTER_OTLP_HEADERS` for a set of headers: `key=value` pairs,
|
|
1011
|
+
* comma-separated, each value percent-encoded — the W3C baggage format the
|
|
1012
|
+
* SDK parses.
|
|
1013
|
+
*/
|
|
1014
|
+
function otlpHeaderString(headers) {
|
|
1015
|
+
return Object.entries(headers).map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join(",");
|
|
1016
|
+
}
|
|
1017
|
+
/**
|
|
1018
|
+
* The `OTEL_*` values a stage resolves for its `Telemetry` node, before any
|
|
1019
|
+
* one process is named: where to export, how to sign, and how much to keep.
|
|
1020
|
+
* `OTEL_SERVICE_NAME` is each process's own — see `scopeTelemetryEnv`.
|
|
1021
|
+
*/
|
|
1022
|
+
function telemetryEnv(destination, sampleRate) {
|
|
1023
|
+
return {
|
|
1024
|
+
OTEL_EXPORTER_OTLP_ENDPOINT: destination.endpoint,
|
|
1025
|
+
...destination.headers ? { OTEL_EXPORTER_OTLP_HEADERS: destination.headers } : {},
|
|
1026
|
+
OTEL_TRACES_SAMPLER: TRACES_SAMPLER,
|
|
1027
|
+
OTEL_TRACES_SAMPLER_ARG: String(sampleRate)
|
|
1028
|
+
};
|
|
1029
|
+
}
|
|
1030
|
+
/** What an `otlp` stage resolves, for every process with the edge. */
|
|
1031
|
+
function otlpTelemetryEnv(resolved) {
|
|
1032
|
+
const headers = otlpHeaderString(resolved.headers);
|
|
1033
|
+
return telemetryEnv({
|
|
1034
|
+
endpoint: resolved.endpoint,
|
|
1035
|
+
...headers ? { headers } : {}
|
|
1036
|
+
}, resolved.sampleRate);
|
|
1037
|
+
}
|
|
1038
|
+
/**
|
|
1039
|
+
* The sample rate a stage's browsers trace page views at — a public value,
|
|
1040
|
+
* the same rate its servers sample at — or undefined when the stage sends no
|
|
1041
|
+
* telemetry. The local stage keeps every trace.
|
|
1042
|
+
*
|
|
1043
|
+
* Lenient where `resolveStageTelemetry` is strict: a site's client is built
|
|
1044
|
+
* before, and apart from, the deploy that refuses a stage with no provider.
|
|
1045
|
+
*/
|
|
1046
|
+
function stageSampleRate(telemetry, stage, local) {
|
|
1047
|
+
if (local) return 1;
|
|
1048
|
+
const config = telemetry?.[stage];
|
|
1049
|
+
if (config === false) return void 0;
|
|
1050
|
+
if (config === void 0 || config === "self-hosted") return 1;
|
|
1051
|
+
return sampleRateOf(stage, config.sampleRate);
|
|
1052
|
+
}
|
|
1053
|
+
//#endregion
|
|
1054
|
+
//#region src/workspace/stages.ts
|
|
1055
|
+
/**
|
|
1056
|
+
* What `gkm test` runs as. The CLI's own, so no project stage may take it:
|
|
1057
|
+
* its containers are torn down and rebuilt between runs.
|
|
1058
|
+
*/
|
|
1059
|
+
const TEST_STAGE = "test";
|
|
1060
|
+
/** A stage becomes part of every physical name, so it has to fit in one. */
|
|
1061
|
+
const STAGE_NAME = /^[a-z][a-z0-9-]*$/;
|
|
1062
|
+
/** Everything wrong with one stage name, or nothing. */
|
|
1063
|
+
function nameProblems(name) {
|
|
1064
|
+
const problems = [];
|
|
1065
|
+
if (!STAGE_NAME.test(name)) problems.push(`stage "${name}" must be lowercase letters, digits and hyphens, starting with a letter`);
|
|
1066
|
+
if (name === "test") problems.push(`stage "${TEST_STAGE}" is reserved for gkm test`);
|
|
1067
|
+
return problems;
|
|
1068
|
+
}
|
|
1069
|
+
/** Everything wrong with a list of deployed stages on its own. */
|
|
1070
|
+
function deployedProblems(deployed) {
|
|
1071
|
+
const problems = deployed.flatMap(nameProblems);
|
|
1072
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1073
|
+
for (const name of deployed) {
|
|
1074
|
+
if (seen.has(name)) problems.push(`stage "${name}" is listed twice`);
|
|
1075
|
+
seen.add(name);
|
|
1076
|
+
}
|
|
1077
|
+
return problems;
|
|
1078
|
+
}
|
|
1079
|
+
/** Everything wrong with a stages block, or nothing. */
|
|
1080
|
+
function stageProblems(stages) {
|
|
1081
|
+
if (!stages) return ["`stages` is required, e.g. stages: { local: 'dev', deployed: ['prod'] }"];
|
|
1082
|
+
const { local, deployed, protected: kept = [] } = stages;
|
|
1083
|
+
const problems = [...nameProblems(local), ...deployedProblems(deployed)];
|
|
1084
|
+
if (deployed.includes(local)) problems.push(`"${local}" is both the local stage and a deployed one; they would share secrets`);
|
|
1085
|
+
for (const name of kept) if (!deployed.includes(name)) problems.push(`protected stage "${name}" is not a deployed stage`);
|
|
1086
|
+
return problems;
|
|
1087
|
+
}
|
|
1088
|
+
/** The stages block, or an error naming everything wrong with it. */
|
|
1089
|
+
function validateStages(stages) {
|
|
1090
|
+
const problems = stageProblems(stages);
|
|
1091
|
+
if (problems.length) throw new InvalidStages(problems);
|
|
1092
|
+
return stages;
|
|
1093
|
+
}
|
|
1094
|
+
/** Refuses a stage the project does not deploy to. */
|
|
1095
|
+
function assertDeployedStage(stages, stage) {
|
|
1096
|
+
if (!stages.deployed.includes(stage)) throw new UndeclaredStage(stage, stages.deployed);
|
|
1097
|
+
}
|
|
1098
|
+
/** A stages block — in gkm.config.ts or from `gkm init`'s flags — that breaks a rule. */
|
|
1099
|
+
var InvalidStages = class extends require_errors.GkmError {
|
|
1100
|
+
problems;
|
|
1101
|
+
constructor(problems) {
|
|
1102
|
+
super(`Invalid stages:\n ${problems.join("\n ")}`);
|
|
1103
|
+
this.problems = problems;
|
|
1104
|
+
this.name = "InvalidStages";
|
|
1105
|
+
}
|
|
1106
|
+
};
|
|
1107
|
+
/**
|
|
1108
|
+
* A stage the project does not deploy to. Refused before anything is
|
|
1109
|
+
* provisioned, so a typo does not become a second environment.
|
|
1110
|
+
*/
|
|
1111
|
+
var UndeclaredStage = class extends require_errors.GkmError {
|
|
1112
|
+
stage;
|
|
1113
|
+
deployed;
|
|
1114
|
+
constructor(stage, deployed) {
|
|
1115
|
+
super(`"${stage}" is not a deployed stage. gkm.config.ts deploys to: ${deployed.join(", ") || "nothing yet"}.`);
|
|
1116
|
+
this.stage = stage;
|
|
1117
|
+
this.deployed = deployed;
|
|
1118
|
+
this.name = "UndeclaredStage";
|
|
1119
|
+
}
|
|
1120
|
+
};
|
|
1121
|
+
/**
|
|
1122
|
+
* A per-stage setting keyed by a stage the workspace does not deploy: a typo,
|
|
1123
|
+
* a stage since removed, or the local stage, which none of them apply to.
|
|
1124
|
+
*/
|
|
1125
|
+
var UnknownStageKey = class extends require_errors.GkmError {
|
|
1126
|
+
setting;
|
|
1127
|
+
stage;
|
|
1128
|
+
deployed;
|
|
1129
|
+
local;
|
|
1130
|
+
constructor(setting, stage, deployed, local) {
|
|
1131
|
+
super(`${setting} names the stage '${stage}', ` + (stage === local ? `the local stage — ${setting} is read for deployed stages only. ` : "which is not one of this workspace's deployed stages. ") + `It takes ${deployed.length > 0 ? deployed.map((s) => `'${s}'`).join(", ") : "no stage: stages.deployed is empty"} — the stages in stages.deployed.`);
|
|
1132
|
+
this.setting = setting;
|
|
1133
|
+
this.stage = stage;
|
|
1134
|
+
this.deployed = deployed;
|
|
1135
|
+
this.local = local;
|
|
1136
|
+
this.name = "UnknownStageKey";
|
|
1137
|
+
}
|
|
1138
|
+
};
|
|
1139
|
+
/**
|
|
1140
|
+
* The first key of a per-stage map that is not a deployed stage.
|
|
1141
|
+
*
|
|
1142
|
+
* @throws {UnknownStageKey}
|
|
1143
|
+
*/
|
|
1144
|
+
function assertDeployedStageKeys(setting, map, stages) {
|
|
1145
|
+
if (!map || !stages?.deployed) return;
|
|
1146
|
+
for (const key of Object.keys(map)) if (!stages.deployed.includes(key)) throw new UnknownStageKey(setting, key, stages.deployed, stages.local);
|
|
1147
|
+
}
|
|
1148
|
+
//#endregion
|
|
1149
|
+
//#region src/workspace/schema.ts
|
|
1150
|
+
/** Routes are a glob, or a list of them. */
|
|
1151
|
+
const RoutesSchema = require_schemas.union([require_schemas.string(), require_schemas.array(require_schemas.string())]);
|
|
1152
|
+
/**
|
|
1153
|
+
* Telescope configuration schema.
|
|
1154
|
+
*/
|
|
1155
|
+
const TelescopeConfigSchema = require_schemas.object({
|
|
1156
|
+
enabled: require_schemas.boolean().optional(),
|
|
1157
|
+
port: require_schemas.number().optional(),
|
|
1158
|
+
path: require_schemas.string().optional(),
|
|
1159
|
+
ignore: require_schemas.array(require_schemas.string()).optional(),
|
|
1160
|
+
recordBody: require_schemas.boolean().optional(),
|
|
1161
|
+
maxEntries: require_schemas.number().optional(),
|
|
1162
|
+
websocket: require_schemas.boolean().optional()
|
|
1163
|
+
});
|
|
1164
|
+
/**
|
|
1165
|
+
* OpenAPI configuration schema.
|
|
1166
|
+
*/
|
|
1167
|
+
const OpenApiConfigSchema = require_schemas.object({
|
|
1168
|
+
enabled: require_schemas.boolean().optional(),
|
|
1169
|
+
title: require_schemas.string().optional(),
|
|
1170
|
+
version: require_schemas.string().optional(),
|
|
1171
|
+
description: require_schemas.string().optional()
|
|
1172
|
+
});
|
|
1173
|
+
/**
|
|
1174
|
+
* Hooks configuration schema.
|
|
1175
|
+
*/
|
|
1176
|
+
const HooksConfigSchema = require_schemas.object({ server: require_schemas.string().optional() });
|
|
1177
|
+
/**
|
|
1178
|
+
* Backend framework schema for non-gkm apps.
|
|
1179
|
+
*/
|
|
1180
|
+
const BackendFrameworkSchema = require_schemas._enum([
|
|
1181
|
+
"hono",
|
|
1182
|
+
"better-auth",
|
|
1183
|
+
"express",
|
|
1184
|
+
"fastify"
|
|
1185
|
+
]);
|
|
1186
|
+
/**
|
|
1187
|
+
* Frontend framework schema.
|
|
1188
|
+
*/
|
|
1189
|
+
const FrontendFrameworkSchema = require_schemas._enum([
|
|
1190
|
+
"nextjs",
|
|
1191
|
+
"remix",
|
|
1192
|
+
"vite",
|
|
1193
|
+
"tanstack-start"
|
|
1194
|
+
]);
|
|
1195
|
+
/**
|
|
1196
|
+
* Mobile framework schema.
|
|
1197
|
+
*/
|
|
1198
|
+
const MobileFrameworkSchema = require_schemas._enum(["expo"]);
|
|
1199
|
+
/**
|
|
1200
|
+
* Combined framework schema (backend, frontend, or mobile).
|
|
1201
|
+
*/
|
|
1202
|
+
const FrameworkSchema = require_schemas.union([
|
|
1203
|
+
BackendFrameworkSchema,
|
|
1204
|
+
FrontendFrameworkSchema,
|
|
1205
|
+
MobileFrameworkSchema
|
|
1206
|
+
]);
|
|
1207
|
+
/** Frontend framework values, kept in sync with FrontendFrameworkSchema. */
|
|
1208
|
+
const FRONTEND_FRAMEWORKS = [
|
|
1209
|
+
"nextjs",
|
|
1210
|
+
"remix",
|
|
1211
|
+
"vite",
|
|
1212
|
+
"tanstack-start"
|
|
1213
|
+
];
|
|
1214
|
+
/** Mobile framework values, kept in sync with MobileFrameworkSchema. */
|
|
1215
|
+
const MOBILE_FRAMEWORKS = ["expo"];
|
|
1216
|
+
/**
|
|
1217
|
+
* A deploy target's name. Which names exist is the workspace's to say — the
|
|
1218
|
+
* built-ins and its own `deploy.targets` — so it is checked against both
|
|
1219
|
+
* once the whole config is read.
|
|
1220
|
+
*/
|
|
1221
|
+
const DeployTargetSchema = require_schemas.string().min(1);
|
|
1222
|
+
/**
|
|
1223
|
+
* The built-in targets a config may name, both deployed by `gkm deploy`:
|
|
1224
|
+
* `dokploy`, and `sst`, which runs `sst deploy` on the manifest `gkm build`
|
|
1225
|
+
* writes.
|
|
1226
|
+
*/
|
|
1227
|
+
const SUPPORTED_DEPLOY_TARGETS = require_define.configurableBuiltins();
|
|
1228
|
+
/** Built-in names reserved for targets that do not exist yet. */
|
|
1229
|
+
const PHASE_2_DEPLOY_TARGETS = Object.entries(require_define.BUILTIN_TARGETS).filter(([, target]) => target.status === "planned").map(([name]) => name);
|
|
1230
|
+
/**
|
|
1231
|
+
* Whether a config may name `target`: a usable built-in, or one of its own
|
|
1232
|
+
* `deploy.targets`.
|
|
1233
|
+
*/
|
|
1234
|
+
function isDeployTargetSupported(target, targets = {}) {
|
|
1235
|
+
return SUPPORTED_DEPLOY_TARGETS.includes(target) || Object.hasOwn(targets, target);
|
|
1236
|
+
}
|
|
1237
|
+
/**
|
|
1238
|
+
* Check if a deploy target is planned for Phase 2.
|
|
1239
|
+
*/
|
|
1240
|
+
function isPhase2DeployTarget(target) {
|
|
1241
|
+
return PHASE_2_DEPLOY_TARGETS.includes(target);
|
|
1242
|
+
}
|
|
1243
|
+
/**
|
|
1244
|
+
* Get error message for unsupported deploy targets.
|
|
1245
|
+
*/
|
|
1246
|
+
function getDeployTargetError(target, appName) {
|
|
1247
|
+
if (isPhase2DeployTarget(target)) return `Deploy target "${target}"${appName ? ` for app "${appName}"` : ""} is coming in Phase 2. Currently "dokploy" and "sst" are supported.`;
|
|
1248
|
+
return `Unknown deploy target: ${target}. Built in: ${SUPPORTED_DEPLOY_TARGETS.join(", ")}; any other is named in deploy.targets with the package that provides it. Coming in Phase 2: ${PHASE_2_DEPLOY_TARGETS.join(", ")}.`;
|
|
1249
|
+
}
|
|
1250
|
+
/**
|
|
1251
|
+
* One `deploy.targets` entry: a package name, a target object, or either
|
|
1252
|
+
* with options.
|
|
1253
|
+
*/
|
|
1254
|
+
const DeployTargetEntrySchema = require_schemas.union([
|
|
1255
|
+
require_schemas.string().min(1, "A target package name cannot be empty"),
|
|
1256
|
+
require_schemas.custom((value) => require_define.isDeployTarget(value), { message: "Expected a package name, a target (defineTarget), or [package or target, options]" }),
|
|
1257
|
+
require_schemas.tuple([require_schemas.union([require_schemas.string().min(1), require_schemas.custom((value) => require_define.isDeployTarget(value), { message: "Expected a package name or a target (defineTarget)" })]), require_schemas.unknown()])
|
|
1258
|
+
]);
|
|
1259
|
+
/**
|
|
1260
|
+
* Dokploy workspace configuration schema.
|
|
1261
|
+
* Supports either a single endpoint or per-stage endpoints.
|
|
1262
|
+
*/
|
|
1263
|
+
const DokployWorkspaceConfigSchema = require_schemas.object({
|
|
1264
|
+
/** Single endpoint for all stages */
|
|
1265
|
+
endpoint: require_schemas.url("Dokploy endpoint must be a valid URL").optional(),
|
|
1266
|
+
/** Per-stage endpoints (stage name -> endpoint URL) */
|
|
1267
|
+
endpoints: require_schemas.record(require_schemas.string(), require_schemas.url("Endpoint must be a valid URL")).optional(),
|
|
1268
|
+
registryId: require_schemas.string().optional(),
|
|
1269
|
+
/** Dokploy's own backup destination for the stage's Postgres. */
|
|
1270
|
+
backups: require_schemas.lazy(() => DokployBackupsConfigSchema).optional(),
|
|
1271
|
+
verify: require_schemas.object({
|
|
1272
|
+
deploymentTimeoutMs: require_schemas.number().int().positive().optional(),
|
|
1273
|
+
healthCheckPath: require_schemas.string().startsWith("/", "healthCheckPath must start with /").optional(),
|
|
1274
|
+
healthyAfter: require_schemas.number().int().positive().optional(),
|
|
1275
|
+
intervalMs: require_schemas.number().int().positive().optional(),
|
|
1276
|
+
healthTimeoutMs: require_schemas.number().int().positive().optional()
|
|
1277
|
+
}).optional()
|
|
1278
|
+
}).refine((data) => data.endpoint || data.endpoints, { message: "Either endpoint or endpoints must be provided" });
|
|
1279
|
+
/**
|
|
1280
|
+
* Valid AWS regions.
|
|
1281
|
+
*/
|
|
1282
|
+
const AwsRegionSchema = require_schemas._enum([
|
|
1283
|
+
"us-east-1",
|
|
1284
|
+
"us-east-2",
|
|
1285
|
+
"us-west-1",
|
|
1286
|
+
"us-west-2",
|
|
1287
|
+
"af-south-1",
|
|
1288
|
+
"ap-east-1",
|
|
1289
|
+
"ap-south-1",
|
|
1290
|
+
"ap-south-2",
|
|
1291
|
+
"ap-southeast-1",
|
|
1292
|
+
"ap-southeast-2",
|
|
1293
|
+
"ap-southeast-3",
|
|
1294
|
+
"ap-southeast-4",
|
|
1295
|
+
"ap-northeast-1",
|
|
1296
|
+
"ap-northeast-2",
|
|
1297
|
+
"ap-northeast-3",
|
|
1298
|
+
"ca-central-1",
|
|
1299
|
+
"eu-central-1",
|
|
1300
|
+
"eu-central-2",
|
|
1301
|
+
"eu-west-1",
|
|
1302
|
+
"eu-west-2",
|
|
1303
|
+
"eu-west-3",
|
|
1304
|
+
"eu-south-1",
|
|
1305
|
+
"eu-south-2",
|
|
1306
|
+
"eu-north-1",
|
|
1307
|
+
"me-south-1",
|
|
1308
|
+
"me-central-1",
|
|
1309
|
+
"sa-east-1"
|
|
1310
|
+
]);
|
|
1311
|
+
/**
|
|
1312
|
+
* DNS record types supported across providers.
|
|
1313
|
+
*/
|
|
1314
|
+
const DnsRecordTypeSchema = require_schemas._enum([
|
|
1315
|
+
"A",
|
|
1316
|
+
"AAAA",
|
|
1317
|
+
"CNAME",
|
|
1318
|
+
"MX",
|
|
1319
|
+
"TXT",
|
|
1320
|
+
"NS",
|
|
1321
|
+
"SRV",
|
|
1322
|
+
"CAA"
|
|
1323
|
+
]);
|
|
1324
|
+
require_schemas.object({
|
|
1325
|
+
/** Subdomain name (e.g., 'api' for api.example.com, '@' for root) */
|
|
1326
|
+
name: require_schemas.string(),
|
|
1327
|
+
/** Record type */
|
|
1328
|
+
type: DnsRecordTypeSchema,
|
|
1329
|
+
/** TTL in seconds */
|
|
1330
|
+
ttl: require_schemas.number().int().positive(),
|
|
1331
|
+
/** Record values */
|
|
1332
|
+
values: require_schemas.array(require_schemas.string())
|
|
1333
|
+
});
|
|
1334
|
+
/**
|
|
1335
|
+
* A DNS record to create or update.
|
|
1336
|
+
*/
|
|
1337
|
+
const UpsertDnsRecordSchema = require_schemas.object({
|
|
1338
|
+
/** Subdomain name (e.g., 'api' for api.example.com, '@' for root) */
|
|
1339
|
+
name: require_schemas.string(),
|
|
1340
|
+
/** Record type */
|
|
1341
|
+
type: DnsRecordTypeSchema,
|
|
1342
|
+
/** TTL in seconds */
|
|
1343
|
+
ttl: require_schemas.number().int().positive(),
|
|
1344
|
+
/** Record value (IP address, hostname, etc.) */
|
|
1345
|
+
value: require_schemas.string()
|
|
1346
|
+
});
|
|
1347
|
+
require_schemas.object({
|
|
1348
|
+
/** The record that was upserted */
|
|
1349
|
+
record: UpsertDnsRecordSchema,
|
|
1350
|
+
/** Whether the record was created (true) or updated (false) */
|
|
1351
|
+
created: require_schemas.boolean(),
|
|
1352
|
+
/** Whether the record already existed with the same value */
|
|
1353
|
+
unchanged: require_schemas.boolean()
|
|
1354
|
+
});
|
|
1355
|
+
/**
|
|
1356
|
+
* How a domain's hosts point at a compose stage's server: an A (and AAAA)
|
|
1357
|
+
* record each (`'a'`, the default), or one A record for `target` and a CNAME
|
|
1358
|
+
* to it for every other host but the apex. `target` is one name, or one per
|
|
1359
|
+
* stage; it must be under the domain.
|
|
1360
|
+
*/
|
|
1361
|
+
const DnsRecordsModeSchema = require_schemas.union([require_schemas.literal("a"), require_schemas.object({
|
|
1362
|
+
mode: require_schemas.literal("cname"),
|
|
1363
|
+
target: require_schemas.union([require_schemas.string(), require_schemas.record(require_schemas.string(), require_schemas.string())])
|
|
1364
|
+
}).strict()]);
|
|
1365
|
+
/**
|
|
1366
|
+
* Hostinger DNS provider config (without domain - domain is the record key).
|
|
1367
|
+
*/
|
|
1368
|
+
const HostingerDnsProviderSchema = require_schemas.object({
|
|
1369
|
+
provider: require_schemas.literal("hostinger"),
|
|
1370
|
+
/** How the hosts point at a compose stage's server — see {@link DnsRecordsModeSchema}. */
|
|
1371
|
+
records: DnsRecordsModeSchema.optional(),
|
|
1372
|
+
/** TTL in seconds (default: 300) */
|
|
1373
|
+
ttl: require_schemas.number().int().positive().optional()
|
|
1374
|
+
});
|
|
1375
|
+
/**
|
|
1376
|
+
* Route53 DNS provider config (without domain - domain is the record key).
|
|
1377
|
+
*/
|
|
1378
|
+
const Route53DnsProviderSchema = require_schemas.object({
|
|
1379
|
+
provider: require_schemas.literal("route53"),
|
|
1380
|
+
/** How the hosts point at a compose stage's server — see {@link DnsRecordsModeSchema}. */
|
|
1381
|
+
records: DnsRecordsModeSchema.optional(),
|
|
1382
|
+
/** AWS region (optional - uses AWS_REGION env var if not provided) */
|
|
1383
|
+
region: AwsRegionSchema.optional(),
|
|
1384
|
+
/** AWS profile name (optional - uses default credential chain if not provided) */
|
|
1385
|
+
profile: require_schemas.string().optional(),
|
|
1386
|
+
/** Hosted zone ID (optional - auto-detected from domain if not provided) */
|
|
1387
|
+
hostedZoneId: require_schemas.string().optional(),
|
|
1388
|
+
/** TTL in seconds (default: 300) */
|
|
1389
|
+
ttl: require_schemas.number().int().positive().optional()
|
|
1390
|
+
});
|
|
1391
|
+
/**
|
|
1392
|
+
* GoDaddy DNS provider config (without domain - domain is the record key).
|
|
1393
|
+
*
|
|
1394
|
+
* Credentials: `GODADDY_API_TOKEN` (a Personal Access Token scoped to
|
|
1395
|
+
* `domains.dns:update`), else `gkm login --provider godaddy`. GoDaddy refuses
|
|
1396
|
+
* a TTL under 600 seconds.
|
|
1397
|
+
*/
|
|
1398
|
+
const GoDaddyDnsProviderSchema = require_schemas.object({
|
|
1399
|
+
provider: require_schemas.literal("godaddy"),
|
|
1400
|
+
/** How the hosts point at a compose stage's server — see {@link DnsRecordsModeSchema}. */
|
|
1401
|
+
records: DnsRecordsModeSchema.optional(),
|
|
1402
|
+
/** TTL in seconds (default and minimum: 600) */
|
|
1403
|
+
ttl: require_schemas.number().int().min(600, "GoDaddy's minimum TTL is 600 seconds").optional()
|
|
1404
|
+
});
|
|
1405
|
+
/**
|
|
1406
|
+
* Cloudflare DNS provider config (placeholder for future).
|
|
1407
|
+
*/
|
|
1408
|
+
const CloudflareDnsProviderSchema = require_schemas.object({
|
|
1409
|
+
provider: require_schemas.literal("cloudflare"),
|
|
1410
|
+
/** How the hosts point at a compose stage's server — see {@link DnsRecordsModeSchema}. */
|
|
1411
|
+
records: DnsRecordsModeSchema.optional(),
|
|
1412
|
+
/** TTL in seconds (default: 300) */
|
|
1413
|
+
ttl: require_schemas.number().int().positive().optional()
|
|
1414
|
+
});
|
|
1415
|
+
/**
|
|
1416
|
+
* Manual DNS configuration (user handles DNS themselves).
|
|
1417
|
+
*/
|
|
1418
|
+
const ManualDnsProviderSchema = require_schemas.object({
|
|
1419
|
+
provider: require_schemas.literal("manual"),
|
|
1420
|
+
/** How the hosts point at a compose stage's server — see {@link DnsRecordsModeSchema}. */
|
|
1421
|
+
records: DnsRecordsModeSchema.optional()
|
|
1422
|
+
});
|
|
1423
|
+
/**
|
|
1424
|
+
* Custom DNS provider config (user-provided implementation).
|
|
1425
|
+
*/
|
|
1426
|
+
const CustomDnsProviderSchema = require_schemas.object({
|
|
1427
|
+
/** Custom DnsProvider implementation */
|
|
1428
|
+
provider: require_schemas.custom((val) => typeof val === "object" && val !== null && typeof val.name === "string" && typeof val.getRecords === "function" && typeof val.upsertRecords === "function", { message: "Custom DNS provider must implement name, getRecords(), and upsertRecords() methods" }),
|
|
1429
|
+
/** How the hosts point at a compose stage's server — see {@link DnsRecordsModeSchema}. */
|
|
1430
|
+
records: DnsRecordsModeSchema.optional(),
|
|
1431
|
+
/** TTL in seconds (default: 300) */
|
|
1432
|
+
ttl: require_schemas.number().int().positive().optional()
|
|
1433
|
+
});
|
|
1434
|
+
/**
|
|
1435
|
+
* Built-in DNS provider config (discriminated union).
|
|
1436
|
+
*/
|
|
1437
|
+
const BuiltInDnsProviderSchema = require_schemas.discriminatedUnion("provider", [
|
|
1438
|
+
HostingerDnsProviderSchema,
|
|
1439
|
+
Route53DnsProviderSchema,
|
|
1440
|
+
GoDaddyDnsProviderSchema,
|
|
1441
|
+
CloudflareDnsProviderSchema,
|
|
1442
|
+
ManualDnsProviderSchema
|
|
1443
|
+
]);
|
|
1444
|
+
/**
|
|
1445
|
+
* Single DNS provider config (for one domain).
|
|
1446
|
+
*/
|
|
1447
|
+
const DnsProviderSchema = require_schemas.union([BuiltInDnsProviderSchema, CustomDnsProviderSchema]);
|
|
1448
|
+
/**
|
|
1449
|
+
* DNS configuration schema.
|
|
1450
|
+
*
|
|
1451
|
+
* Maps root domains to their DNS provider configuration.
|
|
1452
|
+
* Example:
|
|
1453
|
+
* ```
|
|
1454
|
+
* dns: {
|
|
1455
|
+
* 'geekmidas.dev': { provider: 'hostinger' },
|
|
1456
|
+
* 'geekmidas.com': { provider: 'route53' },
|
|
1457
|
+
* }
|
|
1458
|
+
* ```
|
|
1459
|
+
*
|
|
1460
|
+
* Supported providers:
|
|
1461
|
+
* - 'hostinger': Use Hostinger DNS API
|
|
1462
|
+
* - 'route53': Use AWS Route53
|
|
1463
|
+
* - 'godaddy': Use GoDaddy's v1 DNS API
|
|
1464
|
+
* - 'cloudflare': Use Cloudflare DNS API (future)
|
|
1465
|
+
* - 'manual': Don't create records, just print required records
|
|
1466
|
+
* - Custom: Provide a DnsProvider implementation
|
|
1467
|
+
*/
|
|
1468
|
+
const DnsConfigSchema = require_schemas.record(require_schemas.string(), DnsProviderSchema);
|
|
1469
|
+
const HostingerDnsConfigSchema = HostingerDnsProviderSchema.extend({ domain: require_schemas.string().min(1, "Domain is required") });
|
|
1470
|
+
const Route53DnsConfigSchema = Route53DnsProviderSchema.extend({ domain: require_schemas.string().min(1, "Domain is required") });
|
|
1471
|
+
const GoDaddyDnsConfigSchema = GoDaddyDnsProviderSchema.extend({ domain: require_schemas.string().min(1, "Domain is required") });
|
|
1472
|
+
const CloudflareDnsConfigSchema = CloudflareDnsProviderSchema.extend({ domain: require_schemas.string().min(1, "Domain is required") });
|
|
1473
|
+
const ManualDnsConfigSchema = ManualDnsProviderSchema.extend({ domain: require_schemas.string().min(1, "Domain is required") });
|
|
1474
|
+
const CustomDnsConfigSchema = CustomDnsProviderSchema.extend({ domain: require_schemas.string().min(1, "Domain is required") });
|
|
1475
|
+
const BuiltInDnsConfigSchema = require_schemas.discriminatedUnion("provider", [
|
|
1476
|
+
HostingerDnsConfigSchema,
|
|
1477
|
+
Route53DnsConfigSchema,
|
|
1478
|
+
GoDaddyDnsConfigSchema,
|
|
1479
|
+
CloudflareDnsConfigSchema,
|
|
1480
|
+
ManualDnsConfigSchema
|
|
1481
|
+
]);
|
|
1482
|
+
const LegacyDnsConfigSchema = require_schemas.union([BuiltInDnsConfigSchema, CustomDnsConfigSchema]);
|
|
1483
|
+
require_schemas.union([DnsConfigSchema, LegacyDnsConfigSchema]);
|
|
1484
|
+
/**
|
|
1485
|
+
* Backups configuration schema.
|
|
1486
|
+
*
|
|
1487
|
+
* Configures automatic backup destinations for database services.
|
|
1488
|
+
* On first deploy, creates S3 bucket with unique name and IAM credentials.
|
|
1489
|
+
*/
|
|
1490
|
+
const DokployBackupsConfigSchema = require_schemas.object({
|
|
1491
|
+
/** Backup storage type (currently only 's3' supported) */
|
|
1492
|
+
type: require_schemas.literal("s3"),
|
|
1493
|
+
/** AWS profile name for creating bucket/IAM resources */
|
|
1494
|
+
profile: require_schemas.string().optional(),
|
|
1495
|
+
/** AWS region for the backup bucket */
|
|
1496
|
+
region: AwsRegionSchema,
|
|
1497
|
+
/** Cron schedule for backups (default: '0 2 * * *' = 2 AM daily) */
|
|
1498
|
+
schedule: require_schemas.string().optional(),
|
|
1499
|
+
/** Number of backups to retain (default: 30) */
|
|
1500
|
+
retention: require_schemas.number().optional()
|
|
1501
|
+
});
|
|
1502
|
+
/**
|
|
1503
|
+
* `deploy.backups` — when each deployed compose stage's databases are backed
|
|
1504
|
+
* up, and how long a backup is kept. The rules are `checkStageBackups`', so
|
|
1505
|
+
* the schema and a deploy refuse the same entries with the same words.
|
|
1506
|
+
*/
|
|
1507
|
+
const BackupsConfigSchema = require_schemas.record(require_schemas.string(), require_schemas.unknown()).superRefine((entries, ctx) => {
|
|
1508
|
+
for (const [stage, value] of Object.entries(entries)) try {
|
|
1509
|
+
checkStageBackups(stage, value);
|
|
1510
|
+
} catch (error) {
|
|
1511
|
+
ctx.addIssue({
|
|
1512
|
+
code: "custom",
|
|
1513
|
+
message: error instanceof Error ? error.message : String(error),
|
|
1514
|
+
path: [stage]
|
|
1515
|
+
});
|
|
1516
|
+
}
|
|
1517
|
+
});
|
|
1518
|
+
/** `deploy.compose` — what the compose target runs beside the apps. */
|
|
1519
|
+
const ComposeWorkspaceConfigSchema = require_schemas.object({
|
|
1520
|
+
proxy: require_schemas.union([require_schemas._enum(["caddy", "traefik"]), require_schemas.record(require_schemas.string(), require_schemas._enum(["caddy", "traefik"]))]).optional(),
|
|
1521
|
+
tls: require_schemas.record(require_schemas.string(), require_schemas.object({
|
|
1522
|
+
certFile: require_schemas.string().min(1),
|
|
1523
|
+
keyFile: require_schemas.string().min(1)
|
|
1524
|
+
}).strict()).optional(),
|
|
1525
|
+
server: require_schemas.record(require_schemas.string(), require_schemas.object({
|
|
1526
|
+
user: require_schemas.string().min(1),
|
|
1527
|
+
host: require_schemas.string().min(1).optional(),
|
|
1528
|
+
port: require_schemas.number().int().min(1).max(65535).optional()
|
|
1529
|
+
}).strict()).optional()
|
|
1530
|
+
}).strict();
|
|
1531
|
+
const TelemetrySampleRate = require_schemas.number().optional();
|
|
1532
|
+
/**
|
|
1533
|
+
* `deploy.telemetry` — where each stage's telemetry goes. The rules past the
|
|
1534
|
+
* shape are `checkStageTelemetry`'s, so the schema and a deploy refuse the
|
|
1535
|
+
* same configs with the same words.
|
|
1536
|
+
*/
|
|
1537
|
+
const TelemetryConfigSchema = require_schemas.record(require_schemas.string(), require_schemas.union([
|
|
1538
|
+
require_schemas.literal(false),
|
|
1539
|
+
require_schemas.literal("self-hosted"),
|
|
1540
|
+
require_schemas.object({
|
|
1541
|
+
provider: require_schemas.literal("self-hosted"),
|
|
1542
|
+
port: require_schemas.number().optional(),
|
|
1543
|
+
retentionDays: require_schemas.number().optional(),
|
|
1544
|
+
public: require_schemas.object({ allow: require_schemas.array(require_schemas.string()) }).optional(),
|
|
1545
|
+
sampleRate: TelemetrySampleRate
|
|
1546
|
+
}).strict(),
|
|
1547
|
+
require_schemas.object({
|
|
1548
|
+
provider: require_schemas.literal("otlp"),
|
|
1549
|
+
endpoint: require_schemas.string(),
|
|
1550
|
+
headers: require_schemas.record(require_schemas.string(), require_schemas.string()).optional(),
|
|
1551
|
+
sampleRate: TelemetrySampleRate
|
|
1552
|
+
}).strict()
|
|
1553
|
+
])).superRefine((telemetry, ctx) => {
|
|
1554
|
+
for (const [stage, config] of Object.entries(telemetry)) try {
|
|
1555
|
+
checkStageTelemetry(stage, config);
|
|
1556
|
+
} catch (error) {
|
|
1557
|
+
ctx.addIssue({
|
|
1558
|
+
code: "custom",
|
|
1559
|
+
message: error instanceof Error ? error.message : String(error),
|
|
1560
|
+
path: [stage]
|
|
1561
|
+
});
|
|
1562
|
+
}
|
|
1563
|
+
});
|
|
1564
|
+
/**
|
|
1565
|
+
* `deploy.objects` — what backs each deployed stage's buckets. The rules are
|
|
1566
|
+
* `checkStageProvider`'s, so the schema and a provision run refuse the same
|
|
1567
|
+
* entries with the same words.
|
|
1568
|
+
*/
|
|
1569
|
+
const ObjectsConfigSchema = require_schemas.record(require_schemas.string(), require_schemas.unknown()).superRefine((entries, ctx) => {
|
|
1570
|
+
for (const [stage, value] of Object.entries(entries)) try {
|
|
1571
|
+
checkStageProvider("objects", stage, value);
|
|
1572
|
+
} catch (error) {
|
|
1573
|
+
ctx.addIssue({
|
|
1574
|
+
code: "custom",
|
|
1575
|
+
message: error instanceof Error ? error.message : String(error),
|
|
1576
|
+
path: [stage]
|
|
1577
|
+
});
|
|
1578
|
+
}
|
|
1579
|
+
});
|
|
1580
|
+
/**
|
|
1581
|
+
* Deploy configuration schema.
|
|
1582
|
+
*/
|
|
1583
|
+
const DeployConfigSchema = require_schemas.object({
|
|
1584
|
+
default: DeployTargetSchema.optional(),
|
|
1585
|
+
targets: require_schemas.record(require_schemas.string(), DeployTargetEntrySchema).superRefine((targets, ctx) => {
|
|
1586
|
+
for (const name of Object.keys(targets)) if (require_define.builtinTarget(name)) ctx.addIssue({
|
|
1587
|
+
code: "custom",
|
|
1588
|
+
message: `"${name}" is a built-in target; give this one a name of its own`,
|
|
1589
|
+
path: [name]
|
|
1590
|
+
});
|
|
1591
|
+
}).optional(),
|
|
1592
|
+
/** Whose deploy this is, on a target shared with other workspaces. */
|
|
1593
|
+
namespace: require_schemas.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "deploy.namespace must be lowercase letters and digits with single '-' between them").optional(),
|
|
1594
|
+
/** Where every target pushes and pulls the apps' images. */
|
|
1595
|
+
registry: require_schemas.string().min(1).optional(),
|
|
1596
|
+
dokploy: DokployWorkspaceConfigSchema.optional(),
|
|
1597
|
+
compose: ComposeWorkspaceConfigSchema.optional(),
|
|
1598
|
+
telemetry: TelemetryConfigSchema.optional(),
|
|
1599
|
+
objects: ObjectsConfigSchema.optional(),
|
|
1600
|
+
backups: BackupsConfigSchema.optional()
|
|
1601
|
+
});
|
|
1602
|
+
/**
|
|
1603
|
+
* Models configuration schema.
|
|
1604
|
+
*/
|
|
1605
|
+
const ModelsConfigSchema = require_schemas.object({
|
|
1606
|
+
path: require_schemas.string().optional(),
|
|
1607
|
+
schema: require_schemas._enum(["zod"]).optional()
|
|
1608
|
+
});
|
|
1609
|
+
/**
|
|
1610
|
+
* Shared configuration schema.
|
|
1611
|
+
*/
|
|
1612
|
+
const SharedConfigSchema = require_schemas.object({
|
|
1613
|
+
packages: require_schemas.array(require_schemas.string()).optional(),
|
|
1614
|
+
models: ModelsConfigSchema.optional()
|
|
1615
|
+
});
|
|
1616
|
+
/**
|
|
1617
|
+
* Secrets configuration schema.
|
|
1618
|
+
*/
|
|
1619
|
+
const StagesConfigSchema = require_schemas.object({
|
|
1620
|
+
local: require_schemas.string(),
|
|
1621
|
+
deployed: require_schemas.array(require_schemas.string()),
|
|
1622
|
+
protected: require_schemas.array(require_schemas.string()).optional()
|
|
1623
|
+
}).superRefine((stages, ctx) => {
|
|
1624
|
+
for (const message of stageProblems(stages)) ctx.addIssue({
|
|
1625
|
+
code: "custom",
|
|
1626
|
+
message
|
|
1627
|
+
});
|
|
1628
|
+
});
|
|
1629
|
+
const SecretsStoreSchema = require_schemas.union([
|
|
1630
|
+
require_schemas.literal("file"),
|
|
1631
|
+
require_schemas.object({
|
|
1632
|
+
provider: require_schemas.literal("s3"),
|
|
1633
|
+
/** A bucket that exists; omitted, the project bucket. */
|
|
1634
|
+
bucket: require_schemas.string().min(1).optional(),
|
|
1635
|
+
/** Omitted, the S3 deploy state's region. */
|
|
1636
|
+
region: AwsRegionSchema.optional(),
|
|
1637
|
+
/** Omitted, the S3 deploy state's prefix, else 'gkm'. */
|
|
1638
|
+
prefix: require_schemas.string().optional()
|
|
1639
|
+
}).strict(),
|
|
1640
|
+
require_schemas.object({
|
|
1641
|
+
provider: require_schemas.literal("ssm"),
|
|
1642
|
+
region: AwsRegionSchema
|
|
1643
|
+
}).strict(),
|
|
1644
|
+
require_schemas.object({
|
|
1645
|
+
provider: require_schemas.literal("secrets-manager"),
|
|
1646
|
+
region: AwsRegionSchema,
|
|
1647
|
+
kmsKeyId: require_schemas.string().min(1).optional()
|
|
1648
|
+
}).strict(),
|
|
1649
|
+
require_schemas.object({
|
|
1650
|
+
/** Any backend: an object implementing SecretsStore */
|
|
1651
|
+
provider: require_schemas.custom((val) => typeof val === "object" && val !== null && typeof val.name === "string" && typeof val.read === "function" && typeof val.write === "function", { message: "a secrets store has a name, read() and write()" }) })
|
|
1652
|
+
]);
|
|
1653
|
+
const SecretsConfigSchema = require_schemas.object({
|
|
1654
|
+
enabled: require_schemas.boolean().optional(),
|
|
1655
|
+
store: SecretsStoreSchema.optional(),
|
|
1656
|
+
algorithm: require_schemas.string().optional(),
|
|
1657
|
+
kdf: require_schemas._enum(["scrypt", "pbkdf2"]).optional()
|
|
1658
|
+
});
|
|
1659
|
+
/**
|
|
1660
|
+
* Local state provider config.
|
|
1661
|
+
*/
|
|
1662
|
+
const LocalStateConfigSchema = require_schemas.object({ provider: require_schemas.literal("local") });
|
|
1663
|
+
/**
|
|
1664
|
+
* SSM state provider config (requires region).
|
|
1665
|
+
*/
|
|
1666
|
+
const SSMStateConfigSchema = require_schemas.object({
|
|
1667
|
+
provider: require_schemas.literal("ssm"),
|
|
1668
|
+
/** AWS region (required for SSM provider) */
|
|
1669
|
+
region: AwsRegionSchema,
|
|
1670
|
+
/** AWS profile name (optional - uses default credential chain if not provided) */
|
|
1671
|
+
profile: require_schemas.string().optional()
|
|
1672
|
+
});
|
|
1673
|
+
/**
|
|
1674
|
+
* S3 state provider config: conditional writes and a lock object in a bucket.
|
|
1675
|
+
*/
|
|
1676
|
+
const S3StateConfigSchema = require_schemas.object({
|
|
1677
|
+
provider: require_schemas.literal("s3"),
|
|
1678
|
+
/**
|
|
1679
|
+
* Bucket the state lives in (must already exist). Omitted, the project
|
|
1680
|
+
* bucket, created by the first deploy.
|
|
1681
|
+
*/
|
|
1682
|
+
bucket: require_schemas.string().min(1).optional(),
|
|
1683
|
+
/** AWS region of the bucket */
|
|
1684
|
+
region: AwsRegionSchema,
|
|
1685
|
+
/** Key prefix inside the bucket (default: 'gkm') */
|
|
1686
|
+
prefix: require_schemas.string().optional(),
|
|
1687
|
+
/** AWS profile name (optional - uses default credential chain if not provided) */
|
|
1688
|
+
profile: require_schemas.string().optional()
|
|
1689
|
+
});
|
|
1690
|
+
/**
|
|
1691
|
+
* Custom state provider config (user-provided implementation).
|
|
1692
|
+
*/
|
|
1693
|
+
const CustomStateConfigSchema = require_schemas.object({
|
|
1694
|
+
/** Custom StateProvider implementation */
|
|
1695
|
+
provider: require_schemas.custom((val) => typeof val === "object" && val !== null && typeof val.read === "function" && typeof val.write === "function", { message: "Custom provider must implement read() and write() methods" }) });
|
|
1696
|
+
/**
|
|
1697
|
+
* Built-in state provider config (discriminated union).
|
|
1698
|
+
*/
|
|
1699
|
+
const BuiltInStateConfigSchema = require_schemas.discriminatedUnion("provider", [
|
|
1700
|
+
LocalStateConfigSchema,
|
|
1701
|
+
SSMStateConfigSchema,
|
|
1702
|
+
S3StateConfigSchema
|
|
1703
|
+
]);
|
|
1704
|
+
/**
|
|
1705
|
+
* State configuration schema.
|
|
1706
|
+
*
|
|
1707
|
+
* Configures how deployment state is stored.
|
|
1708
|
+
* - 'local': Store in .gkm/deploy-{stage}.json (default)
|
|
1709
|
+
* - 'ssm': Store in AWS SSM Parameter Store (requires region)
|
|
1710
|
+
* - 's3': Store in an S3 bucket with conditional writes (requires region; the
|
|
1711
|
+
* project bucket is created when no bucket is named)
|
|
1712
|
+
* - Custom: Provide a StateStore, or a StateProvider with read/write methods
|
|
1713
|
+
*/
|
|
1714
|
+
const StateConfigSchema = require_schemas.union([BuiltInStateConfigSchema, CustomStateConfigSchema]);
|
|
1715
|
+
/**
|
|
1716
|
+
* App configuration schema.
|
|
1717
|
+
*/
|
|
1718
|
+
const AppConfigSchema = require_schemas.object({
|
|
1719
|
+
type: require_schemas._enum([
|
|
1720
|
+
"backend",
|
|
1721
|
+
"web",
|
|
1722
|
+
"mobile"
|
|
1723
|
+
]).optional().default("backend"),
|
|
1724
|
+
path: require_schemas.string().min(1, "App path is required"),
|
|
1725
|
+
port: require_schemas.number().int().positive("Port must be a positive integer"),
|
|
1726
|
+
dependencies: require_schemas.array(require_schemas.string()).optional(),
|
|
1727
|
+
deploy: DeployTargetSchema.optional(),
|
|
1728
|
+
constructs: RoutesSchema.optional(),
|
|
1729
|
+
routes: RoutesSchema.optional(),
|
|
1730
|
+
functions: RoutesSchema.optional(),
|
|
1731
|
+
crons: RoutesSchema.optional(),
|
|
1732
|
+
queues: RoutesSchema.optional(),
|
|
1733
|
+
topics: RoutesSchema.optional(),
|
|
1734
|
+
subscribers: RoutesSchema.optional(),
|
|
1735
|
+
envParser: require_schemas.string().optional(),
|
|
1736
|
+
logger: require_schemas.string().optional(),
|
|
1737
|
+
hooks: HooksConfigSchema.optional(),
|
|
1738
|
+
telescope: require_schemas.union([
|
|
1739
|
+
require_schemas.string(),
|
|
1740
|
+
require_schemas.boolean(),
|
|
1741
|
+
TelescopeConfigSchema
|
|
1742
|
+
]).optional(),
|
|
1743
|
+
openapi: require_schemas.union([require_schemas.boolean(), OpenApiConfigSchema]).optional(),
|
|
1744
|
+
runtime: require_schemas._enum(["node", "bun"]).optional(),
|
|
1745
|
+
env: require_schemas.union([require_schemas.string(), require_schemas.array(require_schemas.string())]).optional(),
|
|
1746
|
+
entry: require_schemas.string().optional(),
|
|
1747
|
+
framework: FrameworkSchema.optional(),
|
|
1748
|
+
config: require_schemas.object({
|
|
1749
|
+
client: require_schemas.string().optional(),
|
|
1750
|
+
server: require_schemas.string().optional()
|
|
1751
|
+
}).optional()
|
|
1752
|
+
}).refine((data) => {
|
|
1753
|
+
if (data.type === "web") {
|
|
1754
|
+
if (!data.framework || !FRONTEND_FRAMEWORKS.includes(data.framework)) return false;
|
|
1755
|
+
}
|
|
1756
|
+
return true;
|
|
1757
|
+
}, {
|
|
1758
|
+
message: `Web apps must have a valid web framework (${FRONTEND_FRAMEWORKS.join(", ")})`,
|
|
1759
|
+
path: ["framework"]
|
|
1760
|
+
}).refine((data) => {
|
|
1761
|
+
if (data.type === "mobile") {
|
|
1762
|
+
if (!data.framework || !MOBILE_FRAMEWORKS.includes(data.framework)) return false;
|
|
1763
|
+
}
|
|
1764
|
+
return true;
|
|
1765
|
+
}, {
|
|
1766
|
+
message: `Mobile apps must have a valid mobile framework (${MOBILE_FRAMEWORKS.join(", ")})`,
|
|
1767
|
+
path: ["framework"]
|
|
1768
|
+
});
|
|
1769
|
+
/** What `gkm test` hands a feature test. */
|
|
1770
|
+
const TestConfigSchema = require_schemas.object({
|
|
1771
|
+
/** The folder of per-database factories; `test/factories` by default. */
|
|
1772
|
+
factories: require_schemas.string().optional() }).strict();
|
|
1773
|
+
/** How `gkm dev` is reached from outside the apps it runs. */
|
|
1774
|
+
const DevConfigSchema = require_schemas.object({
|
|
1775
|
+
/** Browser origins that may read the discovery endpoint. */
|
|
1776
|
+
allowedOrigins: require_schemas.array(require_schemas.url().refine((value) => URL.canParse(value) && new URL(value).origin === value, { message: "An origin is a scheme, host and port only, e.g. https://console.example.com — no path or trailing slash" })).optional(),
|
|
1777
|
+
/** The discovery endpoint's loopback port; 4983 by default. */
|
|
1778
|
+
discoveryPort: require_schemas.number().int().min(0).max(65535).optional()
|
|
1779
|
+
}).strict();
|
|
1780
|
+
/**
|
|
1781
|
+
* Workspace configuration schema.
|
|
1782
|
+
*/
|
|
1783
|
+
const WorkspaceConfigSchema = require_schemas.object({
|
|
1784
|
+
name: require_schemas.string().optional(),
|
|
1785
|
+
/**
|
|
1786
|
+
* Where the workspace's own constructs live, relative to its root.
|
|
1787
|
+
*
|
|
1788
|
+
* Infrastructure is a fact about the product, not about the process that
|
|
1789
|
+
* happens to import it — so a database two apps share is declared once,
|
|
1790
|
+
* at the top, and both find it. An app-level glob still works and is
|
|
1791
|
+
* additive, for something only one app uses.
|
|
1792
|
+
*/
|
|
1793
|
+
constructs: RoutesSchema.optional(),
|
|
1794
|
+
/**
|
|
1795
|
+
* Apps that nothing declares.
|
|
1796
|
+
*
|
|
1797
|
+
* Optional, and normally absent: a `site` is an app and so is a
|
|
1798
|
+
* `rest-api` that named one, so the list is read off the graph. This is
|
|
1799
|
+
* the escape hatch for what no construct describes — a mobile app, a
|
|
1800
|
+
* process someone runs by hand — and for overriding one field of a
|
|
1801
|
+
* derived app without restating it.
|
|
1802
|
+
*/
|
|
1803
|
+
apps: require_schemas.record(require_schemas.string(), AppConfigSchema).optional(),
|
|
1804
|
+
shared: SharedConfigSchema.optional(),
|
|
1805
|
+
/** Each deployed stage's base domain (stage name -> domain). */
|
|
1806
|
+
domains: require_schemas.record(require_schemas.string(), require_schemas.string()).optional(),
|
|
1807
|
+
/** Each root domain's DNS provider, and how its hosts are pointed. */
|
|
1808
|
+
dns: DnsConfigSchema.optional(),
|
|
1809
|
+
deploy: DeployConfigSchema.optional(),
|
|
1810
|
+
stages: StagesConfigSchema,
|
|
1811
|
+
secrets: SecretsConfigSchema.optional(),
|
|
1812
|
+
state: StateConfigSchema.optional(),
|
|
1813
|
+
test: TestConfigSchema.optional(),
|
|
1814
|
+
dev: DevConfigSchema.optional()
|
|
1815
|
+
}).strict().refine((data) => {
|
|
1816
|
+
const appNames = Object.keys(data.apps ?? {});
|
|
1817
|
+
for (const [appName, app] of Object.entries(data.apps ?? {})) for (const dep of app.dependencies ?? []) {
|
|
1818
|
+
if (!appNames.includes(dep)) return false;
|
|
1819
|
+
if (dep === appName) return false;
|
|
1820
|
+
}
|
|
1821
|
+
return true;
|
|
1822
|
+
}, { message: "App dependencies must reference existing apps and cannot be self-referential" }).refine((data) => {
|
|
1823
|
+
const appNames = Object.keys(data.apps ?? {});
|
|
1824
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1825
|
+
const recStack = /* @__PURE__ */ new Set();
|
|
1826
|
+
function hasCycle(app) {
|
|
1827
|
+
if (recStack.has(app)) return true;
|
|
1828
|
+
if (visited.has(app)) return false;
|
|
1829
|
+
visited.add(app);
|
|
1830
|
+
recStack.add(app);
|
|
1831
|
+
const deps = data.apps?.[app]?.dependencies ?? [];
|
|
1832
|
+
for (const dep of deps) if (hasCycle(dep)) return true;
|
|
1833
|
+
recStack.delete(app);
|
|
1834
|
+
return false;
|
|
1835
|
+
}
|
|
1836
|
+
for (const app of appNames) {
|
|
1837
|
+
visited.clear();
|
|
1838
|
+
recStack.clear();
|
|
1839
|
+
if (hasCycle(app)) return false;
|
|
1840
|
+
}
|
|
1841
|
+
return true;
|
|
1842
|
+
}, { message: "Circular dependencies detected between apps" }).superRefine((data, ctx) => {
|
|
1843
|
+
const defaultTarget = data.deploy?.default;
|
|
1844
|
+
const ownTargets = data.deploy?.targets ?? {};
|
|
1845
|
+
if (defaultTarget && !isDeployTargetSupported(defaultTarget, ownTargets)) {
|
|
1846
|
+
ctx.addIssue({
|
|
1847
|
+
code: "custom",
|
|
1848
|
+
message: getDeployTargetError(defaultTarget),
|
|
1849
|
+
path: ["deploy", "default"]
|
|
1850
|
+
});
|
|
1851
|
+
return;
|
|
1852
|
+
}
|
|
1853
|
+
for (const [appName, app] of Object.entries(data.apps ?? {})) if (app.deploy && !isDeployTargetSupported(app.deploy, ownTargets)) {
|
|
1854
|
+
ctx.addIssue({
|
|
1855
|
+
code: "custom",
|
|
1856
|
+
message: getDeployTargetError(app.deploy, appName),
|
|
1857
|
+
path: [
|
|
1858
|
+
"apps",
|
|
1859
|
+
appName,
|
|
1860
|
+
"deploy"
|
|
1861
|
+
]
|
|
1862
|
+
});
|
|
1863
|
+
return;
|
|
1864
|
+
}
|
|
1865
|
+
const store = data.secrets?.store;
|
|
1866
|
+
if (typeof store === "object" && store.provider === "s3") try {
|
|
1867
|
+
s3SecretsLocation(store, data.state);
|
|
1868
|
+
} catch (error) {
|
|
1869
|
+
ctx.addIssue({
|
|
1870
|
+
code: "custom",
|
|
1871
|
+
message: error instanceof Error ? error.message : String(error),
|
|
1872
|
+
path: ["secrets", "store"]
|
|
1873
|
+
});
|
|
1874
|
+
}
|
|
1875
|
+
try {
|
|
1876
|
+
checkComposeStages(data.deploy?.compose, data.stages);
|
|
1877
|
+
} catch (error) {
|
|
1878
|
+
ctx.addIssue({
|
|
1879
|
+
code: "custom",
|
|
1880
|
+
message: error instanceof Error ? error.message : String(error),
|
|
1881
|
+
path: ["deploy", "compose"]
|
|
1882
|
+
});
|
|
1883
|
+
}
|
|
1884
|
+
const perStage = [
|
|
1885
|
+
[
|
|
1886
|
+
"domains",
|
|
1887
|
+
["domains"],
|
|
1888
|
+
data.domains
|
|
1889
|
+
],
|
|
1890
|
+
[
|
|
1891
|
+
"deploy.objects",
|
|
1892
|
+
["deploy", "objects"],
|
|
1893
|
+
data.deploy?.objects
|
|
1894
|
+
],
|
|
1895
|
+
[
|
|
1896
|
+
"deploy.backups",
|
|
1897
|
+
["deploy", "backups"],
|
|
1898
|
+
data.deploy?.backups
|
|
1899
|
+
],
|
|
1900
|
+
[
|
|
1901
|
+
"deploy.telemetry",
|
|
1902
|
+
["deploy", "telemetry"],
|
|
1903
|
+
data.deploy?.telemetry
|
|
1904
|
+
],
|
|
1905
|
+
...Object.entries(data.dns ?? {}).map(([domain, entry]) => {
|
|
1906
|
+
const records = entry.records;
|
|
1907
|
+
const target = records && typeof records === "object" ? records.target : void 0;
|
|
1908
|
+
return [
|
|
1909
|
+
`dns['${domain}'].records.target`,
|
|
1910
|
+
[
|
|
1911
|
+
"dns",
|
|
1912
|
+
domain,
|
|
1913
|
+
"records",
|
|
1914
|
+
"target"
|
|
1915
|
+
],
|
|
1916
|
+
target && typeof target === "object" ? target : void 0
|
|
1917
|
+
];
|
|
1918
|
+
})
|
|
1919
|
+
];
|
|
1920
|
+
for (const [setting, path, map] of perStage) try {
|
|
1921
|
+
assertDeployedStageKeys(setting, map, data.stages);
|
|
1922
|
+
} catch (error) {
|
|
1923
|
+
ctx.addIssue({
|
|
1924
|
+
code: "custom",
|
|
1925
|
+
message: error instanceof Error ? error.message : String(error),
|
|
1926
|
+
path
|
|
1927
|
+
});
|
|
1928
|
+
}
|
|
1929
|
+
for (const [domain, entry] of Object.entries(data.dns ?? {})) try {
|
|
1930
|
+
checkDnsRecordsMode(domain, entry.records);
|
|
1931
|
+
} catch (error) {
|
|
1932
|
+
ctx.addIssue({
|
|
1933
|
+
code: "custom",
|
|
1934
|
+
message: error instanceof Error ? error.message : String(error),
|
|
1935
|
+
path: [
|
|
1936
|
+
"dns",
|
|
1937
|
+
domain,
|
|
1938
|
+
"records"
|
|
1939
|
+
]
|
|
1940
|
+
});
|
|
1941
|
+
}
|
|
1942
|
+
if (data.state?.provider === "ssm" && !data.name) ctx.addIssue({
|
|
1943
|
+
code: "custom",
|
|
1944
|
+
message: "Workspace name is required when using SSM state provider. Add \"name\" to your gkm.config.ts.",
|
|
1945
|
+
path: ["name"]
|
|
1946
|
+
});
|
|
1947
|
+
});
|
|
1948
|
+
/**
|
|
1949
|
+
* Validate workspace configuration.
|
|
1950
|
+
* Throws ZodError with detailed messages on validation failure.
|
|
1951
|
+
*/
|
|
1952
|
+
function validateWorkspaceConfig(config) {
|
|
1953
|
+
return WorkspaceConfigSchema.parse(config);
|
|
1954
|
+
}
|
|
1955
|
+
/**
|
|
1956
|
+
* Safe validation that returns result instead of throwing.
|
|
1957
|
+
*/
|
|
1958
|
+
function safeValidateWorkspaceConfig(config) {
|
|
1959
|
+
const result = WorkspaceConfigSchema.safeParse(config);
|
|
1960
|
+
if (result.success) return {
|
|
1961
|
+
success: true,
|
|
1962
|
+
data: result.data
|
|
1963
|
+
};
|
|
1964
|
+
return {
|
|
1965
|
+
success: false,
|
|
1966
|
+
error: result.error
|
|
1967
|
+
};
|
|
1968
|
+
}
|
|
1969
|
+
/**
|
|
1970
|
+
* Format Zod errors into user-friendly messages.
|
|
1971
|
+
*/
|
|
1972
|
+
function formatValidationErrors(error) {
|
|
1973
|
+
return `Workspace configuration validation failed:\n${error.issues.map((issue) => {
|
|
1974
|
+
const path = issue.path.join(".");
|
|
1975
|
+
return path ? ` - ${path}: ${issue.message}` : ` - ${issue.message}`;
|
|
1976
|
+
}).join("\n")}`;
|
|
1977
|
+
}
|
|
1978
|
+
//#endregion
|
|
1979
|
+
//#region src/workspace/types.ts
|
|
1980
|
+
/**
|
|
1981
|
+
* Type guard to check if a config is a WorkspaceConfig.
|
|
1982
|
+
*
|
|
1983
|
+
* @example
|
|
1984
|
+
* ```ts
|
|
1985
|
+
* const config = await loadConfig();
|
|
1986
|
+
* if (isWorkspaceConfig(config)) {
|
|
1987
|
+
* // config.apps is available
|
|
1988
|
+
* console.log(Object.keys(config.apps));
|
|
1989
|
+
* }
|
|
1990
|
+
* ```
|
|
1991
|
+
*/
|
|
1992
|
+
function isWorkspaceConfig(config) {
|
|
1993
|
+
if (typeof config !== "object" || config === null) return false;
|
|
1994
|
+
if ("apps" in config && typeof config.apps === "object") return true;
|
|
1995
|
+
return "constructs" in config && Object.keys(config).every((key) => WORKSPACE_KEYS.has(key));
|
|
1996
|
+
}
|
|
1997
|
+
/** Every top-level key a workspace config has — see `WorkspaceConfigSchema`. */
|
|
1998
|
+
const WORKSPACE_KEYS = /* @__PURE__ */ new Set([
|
|
1999
|
+
"name",
|
|
2000
|
+
"constructs",
|
|
2001
|
+
"apps",
|
|
2002
|
+
"shared",
|
|
2003
|
+
"domains",
|
|
2004
|
+
"dns",
|
|
2005
|
+
"deploy",
|
|
2006
|
+
"stages",
|
|
2007
|
+
"secrets",
|
|
2008
|
+
"state"
|
|
2009
|
+
]);
|
|
2010
|
+
//#endregion
|
|
2011
|
+
//#region src/workspace/publicEnv.ts
|
|
2012
|
+
/**
|
|
2013
|
+
* All public env-var prefixes the toolbox understands.
|
|
2014
|
+
*
|
|
2015
|
+
* Used when resolving incoming var names back to a dependency
|
|
2016
|
+
* (e.g. `VITE_AUTH_URL` → `auth`). A var matching any of these
|
|
2017
|
+
* is considered safe to inline into a client bundle.
|
|
2018
|
+
*/
|
|
2019
|
+
const PUBLIC_ENV_PREFIXES = [
|
|
2020
|
+
"NEXT_PUBLIC_",
|
|
2021
|
+
"VITE_",
|
|
2022
|
+
"EXPO_PUBLIC_"
|
|
2023
|
+
];
|
|
2024
|
+
/**
|
|
2025
|
+
* Resolve the public env-var prefix that a frontend framework's bundler
|
|
2026
|
+
* inlines into client code at build time:
|
|
2027
|
+
*
|
|
2028
|
+
* - `nextjs` → `NEXT_PUBLIC_`
|
|
2029
|
+
* - `vite` → `VITE_`
|
|
2030
|
+
* - `tanstack-start` → `VITE_` (uses Vite under the hood)
|
|
2031
|
+
* - `expo` → `EXPO_PUBLIC_`
|
|
2032
|
+
* - `remix` → `''` (Remix exposes via loaders, no prefix convention)
|
|
2033
|
+
*
|
|
2034
|
+
* For backend frameworks or unspecified, falls back to `NEXT_PUBLIC_` to
|
|
2035
|
+
* preserve the historical default. Backend apps never read these vars
|
|
2036
|
+
* from a bundle, so the prefix only affects scaffolding/symmetry.
|
|
2037
|
+
*/
|
|
2038
|
+
function getPublicEnvPrefix(framework) {
|
|
2039
|
+
switch (framework) {
|
|
2040
|
+
case "vite":
|
|
2041
|
+
case "tanstack-start": return "VITE_";
|
|
2042
|
+
case "expo": return "EXPO_PUBLIC_";
|
|
2043
|
+
case "remix": return "";
|
|
2044
|
+
case "nextjs": return "NEXT_PUBLIC_";
|
|
2045
|
+
default: return "NEXT_PUBLIC_";
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
/**
|
|
2049
|
+
* Strip a known public prefix from a var name.
|
|
2050
|
+
* Returns the un-prefixed name, or `null` if no prefix matched.
|
|
2051
|
+
*/
|
|
2052
|
+
function stripPublicPrefix(name) {
|
|
2053
|
+
for (const prefix of PUBLIC_ENV_PREFIXES) if (prefix && name.startsWith(prefix)) return name.slice(prefix.length);
|
|
2054
|
+
return null;
|
|
2055
|
+
}
|
|
2056
|
+
//#endregion
|
|
2057
|
+
//#region src/workspace/index.ts
|
|
2058
|
+
/**
|
|
2059
|
+
* Validate that all dependencies reference existing apps.
|
|
2060
|
+
* Returns the config if valid, throws otherwise.
|
|
2061
|
+
*/
|
|
2062
|
+
function validateDependencies(apps) {
|
|
2063
|
+
const appNames = new Set(Object.keys(apps));
|
|
2064
|
+
for (const [appName, app] of Object.entries(apps)) for (const dep of app.dependencies ?? []) {
|
|
2065
|
+
if (!appNames.has(dep)) throw new Error(`Invalid dependency: App "${appName}" depends on "${dep}" which does not exist. Valid apps are: ${[...appNames].join(", ")}`);
|
|
2066
|
+
if (dep === appName) throw new Error(`Invalid dependency: App "${appName}" cannot depend on itself.`);
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
/**
|
|
2070
|
+
* Define workspace configuration with full TypeScript support and type inference.
|
|
2071
|
+
*
|
|
2072
|
+
* Uses `const` type parameter to infer literal app names, providing:
|
|
2073
|
+
* - Autocomplete for app names in dependencies
|
|
2074
|
+
* - Type errors for invalid dependency references
|
|
2075
|
+
* - Full type inference for the returned config
|
|
2076
|
+
*
|
|
2077
|
+
* @example
|
|
2078
|
+
* ```ts
|
|
2079
|
+
* // gkm.config.ts
|
|
2080
|
+
* import { defineWorkspace } from '@geekmidas/cli';
|
|
2081
|
+
*
|
|
2082
|
+
* export default defineWorkspace({
|
|
2083
|
+
* name: 'my-saas',
|
|
2084
|
+
* apps: {
|
|
2085
|
+
* api: {
|
|
2086
|
+
* type: 'backend',
|
|
2087
|
+
* path: 'apps/api',
|
|
2088
|
+
* port: 3000,
|
|
2089
|
+
* routes: './src/endpoints/**\/*.ts',
|
|
2090
|
+
* envParser: './src/config/env',
|
|
2091
|
+
* logger: './src/logger',
|
|
2092
|
+
* },
|
|
2093
|
+
* web: {
|
|
2094
|
+
* type: 'web',
|
|
2095
|
+
* framework: 'nextjs',
|
|
2096
|
+
* path: 'apps/web',
|
|
2097
|
+
* port: 3001,
|
|
2098
|
+
* dependencies: ['api'], // <- autocomplete shows 'api' | 'web'
|
|
2099
|
+
* },
|
|
2100
|
+
* },
|
|
2101
|
+
* });
|
|
2102
|
+
*
|
|
2103
|
+
* // config.apps.api <- full type inference
|
|
2104
|
+
* // config.apps.foo <- TypeScript error
|
|
2105
|
+
* ```
|
|
2106
|
+
*/
|
|
2107
|
+
function defineWorkspace(config) {
|
|
2108
|
+
assertNoMovedDeployKeys(config.deploy);
|
|
2109
|
+
assertKnownSecretsStore(config.secrets?.store);
|
|
2110
|
+
if (config.apps) validateDependencies(config.apps);
|
|
2111
|
+
const result = safeValidateWorkspaceConfig(config);
|
|
2112
|
+
if (!result.success && result.error) throw new Error(formatValidationErrors(result.error));
|
|
2113
|
+
return config;
|
|
2114
|
+
}
|
|
2115
|
+
/**
|
|
2116
|
+
* Get the package name from package.json in the given directory.
|
|
2117
|
+
*/
|
|
2118
|
+
function getPackageName(cwd) {
|
|
2119
|
+
try {
|
|
2120
|
+
return require(`${cwd}/package.json`).name?.replace(/^@[^/]+\//, "");
|
|
2121
|
+
} catch {
|
|
2122
|
+
return;
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
/**
|
|
2126
|
+
* Normalize a workspace configuration with resolved defaults.
|
|
2127
|
+
*/
|
|
2128
|
+
function normalizeWorkspace(config, cwd) {
|
|
2129
|
+
const name = config.name ?? getPackageName(cwd) ?? (0, node_path.basename)(cwd);
|
|
2130
|
+
const defaultTarget = config.deploy?.default ?? "dokploy";
|
|
2131
|
+
const normalizedApps = {};
|
|
2132
|
+
for (const [appName, app] of Object.entries(config.apps ?? {})) normalizedApps[appName] = normalizeAppConfig(app, defaultTarget);
|
|
2133
|
+
return {
|
|
2134
|
+
name,
|
|
2135
|
+
root: cwd,
|
|
2136
|
+
...config.constructs ? { constructs: config.constructs } : {},
|
|
2137
|
+
apps: normalizedApps,
|
|
2138
|
+
...config.domains ? { domains: config.domains } : {},
|
|
2139
|
+
...config.dns ? { dns: config.dns } : {},
|
|
2140
|
+
deploy: config.deploy ?? { default: "dokploy" },
|
|
2141
|
+
shared: config.shared ?? { packages: ["packages/*"] },
|
|
2142
|
+
stages: validateStages(config.stages),
|
|
2143
|
+
secrets: config.secrets ?? {},
|
|
2144
|
+
state: config.state,
|
|
2145
|
+
test: config.test ?? {},
|
|
2146
|
+
...config.dev ? { dev: config.dev } : {}
|
|
2147
|
+
};
|
|
2148
|
+
}
|
|
2149
|
+
/**
|
|
2150
|
+
* Normalize an app configuration with resolved defaults.
|
|
2151
|
+
* @param app - App configuration
|
|
2152
|
+
* @param defaultTarget - Default deploy target from workspace config
|
|
2153
|
+
*/
|
|
2154
|
+
function normalizeAppConfig(app, defaultTarget) {
|
|
2155
|
+
return {
|
|
2156
|
+
...app,
|
|
2157
|
+
type: app.type ?? "backend",
|
|
2158
|
+
port: app.port,
|
|
2159
|
+
path: app.path,
|
|
2160
|
+
dependencies: app.dependencies ?? [],
|
|
2161
|
+
resolvedDeployTarget: app.deploy ?? defaultTarget
|
|
2162
|
+
};
|
|
2163
|
+
}
|
|
2164
|
+
/**
|
|
2165
|
+
* Wrap a single-app GkmConfig as a workspace.
|
|
2166
|
+
* This allows existing single-app configs to work seamlessly.
|
|
2167
|
+
*/
|
|
2168
|
+
function wrapSingleAppAsWorkspace(config, cwd) {
|
|
2169
|
+
return {
|
|
2170
|
+
name: config.name ?? getPackageName(cwd) ?? (0, node_path.basename)(cwd),
|
|
2171
|
+
root: cwd,
|
|
2172
|
+
apps: { api: {
|
|
2173
|
+
type: "backend",
|
|
2174
|
+
path: ".",
|
|
2175
|
+
port: 3e3,
|
|
2176
|
+
dependencies: [],
|
|
2177
|
+
resolvedDeployTarget: config.deploy?.default ?? "dokploy",
|
|
2178
|
+
constructs: config.constructs,
|
|
2179
|
+
hooks: config.hooks,
|
|
2180
|
+
telescope: config.telescope,
|
|
2181
|
+
openapi: config.openapi,
|
|
2182
|
+
runtime: config.runtime,
|
|
2183
|
+
env: config.env
|
|
2184
|
+
} },
|
|
2185
|
+
...config.domains ? { domains: config.domains } : {},
|
|
2186
|
+
...config.dns ? { dns: config.dns } : {},
|
|
2187
|
+
deploy: {
|
|
2188
|
+
default: "dokploy",
|
|
2189
|
+
...config.deploy
|
|
2190
|
+
},
|
|
2191
|
+
shared: { packages: [] },
|
|
2192
|
+
stages: validateStages(config.stages),
|
|
2193
|
+
secrets: config.secrets ?? {},
|
|
2194
|
+
test: config.test ?? {},
|
|
2195
|
+
...config.dev ? { dev: config.dev } : {}
|
|
2196
|
+
};
|
|
2197
|
+
}
|
|
2198
|
+
/**
|
|
2199
|
+
* Process a loaded configuration (either single-app or workspace).
|
|
2200
|
+
* Returns a normalized workspace in both cases.
|
|
2201
|
+
*/
|
|
2202
|
+
/**
|
|
2203
|
+
* `deploy.dokploy.registry`, which became `deploy.registry`: every target
|
|
2204
|
+
* pushes and pulls through the one registry, so it is not Dokploy's to hold.
|
|
2205
|
+
*
|
|
2206
|
+
* Refused by name rather than dropped: the schema strips keys it does not
|
|
2207
|
+
* know, and a registry silently gone would deploy images under no registry.
|
|
2208
|
+
*/
|
|
2209
|
+
var DokployRegistryMoved = class extends require_errors.GkmError {
|
|
2210
|
+
registry;
|
|
2211
|
+
constructor(registry) {
|
|
2212
|
+
super(`deploy.dokploy.registry is now deploy.registry, read by every deploy target. In gkm.config.ts, move it up one level: deploy: { registry: ${JSON.stringify(registry)}, dokploy: { … } }.`);
|
|
2213
|
+
this.registry = registry;
|
|
2214
|
+
this.name = "DokployRegistryMoved";
|
|
2215
|
+
}
|
|
2216
|
+
};
|
|
2217
|
+
/**
|
|
2218
|
+
* `deploy.domains`, which became `domains` at the root of the config: a stage's
|
|
2219
|
+
* domain is a fact about the product every target and every command reads,
|
|
2220
|
+
* not a deploy setting.
|
|
2221
|
+
*/
|
|
2222
|
+
var DomainsMoved = class extends require_errors.GkmError {
|
|
2223
|
+
domains;
|
|
2224
|
+
constructor(domains) {
|
|
2225
|
+
super(`deploy.domains is now domains, at the root of gkm.config.ts. Move it out of deploy: defineWorkspace({ …, domains: ${JSON.stringify(domains)}, deploy: { … } }).`);
|
|
2226
|
+
this.domains = domains;
|
|
2227
|
+
this.name = "DomainsMoved";
|
|
2228
|
+
}
|
|
2229
|
+
};
|
|
2230
|
+
/**
|
|
2231
|
+
* `deploy.dns`, which became `dns` at the root of the config, keyed by root
|
|
2232
|
+
* domain.
|
|
2233
|
+
*/
|
|
2234
|
+
var DnsMoved = class extends require_errors.GkmError {
|
|
2235
|
+
dns;
|
|
2236
|
+
constructor(dns) {
|
|
2237
|
+
const legacy = dns && typeof dns === "object" && "domain" in dns && "provider" in dns;
|
|
2238
|
+
const { domain, ...rest } = legacy ? dns : {};
|
|
2239
|
+
super(`deploy.dns is now dns, at the root of gkm.config.ts, keyed by root domain. Move it out of deploy: defineWorkspace({ …, dns: ${JSON.stringify(legacy ? { [String(domain)]: rest } : dns)}, deploy: { … } }).`);
|
|
2240
|
+
this.dns = dns;
|
|
2241
|
+
this.name = "DnsMoved";
|
|
2242
|
+
}
|
|
2243
|
+
};
|
|
2244
|
+
/** Refuse a deploy key that has moved, naming where it lives now. */
|
|
2245
|
+
function assertNoMovedDeployKeys(deploy) {
|
|
2246
|
+
const dokploy = deploy?.dokploy;
|
|
2247
|
+
if (dokploy && typeof dokploy === "object" && "registry" in dokploy) throw new DokployRegistryMoved(dokploy.registry);
|
|
2248
|
+
if (deploy && typeof deploy === "object") {
|
|
2249
|
+
if ("domains" in deploy) throw new DomainsMoved(deploy.domains);
|
|
2250
|
+
if ("dns" in deploy) throw new DnsMoved(deploy.dns);
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
function processConfig(config, cwd) {
|
|
2254
|
+
assertNoMovedDeployKeys(config.deploy);
|
|
2255
|
+
if (isWorkspaceConfig(config)) {
|
|
2256
|
+
assertKnownSecretsStore(config.secrets?.store);
|
|
2257
|
+
if (config.stages === void 0) throw new InvalidStages(stageProblems(void 0));
|
|
2258
|
+
const result = safeValidateWorkspaceConfig(config);
|
|
2259
|
+
if (!result.success && result.error) throw new InvalidWorkspaceConfig(formatValidationErrors(result.error));
|
|
2260
|
+
return {
|
|
2261
|
+
type: "workspace",
|
|
2262
|
+
raw: config,
|
|
2263
|
+
workspace: normalizeWorkspace(config, cwd)
|
|
2264
|
+
};
|
|
2265
|
+
}
|
|
2266
|
+
return {
|
|
2267
|
+
type: "single",
|
|
2268
|
+
raw: config,
|
|
2269
|
+
workspace: wrapSingleAppAsWorkspace(config, cwd)
|
|
2270
|
+
};
|
|
2271
|
+
}
|
|
2272
|
+
/** A workspace config the schema refuses; the message lists every issue. */
|
|
2273
|
+
var InvalidWorkspaceConfig = class extends require_errors.GkmError {
|
|
2274
|
+
constructor(message) {
|
|
2275
|
+
super(message);
|
|
2276
|
+
this.name = "InvalidWorkspaceConfig";
|
|
2277
|
+
}
|
|
2278
|
+
};
|
|
2279
|
+
/**
|
|
2280
|
+
* The workspace's own constructs globs, absolute.
|
|
2281
|
+
*
|
|
2282
|
+
* Infrastructure two apps share is a fact about the product, and declaring it
|
|
2283
|
+
* inside one of them makes moving that app a change to the other's database.
|
|
2284
|
+
*/
|
|
2285
|
+
function workspaceConstructGlobs(workspace) {
|
|
2286
|
+
return constructPatterns(workspace.constructs).map((pattern) => (0, node_path.isAbsolute)(pattern) ? pattern : (0, node_path.join)(workspace.root, pattern));
|
|
2287
|
+
}
|
|
2288
|
+
/**
|
|
2289
|
+
* Every constructs glob an app can see, absolute.
|
|
2290
|
+
*
|
|
2291
|
+
* The workspace's own first, then the app's. Absolute because the app builds
|
|
2292
|
+
* from its own directory while the workspace's constructs live above it —
|
|
2293
|
+
* a relative glob resolved from the app would look for the product's
|
|
2294
|
+
* infrastructure inside one of its consumers and find nothing.
|
|
2295
|
+
*
|
|
2296
|
+
* Additive, not overriding: an app that declares its own constructs still sees
|
|
2297
|
+
* the ones the workspace declared for everybody.
|
|
2298
|
+
*/
|
|
2299
|
+
function appConstructGlobs(workspace, appName) {
|
|
2300
|
+
const globs = workspaceConstructGlobs(workspace);
|
|
2301
|
+
const app = workspace.apps[appName];
|
|
2302
|
+
if (app) {
|
|
2303
|
+
const root = (0, node_path.isAbsolute)(app.path) ? app.path : (0, node_path.join)(workspace.root, app.path);
|
|
2304
|
+
for (const pattern of constructPatterns(app.constructs)) globs.push((0, node_path.isAbsolute)(pattern) ? pattern : (0, node_path.join)(root, pattern));
|
|
2305
|
+
}
|
|
2306
|
+
return globs;
|
|
2307
|
+
}
|
|
2308
|
+
/**
|
|
2309
|
+
* Every constructs glob in the workspace: its own, plus each app's.
|
|
2310
|
+
*
|
|
2311
|
+
* One list, so what a deploy provisions and what an app's entry point
|
|
2312
|
+
* registers drivers for are read off the same globs.
|
|
2313
|
+
*/
|
|
2314
|
+
function allConstructGlobs(workspace) {
|
|
2315
|
+
const globs = [...workspaceConstructGlobs(workspace), ...Object.keys(workspace.apps).flatMap((appName) => appConstructGlobs(workspace, appName))];
|
|
2316
|
+
return [...new Set(globs)];
|
|
2317
|
+
}
|
|
2318
|
+
/** A `Routes` in any of its accepted shapes, as a flat list of patterns. */
|
|
2319
|
+
function constructPatterns(routes) {
|
|
2320
|
+
if (!routes) return [];
|
|
2321
|
+
return typeof routes === "string" ? [routes] : routes;
|
|
2322
|
+
}
|
|
2323
|
+
/**
|
|
2324
|
+
* Get the GkmConfig for a specific app in a workspace.
|
|
2325
|
+
* Useful for running existing single-app commands on a specific app.
|
|
2326
|
+
*/
|
|
2327
|
+
function getAppGkmConfig(workspace, appName) {
|
|
2328
|
+
const app = workspace.apps[appName];
|
|
2329
|
+
if (!app || app.type !== "backend") return;
|
|
2330
|
+
return {
|
|
2331
|
+
...workspace.deploy ? { deploy: workspace.deploy } : {},
|
|
2332
|
+
stages: workspace.stages,
|
|
2333
|
+
constructs: appConstructGlobs(workspace, appName),
|
|
2334
|
+
hooks: app.hooks,
|
|
2335
|
+
telescope: app.telescope,
|
|
2336
|
+
openapi: app.openapi,
|
|
2337
|
+
runtime: app.runtime,
|
|
2338
|
+
env: app.env
|
|
2339
|
+
};
|
|
2340
|
+
}
|
|
2341
|
+
/**
|
|
2342
|
+
* Get topologically sorted app names based on dependencies.
|
|
2343
|
+
* Apps with no dependencies come first, then apps that depend on them.
|
|
2344
|
+
*/
|
|
2345
|
+
function getAppBuildOrder(workspace) {
|
|
2346
|
+
const appNames = Object.keys(workspace.apps);
|
|
2347
|
+
const visited = /* @__PURE__ */ new Set();
|
|
2348
|
+
const result = [];
|
|
2349
|
+
function visit(name) {
|
|
2350
|
+
if (visited.has(name)) return;
|
|
2351
|
+
visited.add(name);
|
|
2352
|
+
const app = workspace.apps[name];
|
|
2353
|
+
if (app) for (const dep of app.dependencies) visit(dep);
|
|
2354
|
+
result.push(name);
|
|
2355
|
+
}
|
|
2356
|
+
for (const name of appNames) visit(name);
|
|
2357
|
+
return result;
|
|
2358
|
+
}
|
|
2359
|
+
/**
|
|
2360
|
+
* Resolve the Dokploy endpoint for a specific stage.
|
|
2361
|
+
*
|
|
2362
|
+
* Uses per-stage endpoint from `endpoints` if available,
|
|
2363
|
+
* otherwise falls back to the global `endpoint`.
|
|
2364
|
+
*
|
|
2365
|
+
* @param dokployConfig - Dokploy workspace configuration
|
|
2366
|
+
* @param stage - Deployment stage (e.g., 'development', 'production')
|
|
2367
|
+
* @returns The endpoint URL for the stage, or undefined if not configured
|
|
2368
|
+
*
|
|
2369
|
+
* @example
|
|
2370
|
+
* ```ts
|
|
2371
|
+
* // With per-stage endpoints
|
|
2372
|
+
* const config = {
|
|
2373
|
+
* endpoints: {
|
|
2374
|
+
* development: 'https://dev.dokploy.example.com:3000',
|
|
2375
|
+
* production: 'https://prod.dokploy.example.com:3000',
|
|
2376
|
+
* },
|
|
2377
|
+
* };
|
|
2378
|
+
* getEndpointForStage(config, 'production'); // => 'https://prod.dokploy.example.com:3000'
|
|
2379
|
+
*
|
|
2380
|
+
* // With single endpoint
|
|
2381
|
+
* const config = { endpoint: 'https://dokploy.example.com:3000' };
|
|
2382
|
+
* getEndpointForStage(config, 'production'); // => 'https://dokploy.example.com:3000'
|
|
2383
|
+
* ```
|
|
2384
|
+
*/
|
|
2385
|
+
function getEndpointForStage(dokployConfig, stage) {
|
|
2386
|
+
if (!dokployConfig) return void 0;
|
|
2387
|
+
if (dokployConfig.endpoints?.[stage]) return dokployConfig.endpoints[stage];
|
|
2388
|
+
return dokployConfig.endpoint;
|
|
2389
|
+
}
|
|
2390
|
+
//#endregion
|
|
2391
|
+
Object.defineProperty(exports, "AccessKeyLimit", {
|
|
2392
|
+
enumerable: true,
|
|
2393
|
+
get: function() {
|
|
2394
|
+
return AccessKeyLimit;
|
|
2395
|
+
}
|
|
2396
|
+
});
|
|
2397
|
+
Object.defineProperty(exports, "BucketKeySetElsewhere", {
|
|
2398
|
+
enumerable: true,
|
|
2399
|
+
get: function() {
|
|
2400
|
+
return BucketKeySetElsewhere;
|
|
2401
|
+
}
|
|
2402
|
+
});
|
|
2403
|
+
Object.defineProperty(exports, "BucketNameUnavailable", {
|
|
2404
|
+
enumerable: true,
|
|
2405
|
+
get: function() {
|
|
2406
|
+
return BucketNameUnavailable;
|
|
2407
|
+
}
|
|
2408
|
+
});
|
|
2409
|
+
Object.defineProperty(exports, "BucketOwnedByAnotherProject", {
|
|
2410
|
+
enumerable: true,
|
|
2411
|
+
get: function() {
|
|
2412
|
+
return BucketOwnedByAnotherProject;
|
|
2413
|
+
}
|
|
2414
|
+
});
|
|
2415
|
+
Object.defineProperty(exports, "BucketProbeFailed", {
|
|
2416
|
+
enumerable: true,
|
|
2417
|
+
get: function() {
|
|
2418
|
+
return BucketProbeFailed;
|
|
2419
|
+
}
|
|
2420
|
+
});
|
|
2421
|
+
Object.defineProperty(exports, "BucketRegionMismatch", {
|
|
2422
|
+
enumerable: true,
|
|
2423
|
+
get: function() {
|
|
2424
|
+
return BucketRegionMismatch;
|
|
2425
|
+
}
|
|
2426
|
+
});
|
|
2427
|
+
Object.defineProperty(exports, "BucketRegionUnknown", {
|
|
2428
|
+
enumerable: true,
|
|
2429
|
+
get: function() {
|
|
2430
|
+
return BucketRegionUnknown;
|
|
2431
|
+
}
|
|
2432
|
+
});
|
|
2433
|
+
Object.defineProperty(exports, "ComposeTlsFileMissing", {
|
|
2434
|
+
enumerable: true,
|
|
2435
|
+
get: function() {
|
|
2436
|
+
return ComposeTlsFileMissing;
|
|
2437
|
+
}
|
|
2438
|
+
});
|
|
2439
|
+
Object.defineProperty(exports, "DnsMoved", {
|
|
2440
|
+
enumerable: true,
|
|
2441
|
+
get: function() {
|
|
2442
|
+
return DnsMoved;
|
|
2443
|
+
}
|
|
2444
|
+
});
|
|
2445
|
+
Object.defineProperty(exports, "DokployRegistryMoved", {
|
|
2446
|
+
enumerable: true,
|
|
2447
|
+
get: function() {
|
|
2448
|
+
return DokployRegistryMoved;
|
|
2449
|
+
}
|
|
2450
|
+
});
|
|
2451
|
+
Object.defineProperty(exports, "DomainsMoved", {
|
|
2452
|
+
enumerable: true,
|
|
2453
|
+
get: function() {
|
|
2454
|
+
return DomainsMoved;
|
|
2455
|
+
}
|
|
2456
|
+
});
|
|
2457
|
+
Object.defineProperty(exports, "IamUserNotOwned", {
|
|
2458
|
+
enumerable: true,
|
|
2459
|
+
get: function() {
|
|
2460
|
+
return IamUserNotOwned;
|
|
2461
|
+
}
|
|
2462
|
+
});
|
|
2463
|
+
Object.defineProperty(exports, "InvalidStages", {
|
|
2464
|
+
enumerable: true,
|
|
2465
|
+
get: function() {
|
|
2466
|
+
return InvalidStages;
|
|
2467
|
+
}
|
|
2468
|
+
});
|
|
2469
|
+
Object.defineProperty(exports, "InvalidWorkspaceConfig", {
|
|
2470
|
+
enumerable: true,
|
|
2471
|
+
get: function() {
|
|
2472
|
+
return InvalidWorkspaceConfig;
|
|
2473
|
+
}
|
|
2474
|
+
});
|
|
2475
|
+
Object.defineProperty(exports, "LOGS_PORT", {
|
|
2476
|
+
enumerable: true,
|
|
2477
|
+
get: function() {
|
|
2478
|
+
return LOGS_PORT;
|
|
2479
|
+
}
|
|
2480
|
+
});
|
|
2481
|
+
Object.defineProperty(exports, "PHASE_2_DEPLOY_TARGETS", {
|
|
2482
|
+
enumerable: true,
|
|
2483
|
+
get: function() {
|
|
2484
|
+
return PHASE_2_DEPLOY_TARGETS;
|
|
2485
|
+
}
|
|
2486
|
+
});
|
|
2487
|
+
Object.defineProperty(exports, "PUBLIC_ENV_PREFIXES", {
|
|
2488
|
+
enumerable: true,
|
|
2489
|
+
get: function() {
|
|
2490
|
+
return PUBLIC_ENV_PREFIXES;
|
|
2491
|
+
}
|
|
2492
|
+
});
|
|
2493
|
+
Object.defineProperty(exports, "ProvisionedBucketUnreachable", {
|
|
2494
|
+
enumerable: true,
|
|
2495
|
+
get: function() {
|
|
2496
|
+
return ProvisionedBucketUnreachable;
|
|
2497
|
+
}
|
|
2498
|
+
});
|
|
2499
|
+
Object.defineProperty(exports, "RecordedBucketUnreachable", {
|
|
2500
|
+
enumerable: true,
|
|
2501
|
+
get: function() {
|
|
2502
|
+
return RecordedBucketUnreachable;
|
|
2503
|
+
}
|
|
2504
|
+
});
|
|
2505
|
+
Object.defineProperty(exports, "RotationInProgress", {
|
|
2506
|
+
enumerable: true,
|
|
2507
|
+
get: function() {
|
|
2508
|
+
return RotationInProgress;
|
|
2509
|
+
}
|
|
2510
|
+
});
|
|
2511
|
+
Object.defineProperty(exports, "S3RegionRequired", {
|
|
2512
|
+
enumerable: true,
|
|
2513
|
+
get: function() {
|
|
2514
|
+
return S3RegionRequired;
|
|
2515
|
+
}
|
|
2516
|
+
});
|
|
2517
|
+
Object.defineProperty(exports, "S3_PROVISIONING", {
|
|
2518
|
+
enumerable: true,
|
|
2519
|
+
get: function() {
|
|
2520
|
+
return S3_PROVISIONING;
|
|
2521
|
+
}
|
|
2522
|
+
});
|
|
2523
|
+
Object.defineProperty(exports, "SECRETS_STORE_PROVIDERS", {
|
|
2524
|
+
enumerable: true,
|
|
2525
|
+
get: function() {
|
|
2526
|
+
return SECRETS_STORE_PROVIDERS;
|
|
2527
|
+
}
|
|
2528
|
+
});
|
|
2529
|
+
Object.defineProperty(exports, "SERVER_IPV4_KEY", {
|
|
2530
|
+
enumerable: true,
|
|
2531
|
+
get: function() {
|
|
2532
|
+
return SERVER_IPV4_KEY;
|
|
2533
|
+
}
|
|
2534
|
+
});
|
|
2535
|
+
Object.defineProperty(exports, "SERVER_IPV6_KEY", {
|
|
2536
|
+
enumerable: true,
|
|
2537
|
+
get: function() {
|
|
2538
|
+
return SERVER_IPV6_KEY;
|
|
2539
|
+
}
|
|
2540
|
+
});
|
|
2541
|
+
Object.defineProperty(exports, "SUPPORTED_DEPLOY_TARGETS", {
|
|
2542
|
+
enumerable: true,
|
|
2543
|
+
get: function() {
|
|
2544
|
+
return SUPPORTED_DEPLOY_TARGETS;
|
|
2545
|
+
}
|
|
2546
|
+
});
|
|
2547
|
+
Object.defineProperty(exports, "TEST_STAGE", {
|
|
2548
|
+
enumerable: true,
|
|
2549
|
+
get: function() {
|
|
2550
|
+
return TEST_STAGE;
|
|
2551
|
+
}
|
|
2552
|
+
});
|
|
2553
|
+
Object.defineProperty(exports, "UndeclaredStage", {
|
|
2554
|
+
enumerable: true,
|
|
2555
|
+
get: function() {
|
|
2556
|
+
return UndeclaredStage;
|
|
2557
|
+
}
|
|
2558
|
+
});
|
|
2559
|
+
Object.defineProperty(exports, "UnknownSecretsStoreProvider", {
|
|
2560
|
+
enumerable: true,
|
|
2561
|
+
get: function() {
|
|
2562
|
+
return UnknownSecretsStoreProvider;
|
|
2563
|
+
}
|
|
2564
|
+
});
|
|
2565
|
+
Object.defineProperty(exports, "UnknownStageProvider", {
|
|
2566
|
+
enumerable: true,
|
|
2567
|
+
get: function() {
|
|
2568
|
+
return UnknownStageProvider;
|
|
2569
|
+
}
|
|
2570
|
+
});
|
|
2571
|
+
Object.defineProperty(exports, "WorkspaceConfigSchema", {
|
|
2572
|
+
enumerable: true,
|
|
2573
|
+
get: function() {
|
|
2574
|
+
return WorkspaceConfigSchema;
|
|
2575
|
+
}
|
|
2576
|
+
});
|
|
2577
|
+
Object.defineProperty(exports, "allConstructGlobs", {
|
|
2578
|
+
enumerable: true,
|
|
2579
|
+
get: function() {
|
|
2580
|
+
return allConstructGlobs;
|
|
2581
|
+
}
|
|
2582
|
+
});
|
|
2583
|
+
Object.defineProperty(exports, "appConstructGlobs", {
|
|
2584
|
+
enumerable: true,
|
|
2585
|
+
get: function() {
|
|
2586
|
+
return appConstructGlobs;
|
|
2587
|
+
}
|
|
2588
|
+
});
|
|
2589
|
+
Object.defineProperty(exports, "assertDeployedStage", {
|
|
2590
|
+
enumerable: true,
|
|
2591
|
+
get: function() {
|
|
2592
|
+
return assertDeployedStage;
|
|
2593
|
+
}
|
|
2594
|
+
});
|
|
2595
|
+
Object.defineProperty(exports, "assertNoMovedDeployKeys", {
|
|
2596
|
+
enumerable: true,
|
|
2597
|
+
get: function() {
|
|
2598
|
+
return assertNoMovedDeployKeys;
|
|
2599
|
+
}
|
|
2600
|
+
});
|
|
2601
|
+
Object.defineProperty(exports, "certificateFor", {
|
|
2602
|
+
enumerable: true,
|
|
2603
|
+
get: function() {
|
|
2604
|
+
return certificateFor;
|
|
2605
|
+
}
|
|
2606
|
+
});
|
|
2607
|
+
Object.defineProperty(exports, "checkS3Config", {
|
|
2608
|
+
enumerable: true,
|
|
2609
|
+
get: function() {
|
|
2610
|
+
return checkS3Config;
|
|
2611
|
+
}
|
|
2612
|
+
});
|
|
2613
|
+
Object.defineProperty(exports, "checkStageBackups", {
|
|
2614
|
+
enumerable: true,
|
|
2615
|
+
get: function() {
|
|
2616
|
+
return checkStageBackups;
|
|
2617
|
+
}
|
|
2618
|
+
});
|
|
2619
|
+
Object.defineProperty(exports, "cnameTarget", {
|
|
2620
|
+
enumerable: true,
|
|
2621
|
+
get: function() {
|
|
2622
|
+
return cnameTarget;
|
|
2623
|
+
}
|
|
2624
|
+
});
|
|
2625
|
+
Object.defineProperty(exports, "constructPatterns", {
|
|
2626
|
+
enumerable: true,
|
|
2627
|
+
get: function() {
|
|
2628
|
+
return constructPatterns;
|
|
2629
|
+
}
|
|
2630
|
+
});
|
|
2631
|
+
Object.defineProperty(exports, "defineWorkspace", {
|
|
2632
|
+
enumerable: true,
|
|
2633
|
+
get: function() {
|
|
2634
|
+
return defineWorkspace;
|
|
2635
|
+
}
|
|
2636
|
+
});
|
|
2637
|
+
Object.defineProperty(exports, "deployedProblems", {
|
|
2638
|
+
enumerable: true,
|
|
2639
|
+
get: function() {
|
|
2640
|
+
return deployedProblems;
|
|
2641
|
+
}
|
|
2642
|
+
});
|
|
2643
|
+
Object.defineProperty(exports, "formatValidationErrors", {
|
|
2644
|
+
enumerable: true,
|
|
2645
|
+
get: function() {
|
|
2646
|
+
return formatValidationErrors;
|
|
2647
|
+
}
|
|
2648
|
+
});
|
|
2649
|
+
Object.defineProperty(exports, "getAppBuildOrder", {
|
|
2650
|
+
enumerable: true,
|
|
2651
|
+
get: function() {
|
|
2652
|
+
return getAppBuildOrder;
|
|
2653
|
+
}
|
|
2654
|
+
});
|
|
2655
|
+
Object.defineProperty(exports, "getAppGkmConfig", {
|
|
2656
|
+
enumerable: true,
|
|
2657
|
+
get: function() {
|
|
2658
|
+
return getAppGkmConfig;
|
|
2659
|
+
}
|
|
2660
|
+
});
|
|
2661
|
+
Object.defineProperty(exports, "getDeployTargetError", {
|
|
2662
|
+
enumerable: true,
|
|
2663
|
+
get: function() {
|
|
2664
|
+
return getDeployTargetError;
|
|
2665
|
+
}
|
|
2666
|
+
});
|
|
2667
|
+
Object.defineProperty(exports, "getEndpointForStage", {
|
|
2668
|
+
enumerable: true,
|
|
2669
|
+
get: function() {
|
|
2670
|
+
return getEndpointForStage;
|
|
2671
|
+
}
|
|
2672
|
+
});
|
|
2673
|
+
Object.defineProperty(exports, "getPublicEnvPrefix", {
|
|
2674
|
+
enumerable: true,
|
|
2675
|
+
get: function() {
|
|
2676
|
+
return getPublicEnvPrefix;
|
|
2677
|
+
}
|
|
2678
|
+
});
|
|
2679
|
+
Object.defineProperty(exports, "isDeployTargetSupported", {
|
|
2680
|
+
enumerable: true,
|
|
2681
|
+
get: function() {
|
|
2682
|
+
return isDeployTargetSupported;
|
|
2683
|
+
}
|
|
2684
|
+
});
|
|
2685
|
+
Object.defineProperty(exports, "isPhase2DeployTarget", {
|
|
2686
|
+
enumerable: true,
|
|
2687
|
+
get: function() {
|
|
2688
|
+
return isPhase2DeployTarget;
|
|
2689
|
+
}
|
|
2690
|
+
});
|
|
2691
|
+
Object.defineProperty(exports, "isReservedStageKey", {
|
|
2692
|
+
enumerable: true,
|
|
2693
|
+
get: function() {
|
|
2694
|
+
return isReservedStageKey;
|
|
2695
|
+
}
|
|
2696
|
+
});
|
|
2697
|
+
Object.defineProperty(exports, "isWorkspaceConfig", {
|
|
2698
|
+
enumerable: true,
|
|
2699
|
+
get: function() {
|
|
2700
|
+
return isWorkspaceConfig;
|
|
2701
|
+
}
|
|
2702
|
+
});
|
|
2703
|
+
Object.defineProperty(exports, "normalizeWorkspace", {
|
|
2704
|
+
enumerable: true,
|
|
2705
|
+
get: function() {
|
|
2706
|
+
return normalizeWorkspace;
|
|
2707
|
+
}
|
|
2708
|
+
});
|
|
2709
|
+
Object.defineProperty(exports, "otlpTelemetryEnv", {
|
|
2710
|
+
enumerable: true,
|
|
2711
|
+
get: function() {
|
|
2712
|
+
return otlpTelemetryEnv;
|
|
2713
|
+
}
|
|
2714
|
+
});
|
|
2715
|
+
Object.defineProperty(exports, "processConfig", {
|
|
2716
|
+
enumerable: true,
|
|
2717
|
+
get: function() {
|
|
2718
|
+
return processConfig;
|
|
2719
|
+
}
|
|
2720
|
+
});
|
|
2721
|
+
Object.defineProperty(exports, "provisioningOf", {
|
|
2722
|
+
enumerable: true,
|
|
2723
|
+
get: function() {
|
|
2724
|
+
return provisioningOf;
|
|
2725
|
+
}
|
|
2726
|
+
});
|
|
2727
|
+
Object.defineProperty(exports, "proxyFor", {
|
|
2728
|
+
enumerable: true,
|
|
2729
|
+
get: function() {
|
|
2730
|
+
return proxyFor;
|
|
2731
|
+
}
|
|
2732
|
+
});
|
|
2733
|
+
Object.defineProperty(exports, "resolveStageTelemetry", {
|
|
2734
|
+
enumerable: true,
|
|
2735
|
+
get: function() {
|
|
2736
|
+
return resolveStageTelemetry;
|
|
2737
|
+
}
|
|
2738
|
+
});
|
|
2739
|
+
Object.defineProperty(exports, "s3SecretsKey", {
|
|
2740
|
+
enumerable: true,
|
|
2741
|
+
get: function() {
|
|
2742
|
+
return s3SecretsKey;
|
|
2743
|
+
}
|
|
2744
|
+
});
|
|
2745
|
+
Object.defineProperty(exports, "s3SecretsLocation", {
|
|
2746
|
+
enumerable: true,
|
|
2747
|
+
get: function() {
|
|
2748
|
+
return s3SecretsLocation;
|
|
2749
|
+
}
|
|
2750
|
+
});
|
|
2751
|
+
Object.defineProperty(exports, "safeValidateWorkspaceConfig", {
|
|
2752
|
+
enumerable: true,
|
|
2753
|
+
get: function() {
|
|
2754
|
+
return safeValidateWorkspaceConfig;
|
|
2755
|
+
}
|
|
2756
|
+
});
|
|
2757
|
+
Object.defineProperty(exports, "stageProblems", {
|
|
2758
|
+
enumerable: true,
|
|
2759
|
+
get: function() {
|
|
2760
|
+
return stageProblems;
|
|
2761
|
+
}
|
|
2762
|
+
});
|
|
2763
|
+
Object.defineProperty(exports, "stageProvider", {
|
|
2764
|
+
enumerable: true,
|
|
2765
|
+
get: function() {
|
|
2766
|
+
return stageProvider;
|
|
2767
|
+
}
|
|
2768
|
+
});
|
|
2769
|
+
Object.defineProperty(exports, "stageSampleRate", {
|
|
2770
|
+
enumerable: true,
|
|
2771
|
+
get: function() {
|
|
2772
|
+
return stageSampleRate;
|
|
2773
|
+
}
|
|
2774
|
+
});
|
|
2775
|
+
Object.defineProperty(exports, "stripPublicPrefix", {
|
|
2776
|
+
enumerable: true,
|
|
2777
|
+
get: function() {
|
|
2778
|
+
return stripPublicPrefix;
|
|
2779
|
+
}
|
|
2780
|
+
});
|
|
2781
|
+
Object.defineProperty(exports, "telemetryEnv", {
|
|
2782
|
+
enumerable: true,
|
|
2783
|
+
get: function() {
|
|
2784
|
+
return telemetryEnv;
|
|
2785
|
+
}
|
|
2786
|
+
});
|
|
2787
|
+
Object.defineProperty(exports, "validateWorkspaceConfig", {
|
|
2788
|
+
enumerable: true,
|
|
2789
|
+
get: function() {
|
|
2790
|
+
return validateWorkspaceConfig;
|
|
2791
|
+
}
|
|
2792
|
+
});
|
|
2793
|
+
Object.defineProperty(exports, "workspaceConstructGlobs", {
|
|
2794
|
+
enumerable: true,
|
|
2795
|
+
get: function() {
|
|
2796
|
+
return workspaceConstructGlobs;
|
|
2797
|
+
}
|
|
2798
|
+
});
|
|
2799
|
+
Object.defineProperty(exports, "wrapSingleAppAsWorkspace", {
|
|
2800
|
+
enumerable: true,
|
|
2801
|
+
get: function() {
|
|
2802
|
+
return wrapSingleAppAsWorkspace;
|
|
2803
|
+
}
|
|
2804
|
+
});
|
|
2805
|
+
|
|
2806
|
+
//# sourceMappingURL=workspace-BwqQnool.cjs.map
|