@geekmidas/cli 10.0.0-alpha.12 → 10.0.0-alpha.121
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 +310 -377
- 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-BDa6pPgE.cjs +1090 -0
- package/dist/EndpointGenerator-BDa6pPgE.cjs.map +1 -0
- package/dist/EndpointGenerator-CP_z_E3e.mjs +1031 -0
- package/dist/EndpointGenerator-CP_z_E3e.mjs.map +1 -0
- package/dist/GoDaddyProvider-3C8U_tVu.cjs +388 -0
- package/dist/GoDaddyProvider-3C8U_tVu.cjs.map +1 -0
- package/dist/GoDaddyProvider-D0MQoOM4.mjs +388 -0
- package/dist/GoDaddyProvider-D0MQoOM4.mjs.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-CRi7TosX.mjs +124 -0
- package/dist/LegacyStateStore-CRi7TosX.mjs.map +1 -0
- package/dist/LegacyStateStore-DHsoQGOo.cjs +124 -0
- package/dist/LegacyStateStore-DHsoQGOo.cjs.map +1 -0
- package/dist/LocalStateStore-BQXTkeoS.cjs +178 -0
- package/dist/LocalStateStore-BQXTkeoS.cjs.map +1 -0
- package/dist/LocalStateStore-Dkth0jXX.mjs +167 -0
- package/dist/LocalStateStore-Dkth0jXX.mjs.map +1 -0
- package/dist/OpenApiTsGenerator-CsrukCE-.mjs +536 -0
- package/dist/OpenApiTsGenerator-CsrukCE-.mjs.map +1 -0
- package/dist/OpenApiTsGenerator-FNhY7Ssz.cjs +541 -0
- package/dist/OpenApiTsGenerator-FNhY7Ssz.cjs.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-DGKZgyKz.cjs +222 -0
- package/dist/S3StateStore-DGKZgyKz.cjs.map +1 -0
- package/dist/S3StateStore-pZZnLLBd.mjs +223 -0
- package/dist/S3StateStore-pZZnLLBd.mjs.map +1 -0
- package/dist/SSMStateStore-CZRMERDA.mjs +205 -0
- package/dist/SSMStateStore-CZRMERDA.mjs.map +1 -0
- package/dist/SSMStateStore-DzkyjQ5D.cjs +204 -0
- package/dist/SSMStateStore-DzkyjQ5D.cjs.map +1 -0
- package/dist/SubscriberGenerator-C8CPxrCK.cjs +592 -0
- package/dist/SubscriberGenerator-C8CPxrCK.cjs.map +1 -0
- package/dist/SubscriberGenerator-y3pncae_.mjs +533 -0
- package/dist/SubscriberGenerator-y3pncae_.mjs.map +1 -0
- package/dist/add-C0kXOsWS.mjs +717 -0
- package/dist/add-C0kXOsWS.mjs.map +1 -0
- package/dist/add-Df8cmz0H.cjs +719 -0
- package/dist/add-Df8cmz0H.cjs.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-Bux2iF-v.mjs → bundler-B-I4Du99.mjs} +35 -16
- package/dist/bundler-B-I4Du99.mjs.map +1 -0
- package/dist/{bundler-BMekgx7a.cjs → bundler-Dx_grUmc.cjs} +37 -19
- package/dist/bundler-Dx_grUmc.cjs.map +1 -0
- package/dist/cli-Cd9exatJ.cjs +71 -0
- package/dist/cli-Cd9exatJ.cjs.map +1 -0
- package/dist/cli-DL7GxGAe.mjs +71 -0
- package/dist/cli-DL7GxGAe.mjs.map +1 -0
- package/dist/compose-C43c03WZ.mjs +3284 -0
- package/dist/compose-C43c03WZ.mjs.map +1 -0
- package/dist/compose-Mdgea4Yt.cjs +3427 -0
- package/dist/compose-Mdgea4Yt.cjs.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-B67WJOOm.cjs +272 -0
- package/dist/credentialSchemas-B67WJOOm.cjs.map +1 -0
- package/dist/credentialSchemas-DDOLRCLF.mjs +237 -0
- package/dist/credentialSchemas-DDOLRCLF.mjs.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-BPOgTn1u.mjs +522 -0
- package/dist/databases-BPOgTn1u.mjs.map +1 -0
- package/dist/databases-fOaoBInm.cjs +625 -0
- package/dist/databases-fOaoBInm.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 +654 -0
- package/dist/deploy/public.d.cts.map +1 -0
- package/dist/deploy/public.d.mts +654 -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-BMM2hx-K.cjs +2401 -0
- package/dist/deploy-BMM2hx-K.cjs.map +1 -0
- package/dist/deploy-D5YhwDng.mjs +2328 -0
- package/dist/deploy-D5YhwDng.mjs.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-C5roJuc9.mjs +560 -0
- package/dist/discover-C5roJuc9.mjs.map +1 -0
- package/dist/discover-CSW_BMWa.d.cts +125 -0
- package/dist/discover-CSW_BMWa.d.cts.map +1 -0
- package/dist/discover-DozUKs8f.cjs +639 -0
- package/dist/discover-DozUKs8f.cjs.map +1 -0
- package/dist/discover-xtT7fgWg.d.mts +125 -0
- package/dist/discover-xtT7fgWg.d.mts.map +1 -0
- package/dist/dns-BIYf20p7.cjs +1057 -0
- package/dist/dns-BIYf20p7.cjs.map +1 -0
- package/dist/dns-agf2VCdV.mjs +926 -0
- package/dist/dns-agf2VCdV.mjs.map +1 -0
- package/dist/dokploy-2p4PK6ej.mjs +38 -0
- package/dist/dokploy-2p4PK6ej.mjs.map +1 -0
- package/dist/dokploy-BANMTroy.cjs +38 -0
- package/dist/dokploy-BANMTroy.cjs.map +1 -0
- package/dist/drivers-BjGDYfnC.mjs +195 -0
- package/dist/drivers-BjGDYfnC.mjs.map +1 -0
- package/dist/drivers-DbjiYpW6.cjs +224 -0
- package/dist/drivers-DbjiYpW6.cjs.map +1 -0
- package/dist/eas-CNi16SoO.cjs +337 -0
- package/dist/eas-CNi16SoO.cjs.map +1 -0
- package/dist/eas-iT2JDHCh.mjs +334 -0
- package/dist/eas-iT2JDHCh.mjs.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-DkXVbyZc.mjs +89 -0
- package/dist/encryption-DkXVbyZc.mjs.map +1 -0
- package/dist/encryption-d2lLtkYx.cjs +90 -0
- package/dist/encryption-d2lLtkYx.cjs.map +1 -0
- package/dist/engine-CatKW6E-.mjs +3089 -0
- package/dist/engine-CatKW6E-.mjs.map +1 -0
- package/dist/engine-DlEHexED.cjs +3202 -0
- package/dist/engine-DlEHexED.cjs.map +1 -0
- package/dist/errors-D8Gwp5kU.mjs +23 -0
- package/dist/errors-D8Gwp5kU.mjs.map +1 -0
- package/dist/errors-DMC4Umsb.cjs +34 -0
- package/dist/errors-DMC4Umsb.cjs.map +1 -0
- package/dist/github-BAeaC1Pl.cjs +833 -0
- package/dist/github-BAeaC1Pl.cjs.map +1 -0
- package/dist/github-DErsqKGN.mjs +832 -0
- package/dist/github-DErsqKGN.mjs.map +1 -0
- package/dist/googlePlay-BVcfFWRO.cjs +931 -0
- package/dist/googlePlay-BVcfFWRO.cjs.map +1 -0
- package/dist/googlePlay-BXJ5ZjMK.mjs +931 -0
- package/dist/googlePlay-BXJ5ZjMK.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-BzJ7QJ9c.d.mts → index-8OfpOtSI.d.cts} +39 -20
- package/dist/index-8OfpOtSI.d.cts.map +1 -0
- package/dist/{index-CfMjtdVC.d.cts → index-Dpji6Bxq.d.mts} +39 -20
- package/dist/index-Dpji6Bxq.d.mts.map +1 -0
- package/dist/index.cjs +14291 -11898
- 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 +13938 -11560
- 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-Bb1MmGHl.mjs +181 -0
- package/dist/migrate-Bb1MmGHl.mjs.map +1 -0
- package/dist/migrate-D79GI0XM.cjs +271 -0
- package/dist/migrate-D79GI0XM.cjs.map +1 -0
- package/dist/migrate-L4a273dY.cjs +181 -0
- package/dist/migrate-L4a273dY.cjs.map +1 -0
- package/dist/migrate-s4QizIoM.mjs +266 -0
- package/dist/migrate-s4QizIoM.mjs.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-CcTlmJT6.mjs +123 -0
- package/dist/publish-CcTlmJT6.mjs.map +1 -0
- package/dist/publish-ydscCYsW.cjs +124 -0
- package/dist/publish-ydscCYsW.cjs.map +1 -0
- package/dist/ready-BFFP838V.cjs +404 -0
- package/dist/ready-BFFP838V.cjs.map +1 -0
- package/dist/ready-BOfogqzr.mjs +375 -0
- package/dist/ready-BOfogqzr.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-BI0tWkwz.mjs → reconcile--I7V4Klj.mjs} +4 -7
- package/dist/reconcile--I7V4Klj.mjs.map +1 -0
- package/dist/{reconcile-Bhw5fmKu.cjs → reconcile-CuCQBa5-.cjs} +5 -8
- package/dist/reconcile-CuCQBa5-.cjs.map +1 -0
- package/dist/resolve-BLp2trep.mjs +263 -0
- package/dist/resolve-BLp2trep.mjs.map +1 -0
- package/dist/resolve-BgL-brF9.cjs +316 -0
- package/dist/resolve-BgL-brF9.cjs.map +1 -0
- package/dist/rollback-2cyLXEH1.cjs +165 -0
- package/dist/rollback-2cyLXEH1.cjs.map +1 -0
- package/dist/rollback-DwqZ8zzj.mjs +136 -0
- package/dist/rollback-DwqZ8zzj.mjs.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 +50 -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 +51 -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-DPV7FtAv.cjs → schemas-BCwKKA42.mjs} +958 -1816
- package/dist/schemas-BCwKKA42.mjs.map +1 -0
- package/dist/{workspace-CtnZnWsa.mjs → schemas-BQ_v0lEX.cjs} +1048 -1626
- 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-C2w4UqVP.cjs +9347 -0
- package/dist/seeds-C2w4UqVP.cjs.map +1 -0
- package/dist/seeds-Cwtv-U9-.mjs +8350 -0
- package/dist/seeds-Cwtv-U9-.mjs.map +1 -0
- package/dist/serverSetup-BdFOD93s.cjs +631 -0
- package/dist/serverSetup-BdFOD93s.cjs.map +1 -0
- package/dist/serverSetup-utKsoyGH.mjs +631 -0
- package/dist/serverSetup-utKsoyGH.mjs.map +1 -0
- package/dist/sniffer-8C4q6Szd.mjs +1825 -0
- package/dist/sniffer-8C4q6Szd.mjs.map +1 -0
- package/dist/sniffer-CF7wNxMo.cjs +1998 -0
- package/dist/sniffer-CF7wNxMo.cjs.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-BvM_u2z4.mjs +302 -0
- package/dist/stage-BvM_u2z4.mjs.map +1 -0
- package/dist/stage-DEQJGW3r.cjs +313 -0
- package/dist/stage-DEQJGW3r.cjs.map +1 -0
- package/dist/stages-Cb9rqRbN.mjs +190 -0
- package/dist/stages-Cb9rqRbN.mjs.map +1 -0
- package/dist/stages-D4SnG5yl.cjs +190 -0
- package/dist/stages-D4SnG5yl.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-CW_dWeTe.mjs +85 -0
- package/dist/terminal-CW_dWeTe.mjs.map +1 -0
- package/dist/terminal-DJlqcW_U.cjs +85 -0
- package/dist/terminal-DJlqcW_U.cjs.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-BMekgx7a.cjs.map +0 -1
- package/dist/bundler-Bux2iF-v.mjs.map +0 -1
- package/dist/chunk-Duj1WY3L.mjs +0 -7
- package/dist/config-DTsVjKZD.mjs +0 -402
- package/dist/config-DTsVjKZD.mjs.map +0 -1
- package/dist/config-RYR9j6EF.cjs +0 -462
- package/dist/config-RYR9j6EF.cjs.map +0 -1
- package/dist/config.d.cts.map +0 -1
- package/dist/config.d.mts.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-DQRvPz2S.cjs +0 -2769
- package/dist/credentials-DQRvPz2S.cjs.map +0 -1
- package/dist/credentials-jMJC63qs.mjs +0 -2625
- package/dist/credentials-jMJC63qs.mjs.map +0 -1
- package/dist/discover-BYHQwQqu.mjs +0 -242
- package/dist/discover-BYHQwQqu.mjs.map +0 -1
- package/dist/discover-DmW9Bscc.cjs +0 -290
- package/dist/discover-DmW9Bscc.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-B6KBHf-g.mjs +0 -87
- package/dist/fullstack-secrets-B6KBHf-g.mjs.map +0 -1
- package/dist/fullstack-secrets-DL8v0y4D.cjs +0 -99
- package/dist/fullstack-secrets-DL8v0y4D.cjs.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-BThvbf2W.cjs +0 -197
- package/dist/github-BThvbf2W.cjs.map +0 -1
- package/dist/github-DpMZurEK.mjs +0 -196
- package/dist/github-DpMZurEK.mjs.map +0 -1
- package/dist/index-BzJ7QJ9c.d.mts.map +0 -1
- package/dist/index-CfMjtdVC.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-Bs3-5q_r.mjs +0 -2021
- package/dist/openapi-Bs3-5q_r.mjs.map +0 -1
- package/dist/openapi-DWLTsYLL.cjs +0 -2075
- package/dist/openapi-DWLTsYLL.cjs.map +0 -1
- package/dist/openapi-react-query-Byltvm_7.mjs +0 -167
- package/dist/openapi-react-query-Byltvm_7.mjs.map +0 -1
- package/dist/openapi-react-query-CmJ1xDbn.cjs +0 -173
- package/dist/openapi-react-query-CmJ1xDbn.cjs.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/reconcile-BI0tWkwz.mjs.map +0 -1
- package/dist/reconcile-Bhw5fmKu.cjs.map +0 -1
- package/dist/setup-BmLoNrDX.mjs +0 -12
- package/dist/setup-BrR1_xGy.cjs +0 -12
- package/dist/setup-BsNR2LBG.cjs +0 -467
- package/dist/setup-BsNR2LBG.cjs.map +0 -1
- package/dist/setup-D5-JMknk.mjs +0 -437
- package/dist/setup-D5-JMknk.mjs.map +0 -1
- 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-DVIx8I1N.d.mts +0 -1950
- package/dist/types-DVIx8I1N.d.mts.map +0 -1
- package/dist/types-PMP_qXRY.d.cts +0 -1950
- package/dist/types-PMP_qXRY.d.cts.map +0 -1
- package/dist/workspace-Cji6lE8E.cjs +0 -10
- package/dist/workspace-CkJAtRIX.mjs +0 -9
- package/dist/workspace-CtnZnWsa.mjs.map +0 -1
- package/dist/workspace-DPV7FtAv.cjs.map +0 -1
|
@@ -0,0 +1,931 @@
|
|
|
1
|
+
const require_errors = require("./errors-DMC4Umsb.cjs");
|
|
2
|
+
const require_run = require("./run-fVZ-rLnc.cjs");
|
|
3
|
+
const require_config = require("./config.cjs");
|
|
4
|
+
const require_index = require("./index.cjs");
|
|
5
|
+
const require_stage = require("./stage-DEQJGW3r.cjs");
|
|
6
|
+
const require_eas = require("./eas-CNi16SoO.cjs");
|
|
7
|
+
let node_path = require("node:path");
|
|
8
|
+
let node_fs = require("node:fs");
|
|
9
|
+
let node_fs_promises = require("node:fs/promises");
|
|
10
|
+
let node_child_process = require("node:child_process");
|
|
11
|
+
let node_crypto = require("node:crypto");
|
|
12
|
+
//#region src/mobile/googlePlay/easApi.ts
|
|
13
|
+
/**
|
|
14
|
+
* EAS, as `gkm deploy:google-play` reaches it: the GraphQL API eas-cli itself
|
|
15
|
+
* calls, with an `EXPO_TOKEN` — to upload a Google service account key and
|
|
16
|
+
* assign it to the app's Android credentials for EAS Submit, which is what
|
|
17
|
+
* `eas credentials` does interactively.
|
|
18
|
+
*
|
|
19
|
+
* The documents are eas-cli's own (24.x), each selecting only what gkm reads:
|
|
20
|
+
* eas-cli's credentials fragment also brings back the app's keystore and its
|
|
21
|
+
* passwords, which gkm has no reason to hold.
|
|
22
|
+
*/
|
|
23
|
+
const EAS_GRAPHQL = "https://api.expo.dev/graphql";
|
|
24
|
+
/** No `EXPO_TOKEN` to call EAS with. */
|
|
25
|
+
var ExpoTokenMissing = class extends require_errors.GkmError {
|
|
26
|
+
constructor() {
|
|
27
|
+
super("gkm uploads the key to EAS with an access token, and EXPO_TOKEN is not set. Create one at https://expo.dev/settings/access-tokens and export it as EXPO_TOKEN.");
|
|
28
|
+
this.name = "ExpoTokenMissing";
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
/** EAS refused a call. */
|
|
32
|
+
var EasApiError = class extends require_errors.GkmError {
|
|
33
|
+
operation;
|
|
34
|
+
detail;
|
|
35
|
+
constructor(operation, detail) {
|
|
36
|
+
super(`EAS refused ${operation}: ${detail}`);
|
|
37
|
+
this.operation = operation;
|
|
38
|
+
this.detail = detail;
|
|
39
|
+
this.name = "EasApiError";
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
const KEY_FIELDS = "id clientEmail privateKeyIdentifier";
|
|
43
|
+
function easApi(token = process.env.EXPO_TOKEN, endpoint = EAS_GRAPHQL) {
|
|
44
|
+
const call = async (operation, query, variables) => {
|
|
45
|
+
if (!token) throw new ExpoTokenMissing();
|
|
46
|
+
const response = await fetch(endpoint, {
|
|
47
|
+
method: "POST",
|
|
48
|
+
headers: {
|
|
49
|
+
authorization: `Bearer ${token}`,
|
|
50
|
+
"content-type": "application/json"
|
|
51
|
+
},
|
|
52
|
+
body: JSON.stringify({
|
|
53
|
+
query,
|
|
54
|
+
variables,
|
|
55
|
+
operationName: operation
|
|
56
|
+
})
|
|
57
|
+
});
|
|
58
|
+
const body = await response.json().catch(() => ({}));
|
|
59
|
+
if (!response.ok || body.errors?.length || !body.data) throw new EasApiError(operation, body.errors?.map((error) => error.message).join("; ") || `HTTP ${response.status}`);
|
|
60
|
+
return body.data;
|
|
61
|
+
};
|
|
62
|
+
const credentialsFields = `id googleServiceAccountKeyForSubmissions { ${KEY_FIELDS} }`;
|
|
63
|
+
const asCredentials = (credentials) => ({
|
|
64
|
+
id: credentials.id,
|
|
65
|
+
...credentials.googleServiceAccountKeyForSubmissions ? { submissionsKey: credentials.googleServiceAccountKeyForSubmissions } : {}
|
|
66
|
+
});
|
|
67
|
+
return {
|
|
68
|
+
async project(projectId) {
|
|
69
|
+
const app = (await call("AppByIdQuery", `query AppByIdQuery($appId: String!) {
|
|
70
|
+
app { byId(appId: $appId) { id fullName ownerAccount { id name } } }
|
|
71
|
+
}`, { appId: projectId })).app.byId;
|
|
72
|
+
return {
|
|
73
|
+
accountId: app.ownerAccount.id,
|
|
74
|
+
accountName: app.ownerAccount.name,
|
|
75
|
+
fullName: app.fullName
|
|
76
|
+
};
|
|
77
|
+
},
|
|
78
|
+
async androidCredentials(project, packageName) {
|
|
79
|
+
const existing = (await call("AndroidAppCredentialsByApplicationIdentifierQuery", `query AndroidAppCredentialsByApplicationIdentifierQuery(
|
|
80
|
+
$projectFullName: String!
|
|
81
|
+
$applicationIdentifier: String
|
|
82
|
+
$legacyOnly: Boolean
|
|
83
|
+
) {
|
|
84
|
+
app {
|
|
85
|
+
byFullName(fullName: $projectFullName) {
|
|
86
|
+
id
|
|
87
|
+
androidAppCredentials(
|
|
88
|
+
filter: { applicationIdentifier: $applicationIdentifier, legacyOnly: $legacyOnly }
|
|
89
|
+
) { ${credentialsFields} }
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}`, {
|
|
93
|
+
projectFullName: project.fullName,
|
|
94
|
+
applicationIdentifier: packageName,
|
|
95
|
+
legacyOnly: false
|
|
96
|
+
})).app.byFullName.androidAppCredentials[0];
|
|
97
|
+
if (existing) return asCredentials(existing);
|
|
98
|
+
const created = await call("CreateAndroidAppCredentialsMutation", `mutation CreateAndroidAppCredentialsMutation(
|
|
99
|
+
$androidAppCredentialsInput: AndroidAppCredentialsInput!
|
|
100
|
+
$appId: ID!
|
|
101
|
+
$applicationIdentifier: String!
|
|
102
|
+
) {
|
|
103
|
+
androidAppCredentials {
|
|
104
|
+
createAndroidAppCredentials(
|
|
105
|
+
androidAppCredentialsInput: $androidAppCredentialsInput
|
|
106
|
+
appId: $appId
|
|
107
|
+
applicationIdentifier: $applicationIdentifier
|
|
108
|
+
) { ${credentialsFields} }
|
|
109
|
+
}
|
|
110
|
+
}`, {
|
|
111
|
+
androidAppCredentialsInput: {},
|
|
112
|
+
appId: project.projectId,
|
|
113
|
+
applicationIdentifier: packageName
|
|
114
|
+
});
|
|
115
|
+
return asCredentials(created.androidAppCredentials.createAndroidAppCredentials);
|
|
116
|
+
},
|
|
117
|
+
async createKey(accountId, json) {
|
|
118
|
+
return (await call("CreateGoogleServiceAccountKeyMutation", `mutation CreateGoogleServiceAccountKeyMutation(
|
|
119
|
+
$googleServiceAccountKeyInput: GoogleServiceAccountKeyInput!
|
|
120
|
+
$accountId: ID!
|
|
121
|
+
) {
|
|
122
|
+
googleServiceAccountKey {
|
|
123
|
+
createGoogleServiceAccountKey(
|
|
124
|
+
googleServiceAccountKeyInput: $googleServiceAccountKeyInput
|
|
125
|
+
accountId: $accountId
|
|
126
|
+
) { ${KEY_FIELDS} }
|
|
127
|
+
}
|
|
128
|
+
}`, {
|
|
129
|
+
googleServiceAccountKeyInput: { jsonKey: json },
|
|
130
|
+
accountId
|
|
131
|
+
})).googleServiceAccountKey.createGoogleServiceAccountKey;
|
|
132
|
+
},
|
|
133
|
+
async useForSubmissions(credentialsId, keyId) {
|
|
134
|
+
await call("SetGoogleServiceAccountKeyForSubmissionsMutation", `mutation SetGoogleServiceAccountKeyForSubmissionsMutation(
|
|
135
|
+
$androidAppCredentialsId: ID!
|
|
136
|
+
$googleServiceAccountKeyId: ID!
|
|
137
|
+
) {
|
|
138
|
+
androidAppCredentials {
|
|
139
|
+
setGoogleServiceAccountKeyForSubmissions(
|
|
140
|
+
id: $androidAppCredentialsId
|
|
141
|
+
googleServiceAccountKeyId: $googleServiceAccountKeyId
|
|
142
|
+
) { id }
|
|
143
|
+
}
|
|
144
|
+
}`, {
|
|
145
|
+
androidAppCredentialsId: credentialsId,
|
|
146
|
+
googleServiceAccountKeyId: keyId
|
|
147
|
+
});
|
|
148
|
+
},
|
|
149
|
+
async keys(accountName) {
|
|
150
|
+
const keys = [];
|
|
151
|
+
let after = null;
|
|
152
|
+
do {
|
|
153
|
+
const page = (await call("GoogleServiceAccountKeysPaginatedByAccountQuery", `query GoogleServiceAccountKeysPaginatedByAccountQuery(
|
|
154
|
+
$accountName: String!
|
|
155
|
+
$after: String
|
|
156
|
+
$first: Int
|
|
157
|
+
) {
|
|
158
|
+
account {
|
|
159
|
+
byName(accountName: $accountName) {
|
|
160
|
+
id
|
|
161
|
+
googleServiceAccountKeysPaginated(after: $after, first: $first) {
|
|
162
|
+
edges { node { ${KEY_FIELDS} } }
|
|
163
|
+
pageInfo { hasNextPage endCursor }
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}`, {
|
|
168
|
+
accountName,
|
|
169
|
+
after,
|
|
170
|
+
first: 50
|
|
171
|
+
})).account.byName.googleServiceAccountKeysPaginated;
|
|
172
|
+
keys.push(...page.edges.map((edge) => edge.node));
|
|
173
|
+
after = page.pageInfo.hasNextPage ? page.pageInfo.endCursor : null;
|
|
174
|
+
} while (after);
|
|
175
|
+
return keys;
|
|
176
|
+
},
|
|
177
|
+
async deleteKey(id) {
|
|
178
|
+
await call("DeleteGoogleServiceAccountKeyMutation", `mutation DeleteGoogleServiceAccountKeyMutation($googleServiceAccountKeyId: ID!) {
|
|
179
|
+
googleServiceAccountKey { deleteGoogleServiceAccountKey(id: $googleServiceAccountKeyId) { id } }
|
|
180
|
+
}`, { googleServiceAccountKeyId: id });
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
//#endregion
|
|
185
|
+
//#region src/mobile/googlePlay/google.ts
|
|
186
|
+
/**
|
|
187
|
+
* Google, as `gkm deploy:google-play` reaches it: a token for the person
|
|
188
|
+
* running it, and the Cloud APIs that make a project, a service account and
|
|
189
|
+
* its key.
|
|
190
|
+
*
|
|
191
|
+
* The token is gcloud's own application-default credential, with the
|
|
192
|
+
* `cloud-platform` and `androidpublisher` scopes — no OAuth client of gkm's —
|
|
193
|
+
* kept in gkm's own gcloud config (`<gkm home>/gcloud`), so the person's own
|
|
194
|
+
* gcloud setup is never read or changed. Every call carries the project as
|
|
195
|
+
* its quota project (`x-goog-user-project`), which the Play Developer API
|
|
196
|
+
* requires of a user credential.
|
|
197
|
+
*/
|
|
198
|
+
/** The scopes the sign-in asks for. */
|
|
199
|
+
const GOOGLE_SCOPES = ["https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/androidpublisher"];
|
|
200
|
+
/** `gcloud` is not installed. */
|
|
201
|
+
var GcloudMissing = class extends require_errors.GkmError {
|
|
202
|
+
constructor() {
|
|
203
|
+
super("gkm signs in to Google with gcloud, and there is no `gcloud` here. Install the Google Cloud CLI: https://cloud.google.com/sdk/docs/install.");
|
|
204
|
+
this.name = "GcloudMissing";
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
/** Not signed in, and no terminal to sign in from. */
|
|
208
|
+
var GoogleSignInRequired = class extends require_errors.GkmError {
|
|
209
|
+
config;
|
|
210
|
+
constructor(config) {
|
|
211
|
+
super(`Signing in to Google opens a browser, and this run has no terminal. Run it once where you can sign in, as a Play Console admin: CLOUDSDK_CONFIG=${config} gcloud auth application-default login --scopes=${GOOGLE_SCOPES.join(",")}.`);
|
|
212
|
+
this.config = config;
|
|
213
|
+
this.name = "GoogleSignInRequired";
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
/** A Google API refused a call. */
|
|
217
|
+
var GoogleApiError = class extends require_errors.GkmError {
|
|
218
|
+
method;
|
|
219
|
+
url;
|
|
220
|
+
status;
|
|
221
|
+
detail;
|
|
222
|
+
constructor(method, url, status, detail) {
|
|
223
|
+
super(`Google answered ${status} to ${method} ${url}: ${detail}`);
|
|
224
|
+
this.method = method;
|
|
225
|
+
this.url = url;
|
|
226
|
+
this.status = status;
|
|
227
|
+
this.detail = detail;
|
|
228
|
+
this.name = "GoogleApiError";
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
/** A long-running Google operation did not finish in time. */
|
|
232
|
+
var GoogleOperationTimedOut = class extends require_errors.GkmError {
|
|
233
|
+
operation;
|
|
234
|
+
constructor(operation) {
|
|
235
|
+
super(`Google's operation ${operation} had not finished after a minute. Run the command again: what it finished is kept.`);
|
|
236
|
+
this.operation = operation;
|
|
237
|
+
this.name = "GoogleOperationTimedOut";
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
/** Where gkm keeps its gcloud sign-in. */
|
|
241
|
+
function gcloudConfig(env = process.env) {
|
|
242
|
+
return (0, node_path.join)(require_run.gkmHome(env), "gcloud");
|
|
243
|
+
}
|
|
244
|
+
function gcloud(args, config, inherit) {
|
|
245
|
+
return new Promise((resolve, reject) => {
|
|
246
|
+
const child = (0, node_child_process.spawn)("gcloud", [...args], {
|
|
247
|
+
stdio: [
|
|
248
|
+
"inherit",
|
|
249
|
+
inherit ? "inherit" : "pipe",
|
|
250
|
+
inherit ? "inherit" : "pipe"
|
|
251
|
+
],
|
|
252
|
+
env: {
|
|
253
|
+
...process.env,
|
|
254
|
+
CLOUDSDK_CONFIG: config
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
let stdout = "";
|
|
258
|
+
child.stdout?.on("data", (chunk) => {
|
|
259
|
+
stdout += chunk.toString();
|
|
260
|
+
});
|
|
261
|
+
child.on("error", (error) => reject(error.code === "ENOENT" ? new GcloudMissing() : error));
|
|
262
|
+
child.on("close", (code) => resolve({
|
|
263
|
+
code,
|
|
264
|
+
stdout
|
|
265
|
+
}));
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
/** gcloud's application-default credential, in gkm's own gcloud config. */
|
|
269
|
+
function gcloudSignIn(config = gcloudConfig()) {
|
|
270
|
+
return { async token({ interactive }) {
|
|
271
|
+
const print = () => gcloud([
|
|
272
|
+
"auth",
|
|
273
|
+
"application-default",
|
|
274
|
+
"print-access-token"
|
|
275
|
+
], config, false);
|
|
276
|
+
let printed = await print();
|
|
277
|
+
if (printed.code !== 0) {
|
|
278
|
+
if (!interactive) throw new GoogleSignInRequired(config);
|
|
279
|
+
await gcloud([
|
|
280
|
+
"auth",
|
|
281
|
+
"application-default",
|
|
282
|
+
"login",
|
|
283
|
+
`--scopes=${GOOGLE_SCOPES.join(",")}`
|
|
284
|
+
], config, true);
|
|
285
|
+
printed = await print();
|
|
286
|
+
if (printed.code !== 0) throw new GoogleSignInRequired(config);
|
|
287
|
+
}
|
|
288
|
+
return printed.stdout.trim();
|
|
289
|
+
} };
|
|
290
|
+
}
|
|
291
|
+
function googleClient(token, quotaProject) {
|
|
292
|
+
return { async request(method, url, body, options) {
|
|
293
|
+
const headers = {
|
|
294
|
+
authorization: `Bearer ${token}`,
|
|
295
|
+
...quotaProject ? { "x-goog-user-project": quotaProject } : {}
|
|
296
|
+
};
|
|
297
|
+
let payload;
|
|
298
|
+
if (options?.raw) {
|
|
299
|
+
headers["content-type"] = options.raw.contentType;
|
|
300
|
+
payload = options.raw.data;
|
|
301
|
+
} else if (body !== void 0) {
|
|
302
|
+
headers["content-type"] = "application/json";
|
|
303
|
+
payload = JSON.stringify(body);
|
|
304
|
+
}
|
|
305
|
+
const response = await fetch(url, {
|
|
306
|
+
method,
|
|
307
|
+
headers,
|
|
308
|
+
...payload !== void 0 ? { body: payload } : {}
|
|
309
|
+
});
|
|
310
|
+
const text = await response.text();
|
|
311
|
+
if (!response.ok) {
|
|
312
|
+
let detail = text;
|
|
313
|
+
try {
|
|
314
|
+
detail = JSON.parse(text).error?.message ?? text;
|
|
315
|
+
} catch {}
|
|
316
|
+
throw new GoogleApiError(method, url, response.status, detail);
|
|
317
|
+
}
|
|
318
|
+
return text ? JSON.parse(text) : {};
|
|
319
|
+
} };
|
|
320
|
+
}
|
|
321
|
+
/** A long-running operation, polled until it is done. */
|
|
322
|
+
async function finished(client, base, operation, options = {}) {
|
|
323
|
+
let current = operation;
|
|
324
|
+
for (let attempt = 0; !current.done; attempt++) {
|
|
325
|
+
if (attempt >= (options.attempts ?? 30)) throw new GoogleOperationTimedOut(current.name ?? "(unnamed)");
|
|
326
|
+
await new Promise((resolve) => setTimeout(resolve, options.intervalMs ?? 2e3));
|
|
327
|
+
current = await client.request("GET", `${base}/${current.name}`);
|
|
328
|
+
}
|
|
329
|
+
if (current.error) throw new GoogleApiError("GET", `${base}/${current.name}`, 500, current.error.message ?? "the operation failed");
|
|
330
|
+
}
|
|
331
|
+
//#endregion
|
|
332
|
+
//#region src/mobile/googlePlay/gcp.ts
|
|
333
|
+
/**
|
|
334
|
+
* The Google Cloud side of publishing to Play: a project with the Android
|
|
335
|
+
* Publisher API on, and a service account in it whose JSON key EAS Submit
|
|
336
|
+
* uses. Each step finds what is there before it creates anything, so a
|
|
337
|
+
* second run reuses the project and the account and only makes a new key.
|
|
338
|
+
*/
|
|
339
|
+
const CLOUD_RESOURCE_MANAGER = "https://cloudresourcemanager.googleapis.com/v3";
|
|
340
|
+
const SERVICE_USAGE = "https://serviceusage.googleapis.com/v1";
|
|
341
|
+
const IAM = "https://iam.googleapis.com/v1";
|
|
342
|
+
/** The API EAS Submit and gkm call Play through. */
|
|
343
|
+
const ANDROID_PUBLISHER_API = "androidpublisher.googleapis.com";
|
|
344
|
+
/** A project id or service account id gkm would derive is not a valid one. */
|
|
345
|
+
var GoogleIdInvalid = class extends require_errors.GkmError {
|
|
346
|
+
kind;
|
|
347
|
+
id;
|
|
348
|
+
constructor(kind, id) {
|
|
349
|
+
super(`'${id}' is not a valid Google ${kind} id: 6 to 30 lowercase letters, digits and hyphens, starting with a letter and not ending in a hyphen. ` + (kind === "project" ? "Name one with --project." : ""));
|
|
350
|
+
this.kind = kind;
|
|
351
|
+
this.id = id;
|
|
352
|
+
this.name = "GoogleIdInvalid";
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
/** The project could not be created — most often, its id is someone else's. */
|
|
356
|
+
var GoogleProjectUnavailable = class extends require_errors.GkmError {
|
|
357
|
+
project;
|
|
358
|
+
detail;
|
|
359
|
+
constructor(project, detail) {
|
|
360
|
+
super(`Could not create the Google Cloud project '${project}' (${detail}). Project ids are global: name another, or one you already have, with --project.`);
|
|
361
|
+
this.project = project;
|
|
362
|
+
this.detail = detail;
|
|
363
|
+
this.name = "GoogleProjectUnavailable";
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
const ID = /^[a-z][a-z0-9-]{4,28}[a-z0-9]$/;
|
|
367
|
+
/**
|
|
368
|
+
* An id Google accepts, from a workspace's name: `shop` → `shop-play`.
|
|
369
|
+
* Lowercased, anything else made a hyphen, cut to 30.
|
|
370
|
+
*/
|
|
371
|
+
function playId(workspace) {
|
|
372
|
+
return `${workspace.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^[^a-z]+|-+$/g, "").slice(0, 25).replace(/-+$/, "")}-play`;
|
|
373
|
+
}
|
|
374
|
+
/** Throws unless `id` is a valid project or service account id. */
|
|
375
|
+
function assertGoogleId(kind, id) {
|
|
376
|
+
if (!ID.test(id)) throw new GoogleIdInvalid(kind, id);
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* The project, created when it is not there. Answers whether it was made.
|
|
380
|
+
*
|
|
381
|
+
* @throws {GoogleProjectUnavailable}
|
|
382
|
+
*/
|
|
383
|
+
async function ensureProject(client, project, displayName, options = {}) {
|
|
384
|
+
assertGoogleId("project", project);
|
|
385
|
+
try {
|
|
386
|
+
await client.request("GET", `${CLOUD_RESOURCE_MANAGER}/projects/${project}`);
|
|
387
|
+
return false;
|
|
388
|
+
} catch (error) {
|
|
389
|
+
if (!(error instanceof GoogleApiError) || ![403, 404].includes(error.status)) throw error;
|
|
390
|
+
}
|
|
391
|
+
try {
|
|
392
|
+
const operation = await client.request("POST", `${CLOUD_RESOURCE_MANAGER}/projects`, {
|
|
393
|
+
projectId: project,
|
|
394
|
+
displayName: displayName.slice(0, 30)
|
|
395
|
+
});
|
|
396
|
+
await finished(client, CLOUD_RESOURCE_MANAGER, operation, options);
|
|
397
|
+
} catch (error) {
|
|
398
|
+
if (error instanceof GoogleApiError) throw new GoogleProjectUnavailable(project, error.detail);
|
|
399
|
+
throw error;
|
|
400
|
+
}
|
|
401
|
+
return true;
|
|
402
|
+
}
|
|
403
|
+
/** The Android Publisher API, on in the project. */
|
|
404
|
+
async function enablePublisherApi(client, project, options = {}) {
|
|
405
|
+
const operation = await client.request("POST", `${SERVICE_USAGE}/projects/${project}/services/${ANDROID_PUBLISHER_API}:enable`, {});
|
|
406
|
+
await finished(client, SERVICE_USAGE, operation, options);
|
|
407
|
+
}
|
|
408
|
+
/** The service account's email, which is what Play and EAS know it by. */
|
|
409
|
+
function serviceAccountEmail(project, account) {
|
|
410
|
+
return `${account}@${project}.iam.gserviceaccount.com`;
|
|
411
|
+
}
|
|
412
|
+
/** The service account, created when it is not there. Answers its email. */
|
|
413
|
+
async function ensureServiceAccount(client, project, account, displayName) {
|
|
414
|
+
assertGoogleId("service account", account);
|
|
415
|
+
const email = serviceAccountEmail(project, account);
|
|
416
|
+
try {
|
|
417
|
+
await client.request("GET", `${IAM}/projects/${project}/serviceAccounts/${email}`);
|
|
418
|
+
return email;
|
|
419
|
+
} catch (error) {
|
|
420
|
+
if (!(error instanceof GoogleApiError) || error.status !== 404) throw error;
|
|
421
|
+
}
|
|
422
|
+
await client.request("POST", `${IAM}/projects/${project}/serviceAccounts`, {
|
|
423
|
+
accountId: account,
|
|
424
|
+
serviceAccount: { displayName: displayName.slice(0, 100) }
|
|
425
|
+
});
|
|
426
|
+
return email;
|
|
427
|
+
}
|
|
428
|
+
/** A new key for the account. Its JSON exists only in what this returns. */
|
|
429
|
+
async function createKey(client, project, email) {
|
|
430
|
+
const key = await client.request("POST", `${IAM}/projects/${project}/serviceAccounts/${email}/keys`, {});
|
|
431
|
+
return {
|
|
432
|
+
name: key.name,
|
|
433
|
+
json: Buffer.from(key.privateKeyData, "base64").toString("utf8")
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
/** The account's keys that someone made — not Google's own. */
|
|
437
|
+
async function listKeys(client, project, email) {
|
|
438
|
+
const { keys = [] } = await client.request("GET", `${IAM}/projects/${project}/serviceAccounts/${email}/keys?keyTypes=USER_MANAGED`);
|
|
439
|
+
return keys.map(({ name }) => ({ name }));
|
|
440
|
+
}
|
|
441
|
+
async function deleteKey(client, key) {
|
|
442
|
+
await client.request("DELETE", `${IAM}/${key.name}`);
|
|
443
|
+
}
|
|
444
|
+
/** A key's id: the last part of its name. */
|
|
445
|
+
function keyId(key) {
|
|
446
|
+
return key.name.split("/").pop();
|
|
447
|
+
}
|
|
448
|
+
//#endregion
|
|
449
|
+
//#region src/mobile/googlePlay/play.ts
|
|
450
|
+
/**
|
|
451
|
+
* Play Console, through the Play Developer API (androidpublisher v3): the
|
|
452
|
+
* service account's access to the app, the store listing as code, and a
|
|
453
|
+
* check that the new key can act on the app.
|
|
454
|
+
*/
|
|
455
|
+
const PLAY = "https://androidpublisher.googleapis.com/androidpublisher/v3";
|
|
456
|
+
const PLAY_UPLOAD = "https://androidpublisher.googleapis.com/upload/androidpublisher/v3";
|
|
457
|
+
/** What Play answers about a package no app in the account has. */
|
|
458
|
+
const PACKAGE_MISSING = /packages are not available/i;
|
|
459
|
+
/** The app is not in Play Console yet, and Play has no API that makes one. */
|
|
460
|
+
var PlayAppMissing = class extends require_errors.GkmError {
|
|
461
|
+
packageName;
|
|
462
|
+
developer;
|
|
463
|
+
constructor(packageName, developer) {
|
|
464
|
+
super(`Play Console has no app '${packageName}', and Play has no API that creates one. Create it at https://play.google.com/console/developers/${developer}/create-new-app, with ${packageName} as its package, then run this again.`);
|
|
465
|
+
this.packageName = packageName;
|
|
466
|
+
this.developer = developer;
|
|
467
|
+
this.name = "PlayAppMissing";
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
/** The new key could not open an edit on the app. */
|
|
471
|
+
var PlayKeyUnverified = class extends require_errors.GkmError {
|
|
472
|
+
email;
|
|
473
|
+
detail;
|
|
474
|
+
constructor(email, detail) {
|
|
475
|
+
super(`The new key for ${email} could not open an edit on the app (${detail}). Its access can take a few minutes to reach Play: run this again shortly.`);
|
|
476
|
+
this.email = email;
|
|
477
|
+
this.detail = detail;
|
|
478
|
+
this.name = "PlayKeyUnverified";
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
/**
|
|
482
|
+
* The app-level permissions the service account needs: releasing to the
|
|
483
|
+
* testing tracks, to production only where a stage submits there, and the
|
|
484
|
+
* store listing where the workspace keeps one.
|
|
485
|
+
*/
|
|
486
|
+
function playPermissions(options) {
|
|
487
|
+
return [
|
|
488
|
+
"CAN_MANAGE_TRACK_APKS",
|
|
489
|
+
...options.tracks.includes("production") ? ["CAN_MANAGE_PUBLIC_APKS"] : [],
|
|
490
|
+
...options.listing ? ["CAN_MANAGE_PUBLIC_LISTING"] : []
|
|
491
|
+
].sort();
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* The service account in Play Console with exactly `permissions` on the
|
|
495
|
+
* app: invited with the grant in one call — Play refuses a user with no
|
|
496
|
+
* permission, and a grant for a user not yet there — and, already there,
|
|
497
|
+
* its grant made what it should be. Answers what changed.
|
|
498
|
+
*
|
|
499
|
+
* @throws {PlayAppMissing}
|
|
500
|
+
*/
|
|
501
|
+
async function ensurePlayAccess(client, options) {
|
|
502
|
+
const { developer, email, packageName } = options;
|
|
503
|
+
const permissions = [...options.permissions].sort();
|
|
504
|
+
const users = `${PLAY}/developers/${developer}/users`;
|
|
505
|
+
const missing = (error) => error instanceof GoogleApiError && PACKAGE_MISSING.test(error.detail) ? new PlayAppMissing(packageName, developer) : error;
|
|
506
|
+
const { users: listed = [] } = await client.request("GET", `${users}?pageSize=-1`);
|
|
507
|
+
const user = listed.find((candidate) => candidate.email === email);
|
|
508
|
+
if (!user) {
|
|
509
|
+
await client.request("POST", users, {
|
|
510
|
+
email,
|
|
511
|
+
grants: [{
|
|
512
|
+
packageName,
|
|
513
|
+
appLevelPermissions: permissions
|
|
514
|
+
}]
|
|
515
|
+
}).catch((error) => {
|
|
516
|
+
throw missing(error);
|
|
517
|
+
});
|
|
518
|
+
return "invited";
|
|
519
|
+
}
|
|
520
|
+
const grant = user.grants?.find((g) => g.packageName === packageName);
|
|
521
|
+
const grantPath = `${users}/${encodeURIComponent(email)}/grants`;
|
|
522
|
+
if (!grant) {
|
|
523
|
+
await client.request("POST", grantPath, {
|
|
524
|
+
packageName,
|
|
525
|
+
appLevelPermissions: permissions
|
|
526
|
+
}).catch((error) => {
|
|
527
|
+
throw missing(error);
|
|
528
|
+
});
|
|
529
|
+
return "granted";
|
|
530
|
+
}
|
|
531
|
+
if ([...grant.appLevelPermissions ?? []].sort().join() === permissions.join()) return "unchanged";
|
|
532
|
+
await client.request("PATCH", `${grantPath}/${packageName}?updateMask=appLevelPermissions`, { appLevelPermissions: permissions });
|
|
533
|
+
return "updated";
|
|
534
|
+
}
|
|
535
|
+
/** An access token for the key itself: a signed JWT, exchanged. */
|
|
536
|
+
async function keyToken(key) {
|
|
537
|
+
const tokenUri = key.token_uri ?? "https://oauth2.googleapis.com/token";
|
|
538
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
539
|
+
const encode = (value) => Buffer.from(JSON.stringify(value)).toString("base64url");
|
|
540
|
+
const unsigned = `${encode({
|
|
541
|
+
alg: "RS256",
|
|
542
|
+
typ: "JWT"
|
|
543
|
+
})}.${encode({
|
|
544
|
+
iss: key.client_email,
|
|
545
|
+
scope: "https://www.googleapis.com/auth/androidpublisher",
|
|
546
|
+
aud: tokenUri,
|
|
547
|
+
iat: now,
|
|
548
|
+
exp: now + 600
|
|
549
|
+
})}`;
|
|
550
|
+
const signature = (0, node_crypto.createSign)("RSA-SHA256").update(unsigned).sign(key.private_key).toString("base64url");
|
|
551
|
+
const response = await fetch(tokenUri, {
|
|
552
|
+
method: "POST",
|
|
553
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
554
|
+
body: new URLSearchParams({
|
|
555
|
+
grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
|
556
|
+
assertion: `${unsigned}.${signature}`
|
|
557
|
+
})
|
|
558
|
+
});
|
|
559
|
+
const body = await response.json().catch(() => ({}));
|
|
560
|
+
if (!response.ok || !body.access_token) throw new PlayKeyUnverified(key.client_email, body.error_description ?? `token exchange answered ${response.status}`);
|
|
561
|
+
return body.access_token;
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Proof the key and its grant work: as the key, open an edit on the app and
|
|
565
|
+
* discard it — what EAS Submit's first call is.
|
|
566
|
+
*
|
|
567
|
+
* @throws {PlayKeyUnverified}
|
|
568
|
+
*/
|
|
569
|
+
async function verifyKey(key, packageName) {
|
|
570
|
+
const client = googleClient(await keyToken(key));
|
|
571
|
+
try {
|
|
572
|
+
const edit = await client.request("POST", `${PLAY}/applications/${packageName}/edits`, {});
|
|
573
|
+
await client.request("DELETE", `${PLAY}/applications/${packageName}/edits/${edit.id}`);
|
|
574
|
+
} catch (error) {
|
|
575
|
+
if (error instanceof GoogleApiError) throw new PlayKeyUnverified(key.client_email, error.detail);
|
|
576
|
+
throw error;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
/** The image kinds Play's listing takes, as fastlane supply names them. */
|
|
580
|
+
const IMAGE_TYPES = [
|
|
581
|
+
"icon",
|
|
582
|
+
"featureGraphic",
|
|
583
|
+
"promoGraphic",
|
|
584
|
+
"tvBanner",
|
|
585
|
+
"phoneScreenshots",
|
|
586
|
+
"sevenInchScreenshots",
|
|
587
|
+
"tenInchScreenshots",
|
|
588
|
+
"tvScreenshots",
|
|
589
|
+
"wearScreenshots"
|
|
590
|
+
];
|
|
591
|
+
/**
|
|
592
|
+
* `<app>/store/play`, read: undefined where there is none.
|
|
593
|
+
*
|
|
594
|
+
* ```
|
|
595
|
+
* store/play/
|
|
596
|
+
* data-safety.csv
|
|
597
|
+
* listings/en-US/title.txt, short_description.txt, full_description.txt, video.txt
|
|
598
|
+
* listings/en-US/images/icon.png, featureGraphic.png
|
|
599
|
+
* listings/en-US/images/phoneScreenshots/1.png, 2.png …
|
|
600
|
+
* ```
|
|
601
|
+
*/
|
|
602
|
+
function readPlayStore(appDir) {
|
|
603
|
+
const root = (0, node_path.join)(appDir, "store", "play");
|
|
604
|
+
if (!(0, node_fs.existsSync)(root)) return void 0;
|
|
605
|
+
const text = (path) => (0, node_fs.existsSync)(path) ? (0, node_fs.readFileSync)(path, "utf8").trim() : void 0;
|
|
606
|
+
const listings = [];
|
|
607
|
+
const listingsDir = (0, node_path.join)(root, "listings");
|
|
608
|
+
for (const language of (0, node_fs.existsSync)(listingsDir) ? (0, node_fs.readdirSync)(listingsDir).sort() : []) {
|
|
609
|
+
const dir = (0, node_path.join)(listingsDir, language);
|
|
610
|
+
if (!(0, node_fs.statSync)(dir).isDirectory()) continue;
|
|
611
|
+
const images = {};
|
|
612
|
+
const imagesDir = (0, node_path.join)(dir, "images");
|
|
613
|
+
for (const type of IMAGE_TYPES) {
|
|
614
|
+
const single = [
|
|
615
|
+
".png",
|
|
616
|
+
".jpg",
|
|
617
|
+
".jpeg"
|
|
618
|
+
].map((ext) => (0, node_path.join)(imagesDir, `${type}${ext}`)).find(node_fs.existsSync);
|
|
619
|
+
const many = (0, node_path.join)(imagesDir, type);
|
|
620
|
+
const files = single ? [single] : (0, node_fs.existsSync)(many) ? (0, node_fs.readdirSync)(many).filter((f) => /\.(png|jpe?g)$/i.test(f)).sort().map((f) => (0, node_path.join)(many, f)) : [];
|
|
621
|
+
if (files.length > 0) images[type] = files;
|
|
622
|
+
}
|
|
623
|
+
const listing = {
|
|
624
|
+
language,
|
|
625
|
+
images
|
|
626
|
+
};
|
|
627
|
+
const title = text((0, node_path.join)(dir, "title.txt"));
|
|
628
|
+
const shortDescription = text((0, node_path.join)(dir, "short_description.txt"));
|
|
629
|
+
const fullDescription = text((0, node_path.join)(dir, "full_description.txt"));
|
|
630
|
+
const video = text((0, node_path.join)(dir, "video.txt"));
|
|
631
|
+
if (title) listing.title = title;
|
|
632
|
+
if (shortDescription) listing.shortDescription = shortDescription;
|
|
633
|
+
if (fullDescription) listing.fullDescription = fullDescription;
|
|
634
|
+
if (video) listing.video = video;
|
|
635
|
+
listings.push(listing);
|
|
636
|
+
}
|
|
637
|
+
const dataSafety = text((0, node_path.join)(root, "data-safety.csv"));
|
|
638
|
+
return {
|
|
639
|
+
listings,
|
|
640
|
+
...dataSafety ? { dataSafety } : {}
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* Push the listing through one edit, committed: each locale's text, and each
|
|
645
|
+
* image kind replaced by the files kept. Then the data safety form, which
|
|
646
|
+
* Play takes outside an edit. Answers whether the listing was pushed: an
|
|
647
|
+
* app that has never had an upload may refuse an edit, and that is left for
|
|
648
|
+
* the next run, not failed.
|
|
649
|
+
*/
|
|
650
|
+
async function pushPlayStore(client, packageName, store) {
|
|
651
|
+
const app = `${PLAY}/applications/${packageName}`;
|
|
652
|
+
let listing = false;
|
|
653
|
+
let reason;
|
|
654
|
+
if (store.listings.length > 0) {
|
|
655
|
+
let edit;
|
|
656
|
+
try {
|
|
657
|
+
edit = await client.request("POST", `${app}/edits`, {});
|
|
658
|
+
} catch (error) {
|
|
659
|
+
if (!(error instanceof GoogleApiError)) throw error;
|
|
660
|
+
reason = error.detail;
|
|
661
|
+
}
|
|
662
|
+
if (edit) {
|
|
663
|
+
const base = `${app}/edits/${edit.id}/listings`;
|
|
664
|
+
for (const entry of store.listings) {
|
|
665
|
+
const { images, ...text } = entry;
|
|
666
|
+
if (text.title || text.shortDescription || text.fullDescription) await client.request("PUT", `${base}/${entry.language}`, text);
|
|
667
|
+
for (const [type, files] of Object.entries(images)) {
|
|
668
|
+
await client.request("DELETE", `${base}/${entry.language}/${type}`);
|
|
669
|
+
for (const file of files ?? []) await client.request("POST", `${PLAY_UPLOAD}/applications/${packageName}/edits/${edit.id}/listings/${entry.language}/${type}?uploadType=media`, void 0, { raw: {
|
|
670
|
+
contentType: (0, node_path.extname)(file).toLowerCase() === ".png" ? "image/png" : "image/jpeg",
|
|
671
|
+
data: (0, node_fs.readFileSync)(file)
|
|
672
|
+
} });
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
await client.request("POST", `${app}/edits/${edit.id}:commit`);
|
|
676
|
+
listing = true;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
let dataSafety = false;
|
|
680
|
+
if (store.dataSafety) {
|
|
681
|
+
await client.request("POST", `${app}/dataSafety`, { safetyLabels: store.dataSafety });
|
|
682
|
+
dataSafety = true;
|
|
683
|
+
}
|
|
684
|
+
return {
|
|
685
|
+
listing,
|
|
686
|
+
dataSafety,
|
|
687
|
+
...reason ? { reason } : {}
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
/** What Play Console only takes by hand, for the person to tick off. */
|
|
691
|
+
const PLAY_CONSOLE_CHECKLIST = [
|
|
692
|
+
"App content → Privacy policy: the URL of the published policy",
|
|
693
|
+
"App content → App access: how a reviewer signs in",
|
|
694
|
+
"App content → Ads: whether the app shows ads",
|
|
695
|
+
"App content → Content rating: the questionnaire",
|
|
696
|
+
"App content → Target audience and content",
|
|
697
|
+
"App content → Health apps / Health Connect, where the app reads health data",
|
|
698
|
+
"App content → Sensitive permissions, where the app asks for any",
|
|
699
|
+
"Release → the first upload of a new app, by hand"
|
|
700
|
+
];
|
|
701
|
+
//#endregion
|
|
702
|
+
//#region src/mobile/googlePlay/index.ts
|
|
703
|
+
/**
|
|
704
|
+
* `gkm deploy:google-play`: what EAS Submit needs to publish a `MobileApp`
|
|
705
|
+
* to Google Play, set up in one run — the counterpart of `gkm deploy:github`
|
|
706
|
+
* for the AWS side.
|
|
707
|
+
*
|
|
708
|
+
* 1. Each deployed stage's track and release status into `eas.json`
|
|
709
|
+
* (`submit.<stage>.android`), from `mobileStage`.
|
|
710
|
+
* 2. Google: the project (`--project`, else `<workspace>-play`, made when
|
|
711
|
+
* absent) with the Android Publisher API on, and the service account
|
|
712
|
+
* `<workspace>-play` in it, found or made.
|
|
713
|
+
* 3. Play Console: the account invited with a grant on the app alone, its
|
|
714
|
+
* permissions following the stages' tracks — production releases only
|
|
715
|
+
* when a stage submits there. An app not in Play Console yet is a step
|
|
716
|
+
* only a person can take; interactively, the run says so and waits.
|
|
717
|
+
* 4. A new JSON key, held in memory: uploaded to EAS, assigned to the app's
|
|
718
|
+
* Android credentials for EAS Submit, and proved by opening and
|
|
719
|
+
* discarding an edit with it. Only then are the account's older keys
|
|
720
|
+
* deleted, from EAS and from Google — a failed run leaves the old key
|
|
721
|
+
* working.
|
|
722
|
+
* 5. The store listing kept beside the app (`store/play/`), pushed.
|
|
723
|
+
* 6. What Play Console only takes by hand, as a checklist.
|
|
724
|
+
*
|
|
725
|
+
* Running it again rotates the key and pushes the listing.
|
|
726
|
+
*/
|
|
727
|
+
const logger = console;
|
|
728
|
+
/** No Play developer account to act in. */
|
|
729
|
+
var PlayDeveloperMissing = class extends require_errors.GkmError {
|
|
730
|
+
constructor() {
|
|
731
|
+
super("Name the Play developer account: --developer <id>. It is the number in Play Console's address: https://play.google.com/console/developers/<id>/….");
|
|
732
|
+
this.name = "PlayDeveloperMissing";
|
|
733
|
+
}
|
|
734
|
+
};
|
|
735
|
+
/** The app's Expo config has no Android package, or no EAS project. */
|
|
736
|
+
var ExpoConfigIncomplete = class extends require_errors.GkmError {
|
|
737
|
+
app;
|
|
738
|
+
missing;
|
|
739
|
+
constructor(app, missing) {
|
|
740
|
+
super(missing === "android.package" ? `'${app}' has no android.package in its Expo config: Play knows an app by its package. Set it in app.config.ts: android: { package: 'com.acme.shop' }.` : `'${app}' has no extra.eas.projectId in its Expo config: it is not linked to an EAS project. Run \`eas init\` in the app.`);
|
|
741
|
+
this.app = app;
|
|
742
|
+
this.missing = missing;
|
|
743
|
+
this.name = "ExpoConfigIncomplete";
|
|
744
|
+
}
|
|
745
|
+
};
|
|
746
|
+
/** `expo config` could not read the app's config. */
|
|
747
|
+
var ExpoConfigFailed = class extends require_errors.GkmError {
|
|
748
|
+
dir;
|
|
749
|
+
detail;
|
|
750
|
+
constructor(dir, detail) {
|
|
751
|
+
super(`Could not read the Expo config in ${dir} (${detail}). Check that \`npx expo config --json\` works there.`);
|
|
752
|
+
this.dir = dir;
|
|
753
|
+
this.detail = detail;
|
|
754
|
+
this.name = "ExpoConfigFailed";
|
|
755
|
+
}
|
|
756
|
+
};
|
|
757
|
+
/** The workspace deploys no stage. */
|
|
758
|
+
var NoDeployedStage = class extends require_errors.GkmError {
|
|
759
|
+
constructor() {
|
|
760
|
+
super("gkm.config.ts deploys no stage, so there is nothing to publish to: add one to stages.deployed.");
|
|
761
|
+
this.name = "NoDeployedStage";
|
|
762
|
+
}
|
|
763
|
+
};
|
|
764
|
+
/** `expo config --json`, run with the app's Expo — its own, or the workspace's. */
|
|
765
|
+
function expoConfig(dir, root, env) {
|
|
766
|
+
return new Promise((resolve, reject) => {
|
|
767
|
+
const child = (0, node_child_process.spawn)("expo", [
|
|
768
|
+
"config",
|
|
769
|
+
"--json",
|
|
770
|
+
"--type",
|
|
771
|
+
"public"
|
|
772
|
+
], {
|
|
773
|
+
cwd: dir,
|
|
774
|
+
stdio: [
|
|
775
|
+
"ignore",
|
|
776
|
+
"pipe",
|
|
777
|
+
"pipe"
|
|
778
|
+
],
|
|
779
|
+
env: {
|
|
780
|
+
...process.env,
|
|
781
|
+
...env,
|
|
782
|
+
PATH: [
|
|
783
|
+
(0, node_path.join)(dir, "node_modules", ".bin"),
|
|
784
|
+
(0, node_path.join)(root, "node_modules", ".bin"),
|
|
785
|
+
process.env.PATH
|
|
786
|
+
].join(node_path.delimiter)
|
|
787
|
+
}
|
|
788
|
+
});
|
|
789
|
+
let stdout = "";
|
|
790
|
+
let stderr = "";
|
|
791
|
+
child.stdout.on("data", (chunk) => {
|
|
792
|
+
stdout += chunk.toString();
|
|
793
|
+
});
|
|
794
|
+
child.stderr.on("data", (chunk) => {
|
|
795
|
+
stderr += chunk.toString();
|
|
796
|
+
});
|
|
797
|
+
child.on("error", (error) => reject(new ExpoConfigFailed(dir, error.message)));
|
|
798
|
+
child.on("close", (code) => {
|
|
799
|
+
if (code !== 0) {
|
|
800
|
+
reject(new ExpoConfigFailed(dir, stderr.trim() || `exit ${code}`));
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
try {
|
|
804
|
+
resolve(JSON.parse(stdout));
|
|
805
|
+
} catch {
|
|
806
|
+
reject(new ExpoConfigFailed(dir, `no JSON: ${stdout.slice(0, 200)}`));
|
|
807
|
+
}
|
|
808
|
+
});
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
const defaultGooglePlayDeps = {
|
|
812
|
+
signIn: gcloudSignIn(),
|
|
813
|
+
eas: easApi(),
|
|
814
|
+
expoConfig,
|
|
815
|
+
interactive: require_index.canPrompt(),
|
|
816
|
+
sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms))
|
|
817
|
+
};
|
|
818
|
+
/**
|
|
819
|
+
* Set up publishing a mobile app to Google Play.
|
|
820
|
+
*
|
|
821
|
+
* @throws {PlayDeveloperMissing}, {ExpoConfigIncomplete}, {PlayAppMissing},
|
|
822
|
+
* {PlayKeyUnverified} — and what each step's API throws
|
|
823
|
+
*/
|
|
824
|
+
async function deployGooglePlay(options, deps = defaultGooglePlayDeps) {
|
|
825
|
+
if (!options.developer) throw new PlayDeveloperMissing();
|
|
826
|
+
const developer = options.developer;
|
|
827
|
+
const declared = (await require_config.loadWorkspaceConfig(options.cwd)).workspace.stages;
|
|
828
|
+
const stageName = options.stage ?? declared.protected?.[0] ?? declared.deployed[0];
|
|
829
|
+
if (!stageName) throw new NoDeployedStage();
|
|
830
|
+
const loaded = await require_stage.loadStage({
|
|
831
|
+
stage: stageName,
|
|
832
|
+
...options.cwd ? { cwd: options.cwd } : {},
|
|
833
|
+
...options.profile ? { profile: options.profile } : {}
|
|
834
|
+
});
|
|
835
|
+
const { workspace, manifest, stack } = loaded;
|
|
836
|
+
const declaration = require_eas.chooseMobileApp(manifest, options.app, loaded.appName);
|
|
837
|
+
const name = declaration.id;
|
|
838
|
+
const dir = (0, node_path.join)(workspace.root, declaration.app.path);
|
|
839
|
+
const values = stack.mobileApps.find((candidate) => candidate.id === declaration.id)?.values ?? {};
|
|
840
|
+
const config = await deps.expoConfig(dir, workspace.root, values);
|
|
841
|
+
const packageName = config.android?.package;
|
|
842
|
+
if (!packageName) throw new ExpoConfigIncomplete(name, "android.package");
|
|
843
|
+
const projectId = config.extra?.eas?.projectId;
|
|
844
|
+
if (!projectId) throw new ExpoConfigIncomplete(name, "extra.eas.projectId");
|
|
845
|
+
const stages = workspace.stages.deployed.map((stage) => require_index.mobileStage(workspace, declaration, stage));
|
|
846
|
+
const easJsonPath = (0, node_path.join)(dir, "eas.json");
|
|
847
|
+
const before = (0, node_fs.existsSync)(easJsonPath) ? JSON.parse(await (0, node_fs_promises.readFile)(easJsonPath, "utf8")) : {};
|
|
848
|
+
const after = stages.reduce(require_eas.withStageProfiles, before);
|
|
849
|
+
if (JSON.stringify(before) !== JSON.stringify(after)) {
|
|
850
|
+
await (0, node_fs_promises.writeFile)(easJsonPath, `${JSON.stringify(after, null, 2)}\n`);
|
|
851
|
+
logger.log(`📝 ${easJsonPath}: each stage's build and submit profiles`);
|
|
852
|
+
}
|
|
853
|
+
const store = readPlayStore(dir);
|
|
854
|
+
const permissions = playPermissions({
|
|
855
|
+
tracks: stages.map((stage) => stage.android.track),
|
|
856
|
+
listing: (store?.listings.length ?? 0) > 0
|
|
857
|
+
});
|
|
858
|
+
const project = options.project ?? playId(workspace.name);
|
|
859
|
+
assertGoogleId("project", project);
|
|
860
|
+
const account = playId(workspace.name);
|
|
861
|
+
const token = await deps.signIn.token({ interactive: deps.interactive });
|
|
862
|
+
const created = await ensureProject(googleClient(token), project, `${workspace.name} Play`);
|
|
863
|
+
logger.log(`☁️ Google Cloud project ${project}${created ? " (created)" : ""}`);
|
|
864
|
+
const client = googleClient(token, project);
|
|
865
|
+
await enablePublisherApi(client, project);
|
|
866
|
+
const email = await ensureServiceAccount(client, project, account, `${workspace.name} publishing (gkm)`);
|
|
867
|
+
logger.log(`🤖 Service account ${email}`);
|
|
868
|
+
let access;
|
|
869
|
+
for (let attempt = 0; !access; attempt++) try {
|
|
870
|
+
access = await ensurePlayAccess(client, {
|
|
871
|
+
developer,
|
|
872
|
+
email,
|
|
873
|
+
packageName,
|
|
874
|
+
permissions
|
|
875
|
+
});
|
|
876
|
+
} catch (error) {
|
|
877
|
+
if (!(error instanceof PlayAppMissing) || !deps.interactive) throw error;
|
|
878
|
+
if (attempt === 0) logger.log(`\n⏳ ${error.message}\n Waiting for it…`);
|
|
879
|
+
await deps.sleep(15e3);
|
|
880
|
+
}
|
|
881
|
+
logger.log(`🎟️ Play access on ${packageName}: ${access} (${permissions.join(", ")})`);
|
|
882
|
+
const older = await listKeys(client, project, email);
|
|
883
|
+
const key = await createKey(client, project, email);
|
|
884
|
+
const json = JSON.parse(key.json);
|
|
885
|
+
const easProject = await deps.eas.project(projectId);
|
|
886
|
+
const credentials = await deps.eas.androidCredentials({
|
|
887
|
+
projectId,
|
|
888
|
+
fullName: easProject.fullName
|
|
889
|
+
}, packageName);
|
|
890
|
+
const easKey = await deps.eas.createKey(easProject.accountId, json);
|
|
891
|
+
await deps.eas.useForSubmissions(credentials.id, easKey.id);
|
|
892
|
+
logger.log(`🔑 New key ${keyId(key)} assigned to EAS Submit for ${packageName}`);
|
|
893
|
+
for (let attempt = 0;; attempt++) try {
|
|
894
|
+
await verifyKey(json, packageName);
|
|
895
|
+
break;
|
|
896
|
+
} catch (error) {
|
|
897
|
+
if (!(error instanceof PlayKeyUnverified) || attempt >= 5) throw error;
|
|
898
|
+
await deps.sleep(1e4);
|
|
899
|
+
}
|
|
900
|
+
logger.log("✅ The key opens an edit on the app");
|
|
901
|
+
const easOlder = (await deps.eas.keys(easProject.accountName)).filter((candidate) => candidate.clientEmail === email && candidate.id !== easKey.id);
|
|
902
|
+
for (const old of easOlder) await deps.eas.deleteKey(old.id);
|
|
903
|
+
for (const old of older) await deleteKey(client, old);
|
|
904
|
+
if (older.length + easOlder.length > 0) logger.log(`🧹 Deleted ${older.length} older key(s) from Google, ${easOlder.length} from EAS`);
|
|
905
|
+
const listing = store ? await pushPlayStore(client, packageName, store) : void 0;
|
|
906
|
+
if (listing?.listing) logger.log("🛍️ Store listing pushed");
|
|
907
|
+
if (store && !listing?.listing && listing?.reason) logger.log(`⏭️ Store listing not pushed yet (${listing.reason}): run this again after the app's first upload`);
|
|
908
|
+
if (listing?.dataSafety) logger.log("🛡️ Data safety pushed");
|
|
909
|
+
logger.log("\nLeft to do in Play Console, by hand:");
|
|
910
|
+
for (const item of PLAY_CONSOLE_CHECKLIST) logger.log(` [ ] ${item}`);
|
|
911
|
+
return {
|
|
912
|
+
packageName,
|
|
913
|
+
project,
|
|
914
|
+
email,
|
|
915
|
+
permissions,
|
|
916
|
+
access,
|
|
917
|
+
key: {
|
|
918
|
+
google: keyId(key),
|
|
919
|
+
eas: easKey.id
|
|
920
|
+
},
|
|
921
|
+
deleted: {
|
|
922
|
+
google: older.map(keyId),
|
|
923
|
+
eas: easOlder.map((candidate) => candidate.id)
|
|
924
|
+
},
|
|
925
|
+
listing
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
//#endregion
|
|
929
|
+
exports.deployGooglePlay = deployGooglePlay;
|
|
930
|
+
|
|
931
|
+
//# sourceMappingURL=googlePlay-BVcfFWRO.cjs.map
|