@geekmidas/cli 10.0.0-alpha.122 → 10.0.0-alpha.124
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{EndpointGenerator-BDa6pPgE.cjs → EndpointGenerator-B2MUa4M2.cjs} +2 -2
- package/dist/{EndpointGenerator-BDa6pPgE.cjs.map → EndpointGenerator-B2MUa4M2.cjs.map} +1 -1
- package/dist/{EndpointGenerator-CP_z_E3e.mjs → EndpointGenerator-Z_BUTocT.mjs} +2 -2
- package/dist/{EndpointGenerator-CP_z_E3e.mjs.map → EndpointGenerator-Z_BUTocT.mjs.map} +1 -1
- package/dist/{GoDaddyProvider-BJ9j9mR7.cjs → GoDaddyProvider-0z5RAN1k.cjs} +3 -3
- package/dist/{GoDaddyProvider-BJ9j9mR7.cjs.map → GoDaddyProvider-0z5RAN1k.cjs.map} +1 -1
- package/dist/{GoDaddyProvider-wIKYdm7V.mjs → GoDaddyProvider-55x4o-4z.mjs} +3 -3
- package/dist/{GoDaddyProvider-wIKYdm7V.mjs.map → GoDaddyProvider-55x4o-4z.mjs.map} +1 -1
- package/dist/{HostingerProvider-BNpS9aRJ.mjs → HostingerProvider-Cjh_2Ml6.mjs} +2 -2
- package/dist/{HostingerProvider-BNpS9aRJ.mjs.map → HostingerProvider-Cjh_2Ml6.mjs.map} +1 -1
- package/dist/{HostingerProvider-DR1iws1P.cjs → HostingerProvider-OdQmKXE3.cjs} +2 -2
- package/dist/{HostingerProvider-DR1iws1P.cjs.map → HostingerProvider-OdQmKXE3.cjs.map} +1 -1
- package/dist/{LegacyStateStore-CkXUJP7i.mjs → LegacyStateStore-C-vumKSN.mjs} +3 -3
- package/dist/{LegacyStateStore-CkXUJP7i.mjs.map → LegacyStateStore-C-vumKSN.mjs.map} +1 -1
- package/dist/{LegacyStateStore-D1S87I_B.cjs → LegacyStateStore-DRpB-yC5.cjs} +3 -3
- package/dist/{LegacyStateStore-D1S87I_B.cjs.map → LegacyStateStore-DRpB-yC5.cjs.map} +1 -1
- package/dist/{LocalStateStore-DmXfpvu4.cjs → LocalStateStore-C3ummuDT.cjs} +2 -2
- package/dist/{LocalStateStore-DmXfpvu4.cjs.map → LocalStateStore-C3ummuDT.cjs.map} +1 -1
- package/dist/{LocalStateStore-BWoWeSq0.mjs → LocalStateStore-CtOjpV1w.mjs} +2 -2
- package/dist/{LocalStateStore-BWoWeSq0.mjs.map → LocalStateStore-CtOjpV1w.mjs.map} +1 -1
- package/dist/{OpenApiTsGenerator-CsrukCE-.mjs → OpenApiTsGenerator-MtnMQL_g.mjs} +2 -2
- package/dist/{OpenApiTsGenerator-CsrukCE-.mjs.map → OpenApiTsGenerator-MtnMQL_g.mjs.map} +1 -1
- package/dist/{OpenApiTsGenerator-FNhY7Ssz.cjs → OpenApiTsGenerator-t3_E98Fi.cjs} +2 -2
- package/dist/{OpenApiTsGenerator-FNhY7Ssz.cjs.map → OpenApiTsGenerator-t3_E98Fi.cjs.map} +1 -1
- package/dist/{S3StateStore-D19JSGE9.mjs → S3StateStore-BC4Jm3Tw.mjs} +2 -2
- package/dist/{S3StateStore-D19JSGE9.mjs.map → S3StateStore-BC4Jm3Tw.mjs.map} +1 -1
- package/dist/{S3StateStore-BEdsjjP4.cjs → S3StateStore-BeArp0aF.cjs} +2 -2
- package/dist/{S3StateStore-BEdsjjP4.cjs.map → S3StateStore-BeArp0aF.cjs.map} +1 -1
- package/dist/{SSMStateStore-7xKw0Zz9.mjs → SSMStateStore-Crl76aYZ.mjs} +2 -2
- package/dist/{SSMStateStore-7xKw0Zz9.mjs.map → SSMStateStore-Crl76aYZ.mjs.map} +1 -1
- package/dist/{SSMStateStore--2bcxoiH.cjs → SSMStateStore-DcstR6cW.cjs} +2 -2
- package/dist/{SSMStateStore--2bcxoiH.cjs.map → SSMStateStore-DcstR6cW.cjs.map} +1 -1
- package/dist/{SubscriberGenerator-C8CPxrCK.cjs → SubscriberGenerator-C3QTae-R.cjs} +237 -54
- package/dist/SubscriberGenerator-C3QTae-R.cjs.map +1 -0
- package/dist/{SubscriberGenerator-y3pncae_.mjs → SubscriberGenerator-COAVPgM-.mjs} +227 -56
- package/dist/SubscriberGenerator-COAVPgM-.mjs.map +1 -0
- package/dist/{add-DlFUKdjX.cjs → add-BFjb6mMq.cjs} +7 -7
- package/dist/{add-DlFUKdjX.cjs.map → add-BFjb6mMq.cjs.map} +1 -1
- package/dist/{add-Bu6FkaTn.mjs → add-CIjmgzkb.mjs} +6 -6
- package/dist/{add-Bu6FkaTn.mjs.map → add-CIjmgzkb.mjs.map} +1 -1
- package/dist/{bundler-Bg0lPDoi.cjs → bundler-CiUoIsku.cjs} +5 -5
- package/dist/{bundler-Bg0lPDoi.cjs.map → bundler-CiUoIsku.cjs.map} +1 -1
- package/dist/{bundler-CgE5XTyr.mjs → bundler-D6KFNpQk.mjs} +5 -5
- package/dist/{bundler-CgE5XTyr.mjs.map → bundler-D6KFNpQk.mjs.map} +1 -1
- package/dist/{cli-z5S3bHyK.mjs → cli-DNtSTtMc.mjs} +4 -4
- package/dist/{cli-z5S3bHyK.mjs.map → cli-DNtSTtMc.mjs.map} +1 -1
- package/dist/{cli-Cvjlnpon.cjs → cli-r4eUr98C.cjs} +4 -4
- package/dist/{cli-Cvjlnpon.cjs.map → cli-r4eUr98C.cjs.map} +1 -1
- package/dist/{compose-bKLAtHLj.cjs → compose-BfXXWJSp.cjs} +362 -59
- package/dist/compose-BfXXWJSp.cjs.map +1 -0
- package/dist/{compose-CgBPA44v.mjs → compose-DJvvmF3X.mjs} +336 -33
- package/dist/compose-DJvvmF3X.mjs.map +1 -0
- package/dist/{config-D48erO2j.d.mts → config-IgK6YaGp.d.cts} +3 -4
- package/dist/config-IgK6YaGp.d.cts.map +1 -0
- package/dist/{config-DHDtsQrB.d.cts → config-kE-p5hxq.d.mts} +3 -4
- package/dist/config-kE-p5hxq.d.mts.map +1 -0
- package/dist/config.cjs +2 -2
- package/dist/config.d.cts +3 -3
- package/dist/config.d.mts +3 -3
- package/dist/config.mjs +2 -2
- package/dist/{credentialSchemas-B67WJOOm.cjs → credentialSchemas-AhIBuQxQ.cjs} +2 -2
- package/dist/{credentialSchemas-B67WJOOm.cjs.map → credentialSchemas-AhIBuQxQ.cjs.map} +1 -1
- package/dist/{credentialSchemas-DDOLRCLF.mjs → credentialSchemas-DLKbxYBy.mjs} +2 -2
- package/dist/{credentialSchemas-DDOLRCLF.mjs.map → credentialSchemas-DLKbxYBy.mjs.map} +1 -1
- package/dist/{credentials-Zqr3QQYB.cjs → credentials-D7XJypNd.cjs} +173 -3
- package/dist/credentials-D7XJypNd.cjs.map +1 -0
- package/dist/{credentials-B5N5yKk6.mjs → credentials-DpfnUvIR.mjs} +132 -4
- package/dist/credentials-DpfnUvIR.mjs.map +1 -0
- package/dist/deploy/public.cjs +16 -17
- package/dist/deploy/public.cjs.map +1 -1
- package/dist/deploy/public.d.cts +3 -5
- package/dist/deploy/public.d.cts.map +1 -1
- package/dist/deploy/public.d.mts +3 -5
- package/dist/deploy/public.d.mts.map +1 -1
- package/dist/deploy/public.mjs +11 -12
- package/dist/deploy/public.mjs.map +1 -1
- package/dist/{deploy-CI85F6dr.cjs → deploy-BbI1UMh3.cjs} +477 -156
- package/dist/deploy-BbI1UMh3.cjs.map +1 -0
- package/dist/{deploy-Ddy8UBoE.mjs → deploy-CiRYwFmr.mjs} +456 -117
- package/dist/deploy-CiRYwFmr.mjs.map +1 -0
- package/dist/{discover-C5roJuc9.mjs → discover-BvJOVsYG.mjs} +66 -14
- package/dist/discover-BvJOVsYG.mjs.map +1 -0
- package/dist/{discover-DozUKs8f.cjs → discover-ojkcbxHw.cjs} +88 -12
- package/dist/discover-ojkcbxHw.cjs.map +1 -0
- package/dist/{dokploy-CiozgdKO.cjs → dokploy-C0aQrCgs.cjs} +2 -2
- package/dist/{dokploy-CiozgdKO.cjs.map → dokploy-C0aQrCgs.cjs.map} +1 -1
- package/dist/{dokploy-Boj1NSHV.mjs → dokploy-MM7c3IE0.mjs} +2 -2
- package/dist/{dokploy-Boj1NSHV.mjs.map → dokploy-MM7c3IE0.mjs.map} +1 -1
- package/dist/{drivers-DbjiYpW6.cjs → drivers-CQ32PB5w.cjs} +2 -2
- package/dist/{drivers-DbjiYpW6.cjs.map → drivers-CQ32PB5w.cjs.map} +1 -1
- package/dist/{drivers-BjGDYfnC.mjs → drivers-Chb5Akov.mjs} +2 -2
- package/dist/{drivers-BjGDYfnC.mjs.map → drivers-Chb5Akov.mjs.map} +1 -1
- package/dist/{eas-DCg41jKK.mjs → eas-1Ee6kql0.mjs} +2 -2
- package/dist/{eas-DCg41jKK.mjs.map → eas-1Ee6kql0.mjs.map} +1 -1
- package/dist/{eas-BmBjIFf_.cjs → eas-CJoys9yh.cjs} +2 -2
- package/dist/{eas-BmBjIFf_.cjs.map → eas-CJoys9yh.cjs.map} +1 -1
- package/dist/{emit-C1e8qD08.mjs → emit-CNpuV_6Q.mjs} +17 -1
- package/dist/emit-CNpuV_6Q.mjs.map +1 -0
- package/dist/{emit-Dmc1nvdS.cjs → emit-qvHxMV9b.cjs} +17 -1
- package/dist/emit-qvHxMV9b.cjs.map +1 -0
- package/dist/{encryption-QOQbGXgV.mjs → encryption-B8Bk1Mvu.mjs} +3 -3
- package/dist/{encryption-QOQbGXgV.mjs.map → encryption-B8Bk1Mvu.mjs.map} +1 -1
- package/dist/{encryption-CwLWV2ig.cjs → encryption-Bo9VlRnl.cjs} +3 -3
- package/dist/{encryption-CwLWV2ig.cjs.map → encryption-Bo9VlRnl.cjs.map} +1 -1
- package/dist/{engine-HVqo3mQu.mjs → engine-BuinpJfS.mjs} +7 -8
- package/dist/{engine-HVqo3mQu.mjs.map → engine-BuinpJfS.mjs.map} +1 -1
- package/dist/{engine-D7YNY3v7.cjs → engine-esxTxcIx.cjs} +10 -11
- package/dist/{engine-D7YNY3v7.cjs.map → engine-esxTxcIx.cjs.map} +1 -1
- package/dist/{github-CO8W9SDs.cjs → github-BEA4K3_X.cjs} +80 -47
- package/dist/github-BEA4K3_X.cjs.map +1 -0
- package/dist/{github-CQt3xc3o.mjs → github-BUlKJfOV.mjs} +78 -45
- package/dist/github-BUlKJfOV.mjs.map +1 -0
- package/dist/{googlePlay-Cfu17aEp.mjs → googlePlay-CK05QiTq.mjs} +4 -4
- package/dist/{googlePlay-Cfu17aEp.mjs.map → googlePlay-CK05QiTq.mjs.map} +1 -1
- package/dist/{googlePlay-DSuF6jbF.cjs → googlePlay-CiqAS2ma.cjs} +5 -5
- package/dist/{googlePlay-DSuF6jbF.cjs.map → googlePlay-CiqAS2ma.cjs.map} +1 -1
- package/dist/{index-8OfpOtSI.d.cts → index-4CO0HD27.d.cts} +2 -2
- package/dist/{index-8OfpOtSI.d.cts.map → index-4CO0HD27.d.cts.map} +1 -1
- package/dist/{index-Dpji6Bxq.d.mts → index-DhUf0B0m.d.mts} +2 -2
- package/dist/{index-Dpji6Bxq.d.mts.map → index-DhUf0B0m.d.mts.map} +1 -1
- package/dist/index.cjs +81 -82
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +64 -65
- package/dist/index.mjs.map +1 -1
- package/dist/{local-BwC2v2Y8.mjs → local-B7Zv1NoA.mjs} +2 -2
- package/dist/{local-BwC2v2Y8.mjs.map → local-B7Zv1NoA.mjs.map} +1 -1
- package/dist/{local-DNhnkugT.cjs → local-OPC9BCZt.cjs} +3 -3
- package/dist/{local-DNhnkugT.cjs.map → local-OPC9BCZt.cjs.map} +1 -1
- package/dist/{migrate-CGkir5LM.mjs → migrate-BzCHT9qs.mjs} +3 -3
- package/dist/{migrate-CGkir5LM.mjs.map → migrate-BzCHT9qs.mjs.map} +1 -1
- package/dist/{migrate-CXsMaHbb.cjs → migrate-C-hmbjpG.cjs} +3 -3
- package/dist/{migrate-CXsMaHbb.cjs.map → migrate-C-hmbjpG.cjs.map} +1 -1
- package/dist/{migrate-Bkz8BGaZ.mjs → migrate-CnbvrVag.mjs} +4 -4
- package/dist/{migrate-Bkz8BGaZ.mjs.map → migrate-CnbvrVag.mjs.map} +1 -1
- package/dist/{migrate-dbAI2XF8.cjs → migrate-DeQJBexh.cjs} +4 -4
- package/dist/{migrate-dbAI2XF8.cjs.map → migrate-DeQJBexh.cjs.map} +1 -1
- package/dist/openapi.cjs +3 -3
- package/dist/openapi.d.cts +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/openapi.mjs +3 -3
- package/dist/{publish-eUcWvSdP.mjs → publish-BISBrrqR.mjs} +2 -2
- package/dist/{publish-eUcWvSdP.mjs.map → publish-BISBrrqR.mjs.map} +1 -1
- package/dist/{publish-Bbd-2PI2.cjs → publish-DtSIB-hZ.cjs} +2 -2
- package/dist/{publish-Bbd-2PI2.cjs.map → publish-DtSIB-hZ.cjs.map} +1 -1
- package/dist/{ready-BOfogqzr.mjs → ready-DqsyadOs.mjs} +6 -6
- package/dist/{ready-BOfogqzr.mjs.map → ready-DqsyadOs.mjs.map} +1 -1
- package/dist/{ready-BFFP838V.cjs → ready-IVGu-q18.cjs} +6 -6
- package/dist/{ready-BFFP838V.cjs.map → ready-IVGu-q18.cjs.map} +1 -1
- package/dist/reconcile/public.cjs +2 -2
- package/dist/reconcile/public.d.cts +1 -2
- package/dist/reconcile/public.d.cts.map +1 -1
- package/dist/reconcile/public.d.mts +1 -2
- package/dist/reconcile/public.d.mts.map +1 -1
- package/dist/reconcile/public.mjs +2 -2
- package/dist/{resolve-DSilYpzm.mjs → resolve-BT7fkPN5.mjs} +4 -4
- package/dist/{resolve-DSilYpzm.mjs.map → resolve-BT7fkPN5.mjs.map} +1 -1
- package/dist/{resolve-CyprRcQo.cjs → resolve-C7GKQcE4.cjs} +4 -4
- package/dist/{resolve-CyprRcQo.cjs.map → resolve-C7GKQcE4.cjs.map} +1 -1
- package/dist/{rollback-DfwsnwGO.cjs → rollback-BEiB-Pje.cjs} +7 -7
- package/dist/{rollback-DfwsnwGO.cjs.map → rollback-BEiB-Pje.cjs.map} +1 -1
- package/dist/{rollback-C4ri3h7i.mjs → rollback-DbP0KuDw.mjs} +7 -7
- package/dist/{rollback-C4ri3h7i.mjs.map → rollback-DbP0KuDw.mjs.map} +1 -1
- package/dist/{runtime-ACdfK59n.d.cts → runtime-BFJQNqwd.d.mts} +2 -2
- package/dist/{runtime-ACdfK59n.d.cts.map → runtime-BFJQNqwd.d.mts.map} +1 -1
- package/dist/{runtime-YyQuekxi.d.mts → runtime-BJO-FFef.d.cts} +2 -2
- package/dist/{runtime-YyQuekxi.d.mts.map → runtime-BJO-FFef.d.cts.map} +1 -1
- package/dist/{s3-DQXZdmCM.mjs → s3-B4FTQQkV.mjs} +2 -2
- package/dist/{s3-DQXZdmCM.mjs.map → s3-B4FTQQkV.mjs.map} +1 -1
- package/dist/{s3-CrPoA89T.cjs → s3-DwKLXwUT.cjs} +2 -2
- package/dist/{s3-CrPoA89T.cjs.map → s3-DwKLXwUT.cjs.map} +1 -1
- package/dist/sandbox/credentials-worker.cjs +2 -2
- package/dist/sandbox/credentials-worker.mjs +2 -2
- package/dist/sandbox/discover-worker.cjs +1 -1
- package/dist/sandbox/discover-worker.mjs +1 -1
- package/dist/sandbox/migrate-worker.cjs +1 -1
- package/dist/sandbox/migrate-worker.mjs +1 -1
- package/dist/{seeds-CFG-f24B.cjs → seeds-DOfxmDRO.cjs} +362 -34
- package/dist/seeds-DOfxmDRO.cjs.map +1 -0
- package/dist/{seeds-oY0qDqui.mjs → seeds-rG-Evfz9.mjs} +341 -31
- package/dist/seeds-rG-Evfz9.mjs.map +1 -0
- package/dist/{serverSetup-CQZt0OT6.cjs → serverSetup-CWWVcU6Y.cjs} +6 -6
- package/dist/{serverSetup-CQZt0OT6.cjs.map → serverSetup-CWWVcU6Y.cjs.map} +1 -1
- package/dist/{serverSetup-D8nngcNL.mjs → serverSetup-xGw1fxuZ.mjs} +6 -6
- package/dist/{serverSetup-D8nngcNL.mjs.map → serverSetup-xGw1fxuZ.mjs.map} +1 -1
- package/dist/{sniffer-BAmtXzou.cjs → sniffer-C7O9Pna_.cjs} +9 -9
- package/dist/{sniffer-BAmtXzou.cjs.map → sniffer-C7O9Pna_.cjs.map} +1 -1
- package/dist/{sniffer-Cgx608Tl.mjs → sniffer-DVPgfyHr.mjs} +9 -9
- package/dist/{sniffer-Cgx608Tl.mjs.map → sniffer-DVPgfyHr.mjs.map} +1 -1
- package/dist/{dns-CfEZHt7r.cjs → snsSubscriptions-CSkteK2J.cjs} +520 -3
- package/dist/snsSubscriptions-CSkteK2J.cjs.map +1 -0
- package/dist/{dns-BgxvvcQa.mjs → snsSubscriptions-CvBIGGp0.mjs} +431 -6
- package/dist/snsSubscriptions-CvBIGGp0.mjs.map +1 -0
- package/dist/{sst-C1pxbFEd.mjs → sst-CTqZZl1_.mjs} +3 -4
- package/dist/{sst-C1pxbFEd.mjs.map → sst-CTqZZl1_.mjs.map} +1 -1
- package/dist/{sst-B-Uz4cvm.cjs → sst-lEkBwzGc.cjs} +4 -5
- package/dist/{sst-B-Uz4cvm.cjs.map → sst-lEkBwzGc.cjs.map} +1 -1
- package/dist/{stage-BKDdQm65.mjs → stage-2EKpzPQ1.mjs} +6 -6
- package/dist/{stage-BKDdQm65.mjs.map → stage-2EKpzPQ1.mjs.map} +1 -1
- package/dist/{stage-CJysBXWB.cjs → stage-fRfFtLwj.cjs} +7 -7
- package/dist/{stage-CJysBXWB.cjs.map → stage-fRfFtLwj.cjs.map} +1 -1
- package/dist/{stages-D4SnG5yl.cjs → stages-B7mCWI_R.cjs} +2 -2
- package/dist/{stages-D4SnG5yl.cjs.map → stages-B7mCWI_R.cjs.map} +1 -1
- package/dist/{stages-Cb9rqRbN.mjs → stages-DSltTW2d.mjs} +2 -2
- package/dist/{stages-Cb9rqRbN.mjs.map → stages-DSltTW2d.mjs.map} +1 -1
- package/dist/target/public.cjs +1 -1
- package/dist/target/public.d.cts +2 -2
- package/dist/target/public.d.mts +2 -2
- package/dist/target/public.mjs +1 -1
- package/dist/{terminal-CW_dWeTe.mjs → terminal-BAVrDhhN.mjs} +3 -3
- package/dist/{terminal-CW_dWeTe.mjs.map → terminal-BAVrDhhN.mjs.map} +1 -1
- package/dist/{terminal-DJlqcW_U.cjs → terminal-D1aeTkgv.cjs} +3 -3
- package/dist/{terminal-DJlqcW_U.cjs.map → terminal-D1aeTkgv.cjs.map} +1 -1
- package/dist/{types-HqVPddRK.d.mts → types-CFU2uvrz.d.cts} +263 -4
- package/dist/types-CFU2uvrz.d.cts.map +1 -0
- package/dist/{types-B6UXlEkf.d.cts → types-D3BeGucy.d.mts} +262 -5
- package/dist/types-D3BeGucy.d.mts.map +1 -0
- package/dist/vitest.cjs +1 -1
- package/dist/vitest.mjs +1 -1
- package/dist/workspace/index.cjs +1 -1
- package/dist/workspace/index.d.cts +2 -2
- package/dist/workspace/index.d.mts +2 -2
- package/dist/workspace/index.mjs +1 -1
- package/dist/{workspace-BwqQnool.cjs → workspace-C82GLPnf.cjs} +8 -2
- package/dist/workspace-C82GLPnf.cjs.map +1 -0
- package/dist/{workspace-D1QbNd55.mjs → workspace-DUnnVjqC.mjs} +8 -2
- package/dist/workspace-DUnnVjqC.mjs.map +1 -0
- package/package.json +17 -16
- package/dist/SubscriberGenerator-C8CPxrCK.cjs.map +0 -1
- package/dist/SubscriberGenerator-y3pncae_.mjs.map +0 -1
- package/dist/compose-CgBPA44v.mjs.map +0 -1
- package/dist/compose-bKLAtHLj.cjs.map +0 -1
- package/dist/config-D48erO2j.d.mts.map +0 -1
- package/dist/config-DHDtsQrB.d.cts.map +0 -1
- package/dist/credentials-B5N5yKk6.mjs.map +0 -1
- package/dist/credentials-Zqr3QQYB.cjs.map +0 -1
- package/dist/deploy-CI85F6dr.cjs.map +0 -1
- package/dist/deploy-Ddy8UBoE.mjs.map +0 -1
- package/dist/discover-C5roJuc9.mjs.map +0 -1
- package/dist/discover-CSW_BMWa.d.cts +0 -125
- package/dist/discover-CSW_BMWa.d.cts.map +0 -1
- package/dist/discover-DozUKs8f.cjs.map +0 -1
- package/dist/discover-xtT7fgWg.d.mts +0 -125
- package/dist/discover-xtT7fgWg.d.mts.map +0 -1
- package/dist/dns-BgxvvcQa.mjs.map +0 -1
- package/dist/dns-CfEZHt7r.cjs.map +0 -1
- package/dist/emit-C1e8qD08.mjs.map +0 -1
- package/dist/emit-Dmc1nvdS.cjs.map +0 -1
- package/dist/github-CO8W9SDs.cjs.map +0 -1
- package/dist/github-CQt3xc3o.mjs.map +0 -1
- package/dist/run-BJLIerMM.mjs +0 -133
- package/dist/run-BJLIerMM.mjs.map +0 -1
- package/dist/run-fVZ-rLnc.cjs +0 -174
- package/dist/run-fVZ-rLnc.cjs.map +0 -1
- package/dist/sandbox-C4g1eG31.d.mts +0 -104
- package/dist/sandbox-C4g1eG31.d.mts.map +0 -1
- package/dist/sandbox-CdKgQ3XU.d.cts +0 -104
- package/dist/sandbox-CdKgQ3XU.d.cts.map +0 -1
- package/dist/seeds-CFG-f24B.cjs.map +0 -1
- package/dist/seeds-oY0qDqui.mjs.map +0 -1
- package/dist/types-B6UXlEkf.d.cts.map +0 -1
- package/dist/types-HqVPddRK.d.mts.map +0 -1
- package/dist/workspace-BwqQnool.cjs.map +0 -1
- package/dist/workspace-D1QbNd55.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drivers-DbjiYpW6.cjs","names":["DEFAULT_CACHE","holdsEveryCache","resolve","dirname","join","existsSync","readFileSync"],"sources":["../src/generators/drivers.ts"],"sourcesContent":["/**\n * Driver registration for generated entry points.\n *\n * A construct declares that it needs object storage and is handed one URL; the\n * scheme in that URL picks the driver that builds the client. Which drivers\n * exist is therefore the *entry point's* decision, not the construct's and not\n * the application's — an app that never talks to S3 should never resolve the AWS\n * SDK, and application code that registers a driver has named a provider in the\n * one layer this design keeps provider-free.\n *\n * So the generated entries register: `gkm dev`'s server for local development,\n * and each Lambda handler for its own target. Registration is an explicit call\n * rather than an import side effect, because a side-effecting module is exactly\n * what a bundler is entitled to drop.\n */\n\nimport { existsSync, readFileSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport {\n\ttype CacheBackend,\n\tDEFAULT_CACHE,\n\ttype EventsBackend,\n\tholdsEveryCache,\n} from '../types.js';\n\n/** The `s3://` driver, which serves MinIO locally and S3 deployed. */\nconst S3 = {\n\timports: `import { registerStorageDriver } from '@geekmidas/storage';\\nimport { s3Driver } from '@geekmidas/storage/aws';`,\n\tsetup: 'registerStorageDriver(s3Driver);',\n};\n\n/** What a generated entry needs in order to register the drivers it uses. */\nexport interface RuntimeDrivers {\n\t/** Import lines, or `''` when there is nothing to register. */\n\timports: string;\n\t/** The registration calls, or `''`. */\n\tsetup: string;\n}\n\n/** @deprecated The same shape, from when only storage had drivers. */\nexport type StorageDrivers = RuntimeDrivers;\n\nconst NONE: RuntimeDrivers = { imports: '', setup: '' };\n\n/** The Redis wire protocol's drivers: both schemes, plain and TLS. */\nconst REDIS: RuntimeDrivers = {\n\timports: `import { registerCacheDriver } from '@geekmidas/cache';\\nimport { redisCacheDriver, redissCacheDriver } from '@geekmidas/cache/redis';`,\n\tsetup:\n\t\t'registerCacheDriver(redisCacheDriver);\\nregisterCacheDriver(redissCacheDriver);',\n};\n\n/**\n * The cache driver for a backend — exactly one, never all three.\n *\n * Keyed off the *backend* rather than off a dependency, because unlike storage\n * the backend already says which driver is right: a project caching in Postgres\n * resolves `pg` and never `ioredis`, and one caching in Upstash resolves\n * neither. That is the whole reason the drivers live behind separate subpaths.\n *\n * Both HTTP schemes are registered for Upstash, because the local proxy speaks\n * `http://` and Upstash speaks `https://`, and an entry that registered one\n * would work in exactly one of the two places.\n */\nconst CACHE_DRIVERS: Record<CacheBackend, RuntimeDrivers> = {\n\tupstash: {\n\t\timports: `import { registerCacheDriver } from '@geekmidas/cache';\\nimport { upstashCacheDriver, upstashInsecureCacheDriver } from '@geekmidas/cache/upstash';`,\n\t\tsetup:\n\t\t\t'registerCacheDriver(upstashCacheDriver);\\nregisterCacheDriver(upstashInsecureCacheDriver);',\n\t},\n\telasticache: REDIS,\n\t// The stack's own Redis speaks the same wire protocol — and a managed\n\t// Redis set in its place may be `rediss://`.\n\tredis: REDIS,\n\tdb: {\n\t\timports: `import { registerCacheDriver } from '@geekmidas/cache';\\nimport { postgresCacheDriver } from '@geekmidas/cache/postgres';`,\n\t\tsetup: 'registerCacheDriver(postgresCacheDriver);',\n\t},\n};\n\n/**\n * The events drivers for a broker — only its own, so a bundle never resolves\n * another broker's client library.\n *\n * Keyed off the backend, as the cache is: the target already says which broker\n * carries the app's topics and queues. SNS registers SQS beside it, because on\n * AWS a topic is `sns://` and a queue is `sqs://`, and an entry registering one\n * would publish to topics and fail on the first queue.\n */\nconst EVENTS_DRIVERS: Record<EventsBackend, RuntimeDrivers> = {\n\tpgboss: {\n\t\timports: `import { registerEventsDriver } from '@geekmidas/events';\\nimport { pgbossEventsDriver } from '@geekmidas/events/pgboss';`,\n\t\tsetup: 'registerEventsDriver(pgbossEventsDriver);',\n\t},\n\tsns: {\n\t\timports: `import { registerEventsDriver } from '@geekmidas/events';\\nimport { snsEventsDriver } from '@geekmidas/events/sns';\\nimport { sqsEventsDriver } from '@geekmidas/events/sqs';`,\n\t\tsetup:\n\t\t\t'registerEventsDriver(snsEventsDriver);\\nregisterEventsDriver(sqsEventsDriver);',\n\t},\n\trabbitmq: {\n\t\timports: `import { registerEventsDriver } from '@geekmidas/events';\\nimport { rabbitmqEventsDriver } from '@geekmidas/events/rabbitmq';`,\n\t\tsetup: 'registerEventsDriver(rabbitmqEventsDriver);',\n\t},\n};\n\n/**\n * The KMS cipher, for an entry whose keys are on AWS. `aes256gcm:` is built\n * into `Encryption`; only this one needs a driver, because only it needs an\n * SDK — and only an AWS build has `kms://` keys to open.\n */\nconst KMS: RuntimeDrivers = {\n\timports: `import { registerCipherDriver } from '@geekmidas/constructs/encryption';\\nimport { kmsCipherDriver } from '@geekmidas/constructs/encryption/kms';`,\n\tsetup: 'registerCipherDriver(kmsCipherDriver);',\n};\n\n/**\n * Whether an entry opens KMS keys: built for AWS, from a process that reaches\n * an `Encryption`. A server's keys are keyrings, and `gkm dev` and `gkm test`\n * derive theirs locally — none of them imports `@aws-sdk/client-kms`.\n */\nexport function opensKmsKeys(\n\tmanifest: Record<string, { kind: string }>,\n\ttarget: 'aws' | 'server',\n): boolean {\n\treturn (\n\t\ttarget === 'aws' &&\n\t\tObject.values(manifest).some((d) => d.kind === 'encryption')\n\t);\n}\n\n/**\n * Everything a generated entry should register: object storage, the cache,\n * the events broker and the KMS cipher.\n *\n * Merged here rather than threaded separately, because a generated file has one\n * import block and one setup block whatever fills them.\n */\nexport function driversFor(options: {\n\tappRoot: string;\n\t/**\n\t * The cache backends actually in play — normally one, and more than one only\n\t * where an app declares two caches that live in different places.\n\t *\n\t * Absent or empty means nothing declared a cache, so no cache driver is\n\t * registered at all.\n\t */\n\tcache?: CacheBackend | readonly CacheBackend[] | false;\n\t/**\n\t * The events brokers in play — see {@link eventsBackendsIn}. Absent or\n\t * empty means nothing declared a topic or a queue, so no events driver is\n\t * registered and `@geekmidas/events` is never resolved.\n\t */\n\tevents?: EventsBackend | readonly EventsBackend[] | false;\n\t/** Whether it opens KMS keys — see {@link opensKmsKeys}. */\n\tkms?: boolean;\n}): RuntimeDrivers {\n\tconst backends = listOf(options.cache);\n\tconst brokers = listOf(options.events);\n\n\tconst parts = [\n\t\tstorageDriversFor(options.appRoot),\n\t\t...[...new Set(backends)].map(\n\t\t\t(backend) => CACHE_DRIVERS[backend] ?? CACHE_DRIVERS[DEFAULT_CACHE.aws],\n\t\t),\n\t\t...[...new Set(brokers)].map((broker) => EVENTS_DRIVERS[broker]),\n\t\t...(options.kms ? [KMS] : []),\n\t].filter((part) => part.imports || part.setup);\n\n\tif (parts.length === 0) return NONE;\n\n\treturn {\n\t\timports: parts.map((part) => part.imports).join('\\n'),\n\t\tsetup: parts.map((part) => part.setup).join('\\n'),\n\t};\n}\n\nfunction listOf<T extends string>(\n\tvalue: T | readonly T[] | false | undefined,\n): readonly T[] {\n\tif (value === false || value === undefined) return [];\n\treturn typeof value === 'string' ? [value] : (value as readonly T[]);\n}\n\n/**\n * Which events brokers an app's entry has to speak: the target's, when the app\n * declares a `Topic` or a `Queue` — and none at all when it declares neither,\n * so a project without events never resolves `@geekmidas/events`.\n *\n * A worker on pg-boss counts too: a server schedules its crons through the\n * pg-boss broker, which the plan provisions for a declared worker beside a\n * declared database (`workerBroker` in `reconcile/plan.ts`) whether or not\n * anything publishes.\n */\nexport function eventsBackendsIn(\n\tmanifest: Record<string, { kind: string }>,\n\tconfigured: EventsBackend,\n): EventsBackend[] {\n\tconst kinds = new Set(Object.values(manifest).map((d) => d.kind));\n\tconst declared =\n\t\tkinds.has('topic') ||\n\t\tkinds.has('queue') ||\n\t\t(configured === 'pgboss' && kinds.has('worker') && kinds.has('database'));\n\n\treturn declared ? [configured] : [];\n}\n\n/**\n * Which cache backends an app's entry actually has to speak.\n *\n * The config answers this for a cache that named nowhere, and the *declaration*\n * answers it for one that named a database — `orders.cache('Sessions')` is a\n * statement about the application, so a deployment cannot move that cache and\n * the entry cannot register a driver for somewhere else.\n *\n * Reading only the config is how an entry ends up registering the Upstash\n * driver for a URL the target composed as `postgres://`, which fails at the\n * first request with `UnregisteredCacheScheme` and a stack that points at the\n * cache rather than at the config that disagreed.\n */\nexport function cacheBackendsIn(\n\tmanifest: Record<string, { kind: string; of?: string }>,\n\tconfigured: CacheBackend,\n): CacheBackend[] {\n\tconst caches = Object.values(manifest).filter((d) => d.kind === 'cache');\n\n\t// The stack's own Redis holds every cache, one that named a database too.\n\treturn [\n\t\t...new Set(\n\t\t\tcaches.map((c) =>\n\t\t\t\tc.of && !holdsEveryCache(configured) ? 'db' : configured,\n\t\t\t),\n\t\t),\n\t];\n}\n\n/**\n * The drivers an app's entry points should register.\n *\n * Keyed off the dependency rather than off a declared bucket, because the entry\n * is generated before discovery has run on the watcher's rebuild path — and an\n * app that installed `@geekmidas/storage` has already paid for the SDK it would\n * otherwise resolve lazily.\n *\n * \"Installed\" the way Node resolves it: the app's own `package.json`, or any\n * directory above it. A workspace that lists `@geekmidas/storage` once, at its\n * root, is the common case — and reading only the app's file registered no S3\n * driver there, so the first service to inject a bucket threw\n * `UnregisteredStorageScheme`. A queue whose consumer depended on one logged\n * that once and was never polled.\n */\nexport function storageDriversFor(appRoot: string): RuntimeDrivers {\n\treturn installedFrom(appRoot, '@geekmidas/storage') ? S3 : NONE;\n}\n\n/**\n * Whether `pkg` is a dependency of the app at `appRoot` the way Node resolves\n * it: listed in the app's own `package.json`, or in any directory above it.\n */\nexport function installedFrom(appRoot: string, pkg: string): boolean {\n\tfor (let dir = resolve(appRoot); ; dir = dirname(dir)) {\n\t\tif (lists(join(dir, 'package.json'), pkg)) return true;\n\t\tif (dirname(dir) === dir) return false;\n\t}\n}\n\nfunction lists(manifest: string, dependency: string): boolean {\n\tif (!existsSync(manifest)) return false;\n\n\ttry {\n\t\tconst pkg = JSON.parse(readFileSync(manifest, 'utf8')) as {\n\t\t\tdependencies?: Record<string, string>;\n\t\t\tdevDependencies?: Record<string, string>;\n\t\t};\n\n\t\treturn Boolean(\n\t\t\tpkg.dependencies?.[dependency] ?? pkg.devDependencies?.[dependency],\n\t\t);\n\t} catch {\n\t\t// An unreadable package.json is the build's problem, not this function's.\n\t\treturn false;\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"drivers-CQ32PB5w.cjs","names":["DEFAULT_CACHE","holdsEveryCache","resolve","dirname","join","existsSync","readFileSync"],"sources":["../src/generators/drivers.ts"],"sourcesContent":["/**\n * Driver registration for generated entry points.\n *\n * A construct declares that it needs object storage and is handed one URL; the\n * scheme in that URL picks the driver that builds the client. Which drivers\n * exist is therefore the *entry point's* decision, not the construct's and not\n * the application's — an app that never talks to S3 should never resolve the AWS\n * SDK, and application code that registers a driver has named a provider in the\n * one layer this design keeps provider-free.\n *\n * So the generated entries register: `gkm dev`'s server for local development,\n * and each Lambda handler for its own target. Registration is an explicit call\n * rather than an import side effect, because a side-effecting module is exactly\n * what a bundler is entitled to drop.\n */\n\nimport { existsSync, readFileSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport {\n\ttype CacheBackend,\n\tDEFAULT_CACHE,\n\ttype EventsBackend,\n\tholdsEveryCache,\n} from '../types.js';\n\n/** The `s3://` driver, which serves MinIO locally and S3 deployed. */\nconst S3 = {\n\timports: `import { registerStorageDriver } from '@geekmidas/storage';\\nimport { s3Driver } from '@geekmidas/storage/aws';`,\n\tsetup: 'registerStorageDriver(s3Driver);',\n};\n\n/** What a generated entry needs in order to register the drivers it uses. */\nexport interface RuntimeDrivers {\n\t/** Import lines, or `''` when there is nothing to register. */\n\timports: string;\n\t/** The registration calls, or `''`. */\n\tsetup: string;\n}\n\n/** @deprecated The same shape, from when only storage had drivers. */\nexport type StorageDrivers = RuntimeDrivers;\n\nconst NONE: RuntimeDrivers = { imports: '', setup: '' };\n\n/** The Redis wire protocol's drivers: both schemes, plain and TLS. */\nconst REDIS: RuntimeDrivers = {\n\timports: `import { registerCacheDriver } from '@geekmidas/cache';\\nimport { redisCacheDriver, redissCacheDriver } from '@geekmidas/cache/redis';`,\n\tsetup:\n\t\t'registerCacheDriver(redisCacheDriver);\\nregisterCacheDriver(redissCacheDriver);',\n};\n\n/**\n * The cache driver for a backend — exactly one, never all three.\n *\n * Keyed off the *backend* rather than off a dependency, because unlike storage\n * the backend already says which driver is right: a project caching in Postgres\n * resolves `pg` and never `ioredis`, and one caching in Upstash resolves\n * neither. That is the whole reason the drivers live behind separate subpaths.\n *\n * Both HTTP schemes are registered for Upstash, because the local proxy speaks\n * `http://` and Upstash speaks `https://`, and an entry that registered one\n * would work in exactly one of the two places.\n */\nconst CACHE_DRIVERS: Record<CacheBackend, RuntimeDrivers> = {\n\tupstash: {\n\t\timports: `import { registerCacheDriver } from '@geekmidas/cache';\\nimport { upstashCacheDriver, upstashInsecureCacheDriver } from '@geekmidas/cache/upstash';`,\n\t\tsetup:\n\t\t\t'registerCacheDriver(upstashCacheDriver);\\nregisterCacheDriver(upstashInsecureCacheDriver);',\n\t},\n\telasticache: REDIS,\n\t// The stack's own Redis speaks the same wire protocol — and a managed\n\t// Redis set in its place may be `rediss://`.\n\tredis: REDIS,\n\tdb: {\n\t\timports: `import { registerCacheDriver } from '@geekmidas/cache';\\nimport { postgresCacheDriver } from '@geekmidas/cache/postgres';`,\n\t\tsetup: 'registerCacheDriver(postgresCacheDriver);',\n\t},\n};\n\n/**\n * The events drivers for a broker — only its own, so a bundle never resolves\n * another broker's client library.\n *\n * Keyed off the backend, as the cache is: the target already says which broker\n * carries the app's topics and queues. SNS registers SQS beside it, because on\n * AWS a topic is `sns://` and a queue is `sqs://`, and an entry registering one\n * would publish to topics and fail on the first queue.\n */\nconst EVENTS_DRIVERS: Record<EventsBackend, RuntimeDrivers> = {\n\tpgboss: {\n\t\timports: `import { registerEventsDriver } from '@geekmidas/events';\\nimport { pgbossEventsDriver } from '@geekmidas/events/pgboss';`,\n\t\tsetup: 'registerEventsDriver(pgbossEventsDriver);',\n\t},\n\tsns: {\n\t\timports: `import { registerEventsDriver } from '@geekmidas/events';\\nimport { snsEventsDriver } from '@geekmidas/events/sns';\\nimport { sqsEventsDriver } from '@geekmidas/events/sqs';`,\n\t\tsetup:\n\t\t\t'registerEventsDriver(snsEventsDriver);\\nregisterEventsDriver(sqsEventsDriver);',\n\t},\n\trabbitmq: {\n\t\timports: `import { registerEventsDriver } from '@geekmidas/events';\\nimport { rabbitmqEventsDriver } from '@geekmidas/events/rabbitmq';`,\n\t\tsetup: 'registerEventsDriver(rabbitmqEventsDriver);',\n\t},\n};\n\n/**\n * The KMS cipher, for an entry whose keys are on AWS. `aes256gcm:` is built\n * into `Encryption`; only this one needs a driver, because only it needs an\n * SDK — and only an AWS build has `kms://` keys to open.\n */\nconst KMS: RuntimeDrivers = {\n\timports: `import { registerCipherDriver } from '@geekmidas/constructs/encryption';\\nimport { kmsCipherDriver } from '@geekmidas/constructs/encryption/kms';`,\n\tsetup: 'registerCipherDriver(kmsCipherDriver);',\n};\n\n/**\n * Whether an entry opens KMS keys: built for AWS, from a process that reaches\n * an `Encryption`. A server's keys are keyrings, and `gkm dev` and `gkm test`\n * derive theirs locally — none of them imports `@aws-sdk/client-kms`.\n */\nexport function opensKmsKeys(\n\tmanifest: Record<string, { kind: string }>,\n\ttarget: 'aws' | 'server',\n): boolean {\n\treturn (\n\t\ttarget === 'aws' &&\n\t\tObject.values(manifest).some((d) => d.kind === 'encryption')\n\t);\n}\n\n/**\n * Everything a generated entry should register: object storage, the cache,\n * the events broker and the KMS cipher.\n *\n * Merged here rather than threaded separately, because a generated file has one\n * import block and one setup block whatever fills them.\n */\nexport function driversFor(options: {\n\tappRoot: string;\n\t/**\n\t * The cache backends actually in play — normally one, and more than one only\n\t * where an app declares two caches that live in different places.\n\t *\n\t * Absent or empty means nothing declared a cache, so no cache driver is\n\t * registered at all.\n\t */\n\tcache?: CacheBackend | readonly CacheBackend[] | false;\n\t/**\n\t * The events brokers in play — see {@link eventsBackendsIn}. Absent or\n\t * empty means nothing declared a topic or a queue, so no events driver is\n\t * registered and `@geekmidas/events` is never resolved.\n\t */\n\tevents?: EventsBackend | readonly EventsBackend[] | false;\n\t/** Whether it opens KMS keys — see {@link opensKmsKeys}. */\n\tkms?: boolean;\n}): RuntimeDrivers {\n\tconst backends = listOf(options.cache);\n\tconst brokers = listOf(options.events);\n\n\tconst parts = [\n\t\tstorageDriversFor(options.appRoot),\n\t\t...[...new Set(backends)].map(\n\t\t\t(backend) => CACHE_DRIVERS[backend] ?? CACHE_DRIVERS[DEFAULT_CACHE.aws],\n\t\t),\n\t\t...[...new Set(brokers)].map((broker) => EVENTS_DRIVERS[broker]),\n\t\t...(options.kms ? [KMS] : []),\n\t].filter((part) => part.imports || part.setup);\n\n\tif (parts.length === 0) return NONE;\n\n\treturn {\n\t\timports: parts.map((part) => part.imports).join('\\n'),\n\t\tsetup: parts.map((part) => part.setup).join('\\n'),\n\t};\n}\n\nfunction listOf<T extends string>(\n\tvalue: T | readonly T[] | false | undefined,\n): readonly T[] {\n\tif (value === false || value === undefined) return [];\n\treturn typeof value === 'string' ? [value] : (value as readonly T[]);\n}\n\n/**\n * Which events brokers an app's entry has to speak: the target's, when the app\n * declares a `Topic` or a `Queue` — and none at all when it declares neither,\n * so a project without events never resolves `@geekmidas/events`.\n *\n * A worker on pg-boss counts too: a server schedules its crons through the\n * pg-boss broker, which the plan provisions for a declared worker beside a\n * declared database (`workerBroker` in `reconcile/plan.ts`) whether or not\n * anything publishes.\n */\nexport function eventsBackendsIn(\n\tmanifest: Record<string, { kind: string }>,\n\tconfigured: EventsBackend,\n): EventsBackend[] {\n\tconst kinds = new Set(Object.values(manifest).map((d) => d.kind));\n\tconst declared =\n\t\tkinds.has('topic') ||\n\t\tkinds.has('queue') ||\n\t\t(configured === 'pgboss' && kinds.has('worker') && kinds.has('database'));\n\n\treturn declared ? [configured] : [];\n}\n\n/**\n * Which cache backends an app's entry actually has to speak.\n *\n * The config answers this for a cache that named nowhere, and the *declaration*\n * answers it for one that named a database — `orders.cache('Sessions')` is a\n * statement about the application, so a deployment cannot move that cache and\n * the entry cannot register a driver for somewhere else.\n *\n * Reading only the config is how an entry ends up registering the Upstash\n * driver for a URL the target composed as `postgres://`, which fails at the\n * first request with `UnregisteredCacheScheme` and a stack that points at the\n * cache rather than at the config that disagreed.\n */\nexport function cacheBackendsIn(\n\tmanifest: Record<string, { kind: string; of?: string }>,\n\tconfigured: CacheBackend,\n): CacheBackend[] {\n\tconst caches = Object.values(manifest).filter((d) => d.kind === 'cache');\n\n\t// The stack's own Redis holds every cache, one that named a database too.\n\treturn [\n\t\t...new Set(\n\t\t\tcaches.map((c) =>\n\t\t\t\tc.of && !holdsEveryCache(configured) ? 'db' : configured,\n\t\t\t),\n\t\t),\n\t];\n}\n\n/**\n * The drivers an app's entry points should register.\n *\n * Keyed off the dependency rather than off a declared bucket, because the entry\n * is generated before discovery has run on the watcher's rebuild path — and an\n * app that installed `@geekmidas/storage` has already paid for the SDK it would\n * otherwise resolve lazily.\n *\n * \"Installed\" the way Node resolves it: the app's own `package.json`, or any\n * directory above it. A workspace that lists `@geekmidas/storage` once, at its\n * root, is the common case — and reading only the app's file registered no S3\n * driver there, so the first service to inject a bucket threw\n * `UnregisteredStorageScheme`. A queue whose consumer depended on one logged\n * that once and was never polled.\n */\nexport function storageDriversFor(appRoot: string): RuntimeDrivers {\n\treturn installedFrom(appRoot, '@geekmidas/storage') ? S3 : NONE;\n}\n\n/**\n * Whether `pkg` is a dependency of the app at `appRoot` the way Node resolves\n * it: listed in the app's own `package.json`, or in any directory above it.\n */\nexport function installedFrom(appRoot: string, pkg: string): boolean {\n\tfor (let dir = resolve(appRoot); ; dir = dirname(dir)) {\n\t\tif (lists(join(dir, 'package.json'), pkg)) return true;\n\t\tif (dirname(dir) === dir) return false;\n\t}\n}\n\nfunction lists(manifest: string, dependency: string): boolean {\n\tif (!existsSync(manifest)) return false;\n\n\ttry {\n\t\tconst pkg = JSON.parse(readFileSync(manifest, 'utf8')) as {\n\t\t\tdependencies?: Record<string, string>;\n\t\t\tdevDependencies?: Record<string, string>;\n\t\t};\n\n\t\treturn Boolean(\n\t\t\tpkg.dependencies?.[dependency] ?? pkg.devDependencies?.[dependency],\n\t\t);\n\t} catch {\n\t\t// An unreadable package.json is the build's problem, not this function's.\n\t\treturn false;\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA0BA,MAAM,KAAK;CACV,SAAS;CACT,OAAO;AACR;AAaA,MAAM,OAAuB;CAAE,SAAS;CAAI,OAAO;AAAG;;AAGtD,MAAM,QAAwB;CAC7B,SAAS;CACT,OACC;AACF;;;;;;;;;;;;;AAcA,MAAM,gBAAsD;CAC3D,SAAS;EACR,SAAS;EACT,OACC;CACF;CACA,aAAa;CAGb,OAAO;CACP,IAAI;EACH,SAAS;EACT,OAAO;CACR;AACD;;;;;;;;;;AAWA,MAAM,iBAAwD;CAC7D,QAAQ;EACP,SAAS;EACT,OAAO;CACR;CACA,KAAK;EACJ,SAAS;EACT,OACC;CACF;CACA,UAAU;EACT,SAAS;EACT,OAAO;CACR;AACD;;;;;;AAOA,MAAM,MAAsB;CAC3B,SAAS;CACT,OAAO;AACR;;;;;;AAOA,SAAgB,aACf,UACA,QACU;CACV,OACC,WAAW,SACX,OAAO,OAAO,QAAQ,CAAC,CAAC,MAAM,MAAM,EAAE,SAAS,YAAY;AAE7D;;;;;;;;AASA,SAAgB,WAAW,SAkBR;CAClB,MAAM,WAAW,OAAO,QAAQ,KAAK;CACrC,MAAM,UAAU,OAAO,QAAQ,MAAM;CAErC,MAAM,QAAQ;EACb,kBAAkB,QAAQ,OAAO;EACjC,GAAG,CAAC,GAAG,IAAI,IAAI,QAAQ,CAAC,CAAC,CAAC,KACxB,YAAY,cAAc,YAAY,cAAcA,4BAAAA,cAAc,IACpE;EACA,GAAG,CAAC,GAAG,IAAI,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,WAAW,eAAe,OAAO;EAC/D,GAAI,QAAQ,MAAM,CAAC,GAAG,IAAI,CAAC;CAC5B,CAAC,CAAC,QAAQ,SAAS,KAAK,WAAW,KAAK,KAAK;CAE7C,IAAI,MAAM,WAAW,GAAG,OAAO;CAE/B,OAAO;EACN,SAAS,MAAM,KAAK,SAAS,KAAK,OAAO,CAAC,CAAC,KAAK,IAAI;EACpD,OAAO,MAAM,KAAK,SAAS,KAAK,KAAK,CAAC,CAAC,KAAK,IAAI;CACjD;AACD;AAEA,SAAS,OACR,OACe;CACf,IAAI,UAAU,SAAS,UAAU,KAAA,GAAW,OAAO,CAAC;CACpD,OAAO,OAAO,UAAU,WAAW,CAAC,KAAK,IAAK;AAC/C;;;;;;;;;;;AAYA,SAAgB,iBACf,UACA,YACkB;CAClB,MAAM,QAAQ,IAAI,IAAI,OAAO,OAAO,QAAQ,CAAC,CAAC,KAAK,MAAM,EAAE,IAAI,CAAC;CAMhE,OAJC,MAAM,IAAI,OAAO,KACjB,MAAM,IAAI,OAAO,KAChB,eAAe,YAAY,MAAM,IAAI,QAAQ,KAAK,MAAM,IAAI,UAAU,IAEtD,CAAC,UAAU,IAAI,CAAC;AACnC;;;;;;;;;;;;;;AAeA,SAAgB,gBACf,UACA,YACiB;CACjB,MAAM,SAAS,OAAO,OAAO,QAAQ,CAAC,CAAC,QAAQ,MAAM,EAAE,SAAS,OAAO;CAGvE,OAAO,CACN,GAAG,IAAI,IACN,OAAO,KAAK,MACX,EAAE,MAAM,CAACC,4BAAAA,gBAAgB,UAAU,IAAI,OAAO,UAC/C,CACD,CACD;AACD;;;;;;;;;;;;;;;;AAiBA,SAAgB,kBAAkB,SAAiC;CAClE,OAAO,cAAc,SAAS,oBAAoB,IAAI,KAAK;AAC5D;;;;;AAMA,SAAgB,cAAc,SAAiB,KAAsB;CACpE,KAAK,IAAI,OAAA,GAAMC,UAAAA,QAAAA,CAAQ,OAAO,IAAK,OAAA,GAAMC,UAAAA,QAAAA,CAAQ,GAAG,GAAG;EACtD,IAAI,OAAA,GAAMC,UAAAA,KAAAA,CAAK,KAAK,cAAc,GAAG,GAAG,GAAG,OAAO;EAClD,KAAA,GAAID,UAAAA,QAAAA,CAAQ,GAAG,MAAM,KAAK,OAAO;CAClC;AACD;AAEA,SAAS,MAAM,UAAkB,YAA6B;CAC7D,IAAI,EAAA,GAACE,QAAAA,WAAAA,CAAW,QAAQ,GAAG,OAAO;CAElC,IAAI;EACH,MAAM,MAAM,KAAK,OAAA,GAAMC,QAAAA,aAAAA,CAAa,UAAU,MAAM,CAAC;EAKrD,OAAO,QACN,IAAI,eAAe,eAAe,IAAI,kBAAkB,WACzD;CACD,QAAQ;EAEP,OAAO;CACR;AACD"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { c as DEFAULT_CACHE, d as holdsEveryCache } from "./SubscriberGenerator-COAVPgM-.mjs";
|
|
2
2
|
import { dirname, join, resolve } from "node:path";
|
|
3
3
|
import { existsSync, readFileSync } from "node:fs";
|
|
4
4
|
//#region src/generators/drivers.ts
|
|
@@ -192,4 +192,4 @@ function lists(manifest, dependency) {
|
|
|
192
192
|
//#endregion
|
|
193
193
|
export { opensKmsKeys as a, installedFrom as i, driversFor as n, eventsBackendsIn as r, cacheBackendsIn as t };
|
|
194
194
|
|
|
195
|
-
//# sourceMappingURL=drivers-
|
|
195
|
+
//# sourceMappingURL=drivers-Chb5Akov.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drivers-BjGDYfnC.mjs","names":[],"sources":["../src/generators/drivers.ts"],"sourcesContent":["/**\n * Driver registration for generated entry points.\n *\n * A construct declares that it needs object storage and is handed one URL; the\n * scheme in that URL picks the driver that builds the client. Which drivers\n * exist is therefore the *entry point's* decision, not the construct's and not\n * the application's — an app that never talks to S3 should never resolve the AWS\n * SDK, and application code that registers a driver has named a provider in the\n * one layer this design keeps provider-free.\n *\n * So the generated entries register: `gkm dev`'s server for local development,\n * and each Lambda handler for its own target. Registration is an explicit call\n * rather than an import side effect, because a side-effecting module is exactly\n * what a bundler is entitled to drop.\n */\n\nimport { existsSync, readFileSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport {\n\ttype CacheBackend,\n\tDEFAULT_CACHE,\n\ttype EventsBackend,\n\tholdsEveryCache,\n} from '../types.js';\n\n/** The `s3://` driver, which serves MinIO locally and S3 deployed. */\nconst S3 = {\n\timports: `import { registerStorageDriver } from '@geekmidas/storage';\\nimport { s3Driver } from '@geekmidas/storage/aws';`,\n\tsetup: 'registerStorageDriver(s3Driver);',\n};\n\n/** What a generated entry needs in order to register the drivers it uses. */\nexport interface RuntimeDrivers {\n\t/** Import lines, or `''` when there is nothing to register. */\n\timports: string;\n\t/** The registration calls, or `''`. */\n\tsetup: string;\n}\n\n/** @deprecated The same shape, from when only storage had drivers. */\nexport type StorageDrivers = RuntimeDrivers;\n\nconst NONE: RuntimeDrivers = { imports: '', setup: '' };\n\n/** The Redis wire protocol's drivers: both schemes, plain and TLS. */\nconst REDIS: RuntimeDrivers = {\n\timports: `import { registerCacheDriver } from '@geekmidas/cache';\\nimport { redisCacheDriver, redissCacheDriver } from '@geekmidas/cache/redis';`,\n\tsetup:\n\t\t'registerCacheDriver(redisCacheDriver);\\nregisterCacheDriver(redissCacheDriver);',\n};\n\n/**\n * The cache driver for a backend — exactly one, never all three.\n *\n * Keyed off the *backend* rather than off a dependency, because unlike storage\n * the backend already says which driver is right: a project caching in Postgres\n * resolves `pg` and never `ioredis`, and one caching in Upstash resolves\n * neither. That is the whole reason the drivers live behind separate subpaths.\n *\n * Both HTTP schemes are registered for Upstash, because the local proxy speaks\n * `http://` and Upstash speaks `https://`, and an entry that registered one\n * would work in exactly one of the two places.\n */\nconst CACHE_DRIVERS: Record<CacheBackend, RuntimeDrivers> = {\n\tupstash: {\n\t\timports: `import { registerCacheDriver } from '@geekmidas/cache';\\nimport { upstashCacheDriver, upstashInsecureCacheDriver } from '@geekmidas/cache/upstash';`,\n\t\tsetup:\n\t\t\t'registerCacheDriver(upstashCacheDriver);\\nregisterCacheDriver(upstashInsecureCacheDriver);',\n\t},\n\telasticache: REDIS,\n\t// The stack's own Redis speaks the same wire protocol — and a managed\n\t// Redis set in its place may be `rediss://`.\n\tredis: REDIS,\n\tdb: {\n\t\timports: `import { registerCacheDriver } from '@geekmidas/cache';\\nimport { postgresCacheDriver } from '@geekmidas/cache/postgres';`,\n\t\tsetup: 'registerCacheDriver(postgresCacheDriver);',\n\t},\n};\n\n/**\n * The events drivers for a broker — only its own, so a bundle never resolves\n * another broker's client library.\n *\n * Keyed off the backend, as the cache is: the target already says which broker\n * carries the app's topics and queues. SNS registers SQS beside it, because on\n * AWS a topic is `sns://` and a queue is `sqs://`, and an entry registering one\n * would publish to topics and fail on the first queue.\n */\nconst EVENTS_DRIVERS: Record<EventsBackend, RuntimeDrivers> = {\n\tpgboss: {\n\t\timports: `import { registerEventsDriver } from '@geekmidas/events';\\nimport { pgbossEventsDriver } from '@geekmidas/events/pgboss';`,\n\t\tsetup: 'registerEventsDriver(pgbossEventsDriver);',\n\t},\n\tsns: {\n\t\timports: `import { registerEventsDriver } from '@geekmidas/events';\\nimport { snsEventsDriver } from '@geekmidas/events/sns';\\nimport { sqsEventsDriver } from '@geekmidas/events/sqs';`,\n\t\tsetup:\n\t\t\t'registerEventsDriver(snsEventsDriver);\\nregisterEventsDriver(sqsEventsDriver);',\n\t},\n\trabbitmq: {\n\t\timports: `import { registerEventsDriver } from '@geekmidas/events';\\nimport { rabbitmqEventsDriver } from '@geekmidas/events/rabbitmq';`,\n\t\tsetup: 'registerEventsDriver(rabbitmqEventsDriver);',\n\t},\n};\n\n/**\n * The KMS cipher, for an entry whose keys are on AWS. `aes256gcm:` is built\n * into `Encryption`; only this one needs a driver, because only it needs an\n * SDK — and only an AWS build has `kms://` keys to open.\n */\nconst KMS: RuntimeDrivers = {\n\timports: `import { registerCipherDriver } from '@geekmidas/constructs/encryption';\\nimport { kmsCipherDriver } from '@geekmidas/constructs/encryption/kms';`,\n\tsetup: 'registerCipherDriver(kmsCipherDriver);',\n};\n\n/**\n * Whether an entry opens KMS keys: built for AWS, from a process that reaches\n * an `Encryption`. A server's keys are keyrings, and `gkm dev` and `gkm test`\n * derive theirs locally — none of them imports `@aws-sdk/client-kms`.\n */\nexport function opensKmsKeys(\n\tmanifest: Record<string, { kind: string }>,\n\ttarget: 'aws' | 'server',\n): boolean {\n\treturn (\n\t\ttarget === 'aws' &&\n\t\tObject.values(manifest).some((d) => d.kind === 'encryption')\n\t);\n}\n\n/**\n * Everything a generated entry should register: object storage, the cache,\n * the events broker and the KMS cipher.\n *\n * Merged here rather than threaded separately, because a generated file has one\n * import block and one setup block whatever fills them.\n */\nexport function driversFor(options: {\n\tappRoot: string;\n\t/**\n\t * The cache backends actually in play — normally one, and more than one only\n\t * where an app declares two caches that live in different places.\n\t *\n\t * Absent or empty means nothing declared a cache, so no cache driver is\n\t * registered at all.\n\t */\n\tcache?: CacheBackend | readonly CacheBackend[] | false;\n\t/**\n\t * The events brokers in play — see {@link eventsBackendsIn}. Absent or\n\t * empty means nothing declared a topic or a queue, so no events driver is\n\t * registered and `@geekmidas/events` is never resolved.\n\t */\n\tevents?: EventsBackend | readonly EventsBackend[] | false;\n\t/** Whether it opens KMS keys — see {@link opensKmsKeys}. */\n\tkms?: boolean;\n}): RuntimeDrivers {\n\tconst backends = listOf(options.cache);\n\tconst brokers = listOf(options.events);\n\n\tconst parts = [\n\t\tstorageDriversFor(options.appRoot),\n\t\t...[...new Set(backends)].map(\n\t\t\t(backend) => CACHE_DRIVERS[backend] ?? CACHE_DRIVERS[DEFAULT_CACHE.aws],\n\t\t),\n\t\t...[...new Set(brokers)].map((broker) => EVENTS_DRIVERS[broker]),\n\t\t...(options.kms ? [KMS] : []),\n\t].filter((part) => part.imports || part.setup);\n\n\tif (parts.length === 0) return NONE;\n\n\treturn {\n\t\timports: parts.map((part) => part.imports).join('\\n'),\n\t\tsetup: parts.map((part) => part.setup).join('\\n'),\n\t};\n}\n\nfunction listOf<T extends string>(\n\tvalue: T | readonly T[] | false | undefined,\n): readonly T[] {\n\tif (value === false || value === undefined) return [];\n\treturn typeof value === 'string' ? [value] : (value as readonly T[]);\n}\n\n/**\n * Which events brokers an app's entry has to speak: the target's, when the app\n * declares a `Topic` or a `Queue` — and none at all when it declares neither,\n * so a project without events never resolves `@geekmidas/events`.\n *\n * A worker on pg-boss counts too: a server schedules its crons through the\n * pg-boss broker, which the plan provisions for a declared worker beside a\n * declared database (`workerBroker` in `reconcile/plan.ts`) whether or not\n * anything publishes.\n */\nexport function eventsBackendsIn(\n\tmanifest: Record<string, { kind: string }>,\n\tconfigured: EventsBackend,\n): EventsBackend[] {\n\tconst kinds = new Set(Object.values(manifest).map((d) => d.kind));\n\tconst declared =\n\t\tkinds.has('topic') ||\n\t\tkinds.has('queue') ||\n\t\t(configured === 'pgboss' && kinds.has('worker') && kinds.has('database'));\n\n\treturn declared ? [configured] : [];\n}\n\n/**\n * Which cache backends an app's entry actually has to speak.\n *\n * The config answers this for a cache that named nowhere, and the *declaration*\n * answers it for one that named a database — `orders.cache('Sessions')` is a\n * statement about the application, so a deployment cannot move that cache and\n * the entry cannot register a driver for somewhere else.\n *\n * Reading only the config is how an entry ends up registering the Upstash\n * driver for a URL the target composed as `postgres://`, which fails at the\n * first request with `UnregisteredCacheScheme` and a stack that points at the\n * cache rather than at the config that disagreed.\n */\nexport function cacheBackendsIn(\n\tmanifest: Record<string, { kind: string; of?: string }>,\n\tconfigured: CacheBackend,\n): CacheBackend[] {\n\tconst caches = Object.values(manifest).filter((d) => d.kind === 'cache');\n\n\t// The stack's own Redis holds every cache, one that named a database too.\n\treturn [\n\t\t...new Set(\n\t\t\tcaches.map((c) =>\n\t\t\t\tc.of && !holdsEveryCache(configured) ? 'db' : configured,\n\t\t\t),\n\t\t),\n\t];\n}\n\n/**\n * The drivers an app's entry points should register.\n *\n * Keyed off the dependency rather than off a declared bucket, because the entry\n * is generated before discovery has run on the watcher's rebuild path — and an\n * app that installed `@geekmidas/storage` has already paid for the SDK it would\n * otherwise resolve lazily.\n *\n * \"Installed\" the way Node resolves it: the app's own `package.json`, or any\n * directory above it. A workspace that lists `@geekmidas/storage` once, at its\n * root, is the common case — and reading only the app's file registered no S3\n * driver there, so the first service to inject a bucket threw\n * `UnregisteredStorageScheme`. A queue whose consumer depended on one logged\n * that once and was never polled.\n */\nexport function storageDriversFor(appRoot: string): RuntimeDrivers {\n\treturn installedFrom(appRoot, '@geekmidas/storage') ? S3 : NONE;\n}\n\n/**\n * Whether `pkg` is a dependency of the app at `appRoot` the way Node resolves\n * it: listed in the app's own `package.json`, or in any directory above it.\n */\nexport function installedFrom(appRoot: string, pkg: string): boolean {\n\tfor (let dir = resolve(appRoot); ; dir = dirname(dir)) {\n\t\tif (lists(join(dir, 'package.json'), pkg)) return true;\n\t\tif (dirname(dir) === dir) return false;\n\t}\n}\n\nfunction lists(manifest: string, dependency: string): boolean {\n\tif (!existsSync(manifest)) return false;\n\n\ttry {\n\t\tconst pkg = JSON.parse(readFileSync(manifest, 'utf8')) as {\n\t\t\tdependencies?: Record<string, string>;\n\t\t\tdevDependencies?: Record<string, string>;\n\t\t};\n\n\t\treturn Boolean(\n\t\t\tpkg.dependencies?.[dependency] ?? pkg.devDependencies?.[dependency],\n\t\t);\n\t} catch {\n\t\t// An unreadable package.json is the build's problem, not this function's.\n\t\treturn false;\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"drivers-Chb5Akov.mjs","names":[],"sources":["../src/generators/drivers.ts"],"sourcesContent":["/**\n * Driver registration for generated entry points.\n *\n * A construct declares that it needs object storage and is handed one URL; the\n * scheme in that URL picks the driver that builds the client. Which drivers\n * exist is therefore the *entry point's* decision, not the construct's and not\n * the application's — an app that never talks to S3 should never resolve the AWS\n * SDK, and application code that registers a driver has named a provider in the\n * one layer this design keeps provider-free.\n *\n * So the generated entries register: `gkm dev`'s server for local development,\n * and each Lambda handler for its own target. Registration is an explicit call\n * rather than an import side effect, because a side-effecting module is exactly\n * what a bundler is entitled to drop.\n */\n\nimport { existsSync, readFileSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport {\n\ttype CacheBackend,\n\tDEFAULT_CACHE,\n\ttype EventsBackend,\n\tholdsEveryCache,\n} from '../types.js';\n\n/** The `s3://` driver, which serves MinIO locally and S3 deployed. */\nconst S3 = {\n\timports: `import { registerStorageDriver } from '@geekmidas/storage';\\nimport { s3Driver } from '@geekmidas/storage/aws';`,\n\tsetup: 'registerStorageDriver(s3Driver);',\n};\n\n/** What a generated entry needs in order to register the drivers it uses. */\nexport interface RuntimeDrivers {\n\t/** Import lines, or `''` when there is nothing to register. */\n\timports: string;\n\t/** The registration calls, or `''`. */\n\tsetup: string;\n}\n\n/** @deprecated The same shape, from when only storage had drivers. */\nexport type StorageDrivers = RuntimeDrivers;\n\nconst NONE: RuntimeDrivers = { imports: '', setup: '' };\n\n/** The Redis wire protocol's drivers: both schemes, plain and TLS. */\nconst REDIS: RuntimeDrivers = {\n\timports: `import { registerCacheDriver } from '@geekmidas/cache';\\nimport { redisCacheDriver, redissCacheDriver } from '@geekmidas/cache/redis';`,\n\tsetup:\n\t\t'registerCacheDriver(redisCacheDriver);\\nregisterCacheDriver(redissCacheDriver);',\n};\n\n/**\n * The cache driver for a backend — exactly one, never all three.\n *\n * Keyed off the *backend* rather than off a dependency, because unlike storage\n * the backend already says which driver is right: a project caching in Postgres\n * resolves `pg` and never `ioredis`, and one caching in Upstash resolves\n * neither. That is the whole reason the drivers live behind separate subpaths.\n *\n * Both HTTP schemes are registered for Upstash, because the local proxy speaks\n * `http://` and Upstash speaks `https://`, and an entry that registered one\n * would work in exactly one of the two places.\n */\nconst CACHE_DRIVERS: Record<CacheBackend, RuntimeDrivers> = {\n\tupstash: {\n\t\timports: `import { registerCacheDriver } from '@geekmidas/cache';\\nimport { upstashCacheDriver, upstashInsecureCacheDriver } from '@geekmidas/cache/upstash';`,\n\t\tsetup:\n\t\t\t'registerCacheDriver(upstashCacheDriver);\\nregisterCacheDriver(upstashInsecureCacheDriver);',\n\t},\n\telasticache: REDIS,\n\t// The stack's own Redis speaks the same wire protocol — and a managed\n\t// Redis set in its place may be `rediss://`.\n\tredis: REDIS,\n\tdb: {\n\t\timports: `import { registerCacheDriver } from '@geekmidas/cache';\\nimport { postgresCacheDriver } from '@geekmidas/cache/postgres';`,\n\t\tsetup: 'registerCacheDriver(postgresCacheDriver);',\n\t},\n};\n\n/**\n * The events drivers for a broker — only its own, so a bundle never resolves\n * another broker's client library.\n *\n * Keyed off the backend, as the cache is: the target already says which broker\n * carries the app's topics and queues. SNS registers SQS beside it, because on\n * AWS a topic is `sns://` and a queue is `sqs://`, and an entry registering one\n * would publish to topics and fail on the first queue.\n */\nconst EVENTS_DRIVERS: Record<EventsBackend, RuntimeDrivers> = {\n\tpgboss: {\n\t\timports: `import { registerEventsDriver } from '@geekmidas/events';\\nimport { pgbossEventsDriver } from '@geekmidas/events/pgboss';`,\n\t\tsetup: 'registerEventsDriver(pgbossEventsDriver);',\n\t},\n\tsns: {\n\t\timports: `import { registerEventsDriver } from '@geekmidas/events';\\nimport { snsEventsDriver } from '@geekmidas/events/sns';\\nimport { sqsEventsDriver } from '@geekmidas/events/sqs';`,\n\t\tsetup:\n\t\t\t'registerEventsDriver(snsEventsDriver);\\nregisterEventsDriver(sqsEventsDriver);',\n\t},\n\trabbitmq: {\n\t\timports: `import { registerEventsDriver } from '@geekmidas/events';\\nimport { rabbitmqEventsDriver } from '@geekmidas/events/rabbitmq';`,\n\t\tsetup: 'registerEventsDriver(rabbitmqEventsDriver);',\n\t},\n};\n\n/**\n * The KMS cipher, for an entry whose keys are on AWS. `aes256gcm:` is built\n * into `Encryption`; only this one needs a driver, because only it needs an\n * SDK — and only an AWS build has `kms://` keys to open.\n */\nconst KMS: RuntimeDrivers = {\n\timports: `import { registerCipherDriver } from '@geekmidas/constructs/encryption';\\nimport { kmsCipherDriver } from '@geekmidas/constructs/encryption/kms';`,\n\tsetup: 'registerCipherDriver(kmsCipherDriver);',\n};\n\n/**\n * Whether an entry opens KMS keys: built for AWS, from a process that reaches\n * an `Encryption`. A server's keys are keyrings, and `gkm dev` and `gkm test`\n * derive theirs locally — none of them imports `@aws-sdk/client-kms`.\n */\nexport function opensKmsKeys(\n\tmanifest: Record<string, { kind: string }>,\n\ttarget: 'aws' | 'server',\n): boolean {\n\treturn (\n\t\ttarget === 'aws' &&\n\t\tObject.values(manifest).some((d) => d.kind === 'encryption')\n\t);\n}\n\n/**\n * Everything a generated entry should register: object storage, the cache,\n * the events broker and the KMS cipher.\n *\n * Merged here rather than threaded separately, because a generated file has one\n * import block and one setup block whatever fills them.\n */\nexport function driversFor(options: {\n\tappRoot: string;\n\t/**\n\t * The cache backends actually in play — normally one, and more than one only\n\t * where an app declares two caches that live in different places.\n\t *\n\t * Absent or empty means nothing declared a cache, so no cache driver is\n\t * registered at all.\n\t */\n\tcache?: CacheBackend | readonly CacheBackend[] | false;\n\t/**\n\t * The events brokers in play — see {@link eventsBackendsIn}. Absent or\n\t * empty means nothing declared a topic or a queue, so no events driver is\n\t * registered and `@geekmidas/events` is never resolved.\n\t */\n\tevents?: EventsBackend | readonly EventsBackend[] | false;\n\t/** Whether it opens KMS keys — see {@link opensKmsKeys}. */\n\tkms?: boolean;\n}): RuntimeDrivers {\n\tconst backends = listOf(options.cache);\n\tconst brokers = listOf(options.events);\n\n\tconst parts = [\n\t\tstorageDriversFor(options.appRoot),\n\t\t...[...new Set(backends)].map(\n\t\t\t(backend) => CACHE_DRIVERS[backend] ?? CACHE_DRIVERS[DEFAULT_CACHE.aws],\n\t\t),\n\t\t...[...new Set(brokers)].map((broker) => EVENTS_DRIVERS[broker]),\n\t\t...(options.kms ? [KMS] : []),\n\t].filter((part) => part.imports || part.setup);\n\n\tif (parts.length === 0) return NONE;\n\n\treturn {\n\t\timports: parts.map((part) => part.imports).join('\\n'),\n\t\tsetup: parts.map((part) => part.setup).join('\\n'),\n\t};\n}\n\nfunction listOf<T extends string>(\n\tvalue: T | readonly T[] | false | undefined,\n): readonly T[] {\n\tif (value === false || value === undefined) return [];\n\treturn typeof value === 'string' ? [value] : (value as readonly T[]);\n}\n\n/**\n * Which events brokers an app's entry has to speak: the target's, when the app\n * declares a `Topic` or a `Queue` — and none at all when it declares neither,\n * so a project without events never resolves `@geekmidas/events`.\n *\n * A worker on pg-boss counts too: a server schedules its crons through the\n * pg-boss broker, which the plan provisions for a declared worker beside a\n * declared database (`workerBroker` in `reconcile/plan.ts`) whether or not\n * anything publishes.\n */\nexport function eventsBackendsIn(\n\tmanifest: Record<string, { kind: string }>,\n\tconfigured: EventsBackend,\n): EventsBackend[] {\n\tconst kinds = new Set(Object.values(manifest).map((d) => d.kind));\n\tconst declared =\n\t\tkinds.has('topic') ||\n\t\tkinds.has('queue') ||\n\t\t(configured === 'pgboss' && kinds.has('worker') && kinds.has('database'));\n\n\treturn declared ? [configured] : [];\n}\n\n/**\n * Which cache backends an app's entry actually has to speak.\n *\n * The config answers this for a cache that named nowhere, and the *declaration*\n * answers it for one that named a database — `orders.cache('Sessions')` is a\n * statement about the application, so a deployment cannot move that cache and\n * the entry cannot register a driver for somewhere else.\n *\n * Reading only the config is how an entry ends up registering the Upstash\n * driver for a URL the target composed as `postgres://`, which fails at the\n * first request with `UnregisteredCacheScheme` and a stack that points at the\n * cache rather than at the config that disagreed.\n */\nexport function cacheBackendsIn(\n\tmanifest: Record<string, { kind: string; of?: string }>,\n\tconfigured: CacheBackend,\n): CacheBackend[] {\n\tconst caches = Object.values(manifest).filter((d) => d.kind === 'cache');\n\n\t// The stack's own Redis holds every cache, one that named a database too.\n\treturn [\n\t\t...new Set(\n\t\t\tcaches.map((c) =>\n\t\t\t\tc.of && !holdsEveryCache(configured) ? 'db' : configured,\n\t\t\t),\n\t\t),\n\t];\n}\n\n/**\n * The drivers an app's entry points should register.\n *\n * Keyed off the dependency rather than off a declared bucket, because the entry\n * is generated before discovery has run on the watcher's rebuild path — and an\n * app that installed `@geekmidas/storage` has already paid for the SDK it would\n * otherwise resolve lazily.\n *\n * \"Installed\" the way Node resolves it: the app's own `package.json`, or any\n * directory above it. A workspace that lists `@geekmidas/storage` once, at its\n * root, is the common case — and reading only the app's file registered no S3\n * driver there, so the first service to inject a bucket threw\n * `UnregisteredStorageScheme`. A queue whose consumer depended on one logged\n * that once and was never polled.\n */\nexport function storageDriversFor(appRoot: string): RuntimeDrivers {\n\treturn installedFrom(appRoot, '@geekmidas/storage') ? S3 : NONE;\n}\n\n/**\n * Whether `pkg` is a dependency of the app at `appRoot` the way Node resolves\n * it: listed in the app's own `package.json`, or in any directory above it.\n */\nexport function installedFrom(appRoot: string, pkg: string): boolean {\n\tfor (let dir = resolve(appRoot); ; dir = dirname(dir)) {\n\t\tif (lists(join(dir, 'package.json'), pkg)) return true;\n\t\tif (dirname(dir) === dir) return false;\n\t}\n}\n\nfunction lists(manifest: string, dependency: string): boolean {\n\tif (!existsSync(manifest)) return false;\n\n\ttry {\n\t\tconst pkg = JSON.parse(readFileSync(manifest, 'utf8')) as {\n\t\t\tdependencies?: Record<string, string>;\n\t\t\tdevDependencies?: Record<string, string>;\n\t\t};\n\n\t\treturn Boolean(\n\t\t\tpkg.dependencies?.[dependency] ?? pkg.devDependencies?.[dependency],\n\t\t);\n\t} catch {\n\t\t// An unreadable package.json is the build's problem, not this function's.\n\t\treturn false;\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as GkmError } from "./errors-D8Gwp5kU.mjs";
|
|
2
2
|
import { t as appKey } from "./derive-B4vDpzgP.mjs";
|
|
3
3
|
import { i as easWorkflowPath, n as mobileStage, r as easWorkflow } from "./index.mjs";
|
|
4
|
-
import { t as loadStage } from "./stage-
|
|
4
|
+
import { t as loadStage } from "./stage-2EKpzPQ1.mjs";
|
|
5
5
|
import { delimiter, dirname, join } from "node:path";
|
|
6
6
|
import { existsSync } from "node:fs";
|
|
7
7
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
@@ -331,4 +331,4 @@ async function easSync(options, eas) {
|
|
|
331
331
|
//#endregion
|
|
332
332
|
export { chooseMobileApp, easCli, easSync, withStageProfiles };
|
|
333
333
|
|
|
334
|
-
//# sourceMappingURL=eas-
|
|
334
|
+
//# sourceMappingURL=eas-1Ee6kql0.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eas-DCg41jKK.mjs","names":[],"sources":["../src/mobile/eas.ts"],"sourcesContent":["/**\n * `gkm eas:sync --stage <stage>`: a mobile app's EAS project set up for a\n * deployed stage, from what the workspace declares — so a store build, which\n * EAS runs on its own machines, outside gkm, is built with the same values\n * the stage's servers were deployed with.\n *\n * - **`eas.json`**: the stage's build profile (`build.<stage>`) reads its EAS\n * environment and listens on its channel; its submit profile\n * (`submit.<stage>.android`) has its Play track and release status — see\n * `mobileStage`. Every other field is left as it is.\n * - **The EAS environment**: each value the app's build reads for the stage\n * (`StackMobileApp.values`) — its scheme, its edges' public URLs under\n * `EXPO_PUBLIC_`, and every stage secret already named so — set with\n * `eas env:set`, as `plaintext`: a bundle inlines them, so none is secret.\n * Nothing else is written: no server key reaches EAS.\n */\n\nimport { spawn } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { delimiter, dirname, join } from 'node:path';\nimport type {\n\tConstructManifest,\n\tMobileAppDeclaration,\n} from '@geekmidas/manifest';\nimport { GkmError } from '../errors';\nimport { loadStage } from '../exec/stage.js';\nimport { appKey } from '../workspace/derive.js';\nimport { easWorkflow, easWorkflowPath } from './easWorkflow.js';\nimport { type MobileStage, mobileStage } from './stage.js';\n\nconst logger = console;\n\n/** The workspace declares no `MobileApp`: nothing to set up. */\nexport class NoMobileApp extends GkmError {\n\tconstructor() {\n\t\tsuper(\n\t\t\t'The workspace declares no MobileApp, so there is no EAS project to set ' +\n\t\t\t\t\"up. Declare one: new MobileApp('App', { path: 'apps/app' }).dependsOn([api, auth]).\",\n\t\t);\n\t\tthis.name = 'NoMobileApp';\n\t}\n}\n\n/** Several `MobileApp`s, and nothing to say which. */\nexport class MobileAppNotChosen extends GkmError {\n\tconstructor(readonly apps: readonly string[]) {\n\t\tsuper(\n\t\t\t`The workspace declares ${apps.length} mobile apps (${apps.join(', ')}). ` +\n\t\t\t\t`Run from one's directory, or name it: --app ${apps[0]}.`,\n\t\t);\n\t\tthis.name = 'MobileAppNotChosen';\n\t}\n}\n\n/** `--app` names no `MobileApp`. */\nexport class MobileAppUnknown extends GkmError {\n\tconstructor(\n\t\treadonly app: string,\n\t\treadonly apps: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`'${app}' is not one of the workspace's mobile apps (${apps.join(', ')}).`,\n\t\t);\n\t\tthis.name = 'MobileAppUnknown';\n\t}\n}\n\n/** `eas` is not installed where the app is. */\nexport class EasCliMissing extends GkmError {\n\tconstructor(readonly dir: string) {\n\t\tsuper(\n\t\t\t`No \\`eas\\` to run in ${dir}. Add it to the app: pnpm add -D eas-cli ` +\n\t\t\t\t'(or install it globally: npm install -g eas-cli).',\n\t\t);\n\t\tthis.name = 'EasCliMissing';\n\t}\n}\n\n/** `eas` refused a command. */\nexport class EasCommandFailed extends GkmError {\n\tconstructor(\n\t\treadonly command: string,\n\t\treadonly code: number | null,\n\t\treadonly stderr: string,\n\t) {\n\t\tsuper(\n\t\t\t`\\`eas ${command}\\` failed (exit ${code}): ${stderr.trim() || 'it said nothing'}. ` +\n\t\t\t\t'In CI, EXPO_TOKEN must be set; on this machine, `eas login`.',\n\t\t);\n\t\tthis.name = 'EasCommandFailed';\n\t}\n}\n\n/** A platform EAS builds for. */\nexport type EasPlatform = 'ios' | 'android';\n\n/** What gkm asks of EAS, so tests can stand in. */\nexport interface EasCli {\n\t/** Create or update a variable in one of the project's environments. */\n\tsetEnv(\n\t\tdir: string,\n\t\tvariable: { environment: string; name: string; value: string },\n\t): Promise<void>;\n\t/**\n\t * The fingerprint of the app's native code, as a build with `profile`\n\t * would compute it — what its runtime version is.\n\t */\n\tfingerprint(\n\t\tdir: string,\n\t\toptions: { platform: EasPlatform; profile: string },\n\t): Promise<string>;\n\t/** A finished store build of `profile` with that fingerprint, if any. */\n\tstoreBuild(\n\t\tdir: string,\n\t\toptions: { platform: EasPlatform; profile: string; fingerprint: string },\n\t): Promise<string | undefined>;\n\t/** Start a store build of `profile`, submitted to the store when asked. */\n\tbuild(\n\t\tdir: string,\n\t\toptions: {\n\t\t\tplatform: EasPlatform;\n\t\t\tprofile: string;\n\t\t\tsubmit: boolean;\n\t\t\twait: boolean;\n\t\t},\n\t): Promise<void>;\n\t/** Publish an update of the app's JavaScript on a channel. */\n\tupdate(\n\t\tdir: string,\n\t\toptions: {\n\t\t\tplatform: EasPlatform;\n\t\t\tchannel: string;\n\t\t\tenvironment: string;\n\t\t\tmessage: string;\n\t\t},\n\t): Promise<void>;\n}\n\n/**\n * Run `eas` in `dir`: the app's own, else the workspace's, else the one on\n * PATH. What it prints on stdout is answered; a failure is\n * {@link EasCommandFailed} with what it said.\n */\nfunction runEas(\n\troot: string,\n\tdir: string,\n\targs: readonly string[],\n\toptions: { inherit?: boolean; env?: Readonly<Record<string, string>> } = {},\n): Promise<string> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst child = spawn('eas', [...args, '--non-interactive'], {\n\t\t\tcwd: dir,\n\t\t\t// A build's or an update's progress is the user's to read.\n\t\t\tstdio: ['ignore', options.inherit ? 'inherit' : 'pipe', 'pipe'],\n\t\t\tenv: {\n\t\t\t\t...process.env,\n\t\t\t\t...options.env,\n\t\t\t\tPATH: [\n\t\t\t\t\tjoin(dir, 'node_modules', '.bin'),\n\t\t\t\t\tjoin(root, 'node_modules', '.bin'),\n\t\t\t\t\tprocess.env.PATH,\n\t\t\t\t].join(delimiter),\n\t\t\t},\n\t\t});\n\t\tlet stdout = '';\n\t\tlet stderr = '';\n\t\tchild.stdout?.on('data', (chunk: Buffer) => {\n\t\t\tstdout += chunk.toString();\n\t\t});\n\t\tchild.stderr?.on('data', (chunk: Buffer) => {\n\t\t\tstderr += chunk.toString();\n\t\t\tif (options.inherit) process.stderr.write(chunk);\n\t\t});\n\t\tconst command = args.slice(0, 3).join(' ');\n\t\tchild.on('error', (error: NodeJS.ErrnoException) =>\n\t\t\treject(\n\t\t\t\terror.code === 'ENOENT'\n\t\t\t\t\t? new EasCliMissing(dir)\n\t\t\t\t\t: new EasCommandFailed(command, null, error.message),\n\t\t\t),\n\t\t);\n\t\tchild.on('close', (code) =>\n\t\t\tcode === 0\n\t\t\t\t? resolve(stdout)\n\t\t\t\t: reject(new EasCommandFailed(command, code, stderr)),\n\t\t);\n\t});\n}\n\n/** The JSON `eas … --json` printed, read for `what`. */\nfunction easJson(output: string, what: string): unknown {\n\ttry {\n\t\treturn JSON.parse(output);\n\t} catch {\n\t\tthrow new EasCommandFailed(what, 0, `not JSON: ${output.slice(0, 200)}`);\n\t}\n}\n\n/**\n * The real one — never a prompt (`--non-interactive`). `env` is what the\n * app's `app.config.ts` reads for the stage: every `eas` command evaluates\n * it, and one that reads a value it lacks fails before it starts — and a\n * fingerprint computed here must match the one EAS computes with the\n * stage's values.\n */\nexport function easCli(\n\troot: string,\n\tenv: Readonly<Record<string, string>> = {},\n): EasCli {\n\tconst run = (\n\t\tdir: string,\n\t\targs: readonly string[],\n\t\toptions: { inherit?: boolean } = {},\n\t) => runEas(root, dir, args, { ...options, env });\n\treturn {\n\t\tasync setEnv(dir, { environment, name, value }) {\n\t\t\tawait run(dir, [\n\t\t\t\t'env:set',\n\t\t\t\t'--environment',\n\t\t\t\tenvironment,\n\t\t\t\t'--name',\n\t\t\t\tname,\n\t\t\t\t'--value',\n\t\t\t\tvalue,\n\t\t\t\t'--visibility',\n\t\t\t\t'plaintext',\n\t\t\t\t'--scope',\n\t\t\t\t'project',\n\t\t\t]);\n\t\t},\n\n\t\tasync fingerprint(dir, { platform, profile }) {\n\t\t\tconst output = await run(dir, [\n\t\t\t\t'fingerprint:generate',\n\t\t\t\t'--platform',\n\t\t\t\tplatform,\n\t\t\t\t'--build-profile',\n\t\t\t\tprofile,\n\t\t\t\t'--json',\n\t\t\t]);\n\t\t\tconst hash = (\n\t\t\t\teasJson(output, 'fingerprint:generate') as { hash?: unknown }\n\t\t\t).hash;\n\t\t\tif (typeof hash !== 'string') {\n\t\t\t\tthrow new EasCommandFailed('fingerprint:generate', 0, 'no hash');\n\t\t\t}\n\t\t\treturn hash;\n\t\t},\n\n\t\tasync storeBuild(dir, { platform, profile, fingerprint }) {\n\t\t\tconst output = await run(dir, [\n\t\t\t\t'build:list',\n\t\t\t\t'--platform',\n\t\t\t\tplatform,\n\t\t\t\t'--build-profile',\n\t\t\t\tprofile,\n\t\t\t\t'--fingerprint-hash',\n\t\t\t\tfingerprint,\n\t\t\t\t'--status',\n\t\t\t\t'finished',\n\t\t\t\t'--distribution',\n\t\t\t\t'store',\n\t\t\t\t'--limit',\n\t\t\t\t'1',\n\t\t\t\t'--json',\n\t\t\t]);\n\t\t\tconst builds = easJson(output, 'build:list');\n\t\t\tif (!Array.isArray(builds)) return undefined;\n\t\t\tconst id = (builds[0] as { id?: unknown } | undefined)?.id;\n\t\t\treturn typeof id === 'string' ? id : undefined;\n\t\t},\n\n\t\tasync build(dir, { platform, profile, submit, wait }) {\n\t\t\tawait run(\n\t\t\t\tdir,\n\t\t\t\t[\n\t\t\t\t\t'build',\n\t\t\t\t\t'--platform',\n\t\t\t\t\tplatform,\n\t\t\t\t\t'--profile',\n\t\t\t\t\tprofile,\n\t\t\t\t\t...(submit ? ['--auto-submit'] : []),\n\t\t\t\t\t...(wait ? [] : ['--no-wait']),\n\t\t\t\t],\n\t\t\t\t{ inherit: true },\n\t\t\t);\n\t\t},\n\n\t\tasync update(dir, { platform, channel, environment, message }) {\n\t\t\tawait run(\n\t\t\t\tdir,\n\t\t\t\t[\n\t\t\t\t\t'update',\n\t\t\t\t\t'--platform',\n\t\t\t\t\tplatform,\n\t\t\t\t\t'--channel',\n\t\t\t\t\tchannel,\n\t\t\t\t\t'--environment',\n\t\t\t\t\tenvironment,\n\t\t\t\t\t'--message',\n\t\t\t\t\tmessage,\n\t\t\t\t],\n\t\t\t\t{ inherit: true },\n\t\t\t);\n\t\t},\n\t};\n}\n\n/**\n * The mobile app a command is about: the one `--app` names, else the one\n * this directory is, else the only one.\n *\n * @throws {NoMobileApp}, {MobileAppNotChosen}, {MobileAppUnknown}\n */\nexport function chooseMobileApp(\n\tmanifest: ConstructManifest,\n\tapp: string | undefined,\n\there: string | undefined,\n): MobileAppDeclaration {\n\tconst declarations = Object.values(manifest).filter(\n\t\t(declaration): declaration is MobileAppDeclaration =>\n\t\t\tdeclaration.kind === 'mobile-app',\n\t);\n\tconst names = declarations.map((declaration) => appKey(declaration.id));\n\tif (declarations.length === 0) throw new NoMobileApp();\n\tconst name =\n\t\tapp ??\n\t\t(here && names.includes(here)\n\t\t\t? here\n\t\t\t: names.length === 1\n\t\t\t\t? names[0]\n\t\t\t\t: undefined);\n\tif (!name) throw new MobileAppNotChosen(names);\n\tconst declaration = declarations.find((d) => appKey(d.id) === name);\n\tif (!declaration) throw new MobileAppUnknown(name, names);\n\treturn declaration;\n}\n\n/** `eas.json`, as far as gkm writes it. */\ninterface EasJson {\n\tbuild?: Record<string, Record<string, unknown>>;\n\tsubmit?: Record<string, Record<string, unknown>>;\n\t[key: string]: unknown;\n}\n\n/**\n * `eas.json` with the stage's build and submit profiles as `stage` says:\n * every field gkm does not decide is kept.\n */\nexport function withStageProfiles(\n\teasJson: EasJson,\n\tstage: MobileStage,\n): EasJson {\n\tconst build = easJson.build?.[stage.stage] ?? {};\n\tconst submit = easJson.submit?.[stage.stage] ?? {};\n\tconst android = (submit.android as Record<string, unknown> | undefined) ?? {};\n\treturn {\n\t\t...easJson,\n\t\tbuild: {\n\t\t\t...easJson.build,\n\t\t\t[stage.stage]: {\n\t\t\t\t...build,\n\t\t\t\tenvironment: stage.environment,\n\t\t\t\tchannel: stage.channel,\n\t\t\t},\n\t\t},\n\t\tsubmit: {\n\t\t\t...easJson.submit,\n\t\t\t[stage.stage]: {\n\t\t\t\t...submit,\n\t\t\t\tandroid: {\n\t\t\t\t\t...android,\n\t\t\t\t\ttrack: stage.android.track,\n\t\t\t\t\treleaseStatus: stage.android.releaseStatus,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t};\n}\n\nexport interface EasSyncOptions {\n\tstage: string;\n\t/** The mobile app, by its app key; the one here, or the only one, if not. */\n\tapp?: string;\n\t/** The AWS profile for the stage's account, where its secrets are kept. */\n\tprofile?: string;\n\tcwd?: string;\n\t/** Say what would be written, and write nothing. */\n\tdryRun?: boolean;\n}\n\n/** What a sync wrote — or, dry, would. */\nexport interface EasSyncResult {\n\tapp: string;\n\t/** The workspace's root, where its `eas` may be installed. */\n\troot: string;\n\tdir: string;\n\tstage: MobileStage;\n\tvalues: Record<string, string>;\n\t/** Whether `eas.json` changed. */\n\teasJsonChanged: boolean;\n\t/** The stage's EAS workflow, relative to the app, and whether it changed. */\n\tworkflow: { path: string; changed: boolean };\n}\n\n/**\n * Set up a mobile app's EAS project for a deployed stage.\n *\n * @throws {NoMobileApp}, {MobileAppNotChosen}, {MobileAppUnknown},\n * {MobileAppStageUndeclared}, {EasCliMissing}, {EasCommandFailed} — and\n * what loading the stage throws: {UndeclaredStage}, {ExecStageNotComposed},\n * {ExecStageNotDeployed}\n */\nexport async function easSync(\n\toptions: EasSyncOptions,\n\teas?: EasCli,\n): Promise<EasSyncResult> {\n\tconst loaded = await loadStage(options);\n\tconst { workspace, manifest, stack } = loaded;\n\n\tconst declaration = chooseMobileApp(manifest, options.app, loaded.appName);\n\tconst name = appKey(declaration.id);\n\n\tconst stage = mobileStage(workspace, declaration, options.stage);\n\tconst values =\n\t\tstack.mobileApps.find((candidate) => candidate.name === name)?.values ?? {};\n\tconst dir = join(workspace.root, declaration.app.path);\n\n\tconst path = join(dir, 'eas.json');\n\tconst before = existsSync(path)\n\t\t? (JSON.parse(await readFile(path, 'utf8')) as EasJson)\n\t\t: {};\n\tconst after = withStageProfiles(before, stage);\n\tconst easJsonChanged = JSON.stringify(before) !== JSON.stringify(after);\n\n\tconst workflowPath = easWorkflowPath(stage.stage);\n\tconst workflowFile = join(dir, workflowPath);\n\tconst workflowYaml = easWorkflow(stage, {\n\t\trelease: workspace.stages.protected?.includes(stage.stage) ?? false,\n\t\tiosSubmits: Boolean(\n\t\t\t(after.submit?.[stage.stage]?.ios as { ascAppId?: string } | undefined)\n\t\t\t\t?.ascAppId,\n\t\t),\n\t});\n\tconst workflow = {\n\t\tpath: workflowPath,\n\t\tchanged:\n\t\t\t!existsSync(workflowFile) ||\n\t\t\t(await readFile(workflowFile, 'utf8')) !== workflowYaml,\n\t};\n\n\tconst where = `${name} on '${stage.stage}': EAS environment '${stage.environment}', channel '${stage.channel}', Play track '${stage.android.track}' (${stage.android.releaseStatus})`;\n\tif (options.dryRun) {\n\t\tlogger.log(`📱 ${where} (dry run)`);\n\t\tif (easJsonChanged) {\n\t\t\tlogger.log(\n\t\t\t\t` would write build.${stage.stage} and submit.${stage.stage} in ${path}`,\n\t\t\t);\n\t\t}\n\t\tif (workflow.changed) logger.log(` would write ${workflowFile}`);\n\t\tfor (const [key, value] of Object.entries(values)) {\n\t\t\tlogger.log(` would set ${key}=${value}`);\n\t\t}\n\t\treturn {\n\t\t\tapp: name,\n\t\t\troot: workspace.root,\n\t\t\tdir,\n\t\t\tstage,\n\t\t\tvalues,\n\t\t\teasJsonChanged,\n\t\t\tworkflow,\n\t\t};\n\t}\n\n\tlogger.log(`📱 ${where}`);\n\tif (easJsonChanged) {\n\t\tawait writeFile(path, `${JSON.stringify(after, null, 2)}\\n`);\n\t\tlogger.log(\n\t\t\t` wrote build.${stage.stage} and submit.${stage.stage} in ${path}`,\n\t\t);\n\t}\n\tif (workflow.changed) {\n\t\tawait mkdir(dirname(workflowFile), { recursive: true });\n\t\tawait writeFile(workflowFile, workflowYaml);\n\t\tlogger.log(` wrote ${workflowFile}`);\n\t}\n\tconst cli = eas ?? easCli(workspace.root, values);\n\tfor (const [key, value] of Object.entries(values)) {\n\t\tawait cli.setEnv(dir, { environment: stage.environment, name: key, value });\n\t\tlogger.log(` set ${key}`);\n\t}\n\treturn {\n\t\tapp: name,\n\t\troot: workspace.root,\n\t\tdir,\n\t\tstage,\n\t\tvalues,\n\t\teasJsonChanged,\n\t\tworkflow,\n\t};\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"eas-1Ee6kql0.mjs","names":[],"sources":["../src/mobile/eas.ts"],"sourcesContent":["/**\n * `gkm eas:sync --stage <stage>`: a mobile app's EAS project set up for a\n * deployed stage, from what the workspace declares — so a store build, which\n * EAS runs on its own machines, outside gkm, is built with the same values\n * the stage's servers were deployed with.\n *\n * - **`eas.json`**: the stage's build profile (`build.<stage>`) reads its EAS\n * environment and listens on its channel; its submit profile\n * (`submit.<stage>.android`) has its Play track and release status — see\n * `mobileStage`. Every other field is left as it is.\n * - **The EAS environment**: each value the app's build reads for the stage\n * (`StackMobileApp.values`) — its scheme, its edges' public URLs under\n * `EXPO_PUBLIC_`, and every stage secret already named so — set with\n * `eas env:set`, as `plaintext`: a bundle inlines them, so none is secret.\n * Nothing else is written: no server key reaches EAS.\n */\n\nimport { spawn } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { delimiter, dirname, join } from 'node:path';\nimport type {\n\tConstructManifest,\n\tMobileAppDeclaration,\n} from '@geekmidas/manifest';\nimport { GkmError } from '../errors';\nimport { loadStage } from '../exec/stage.js';\nimport { appKey } from '../workspace/derive.js';\nimport { easWorkflow, easWorkflowPath } from './easWorkflow.js';\nimport { type MobileStage, mobileStage } from './stage.js';\n\nconst logger = console;\n\n/** The workspace declares no `MobileApp`: nothing to set up. */\nexport class NoMobileApp extends GkmError {\n\tconstructor() {\n\t\tsuper(\n\t\t\t'The workspace declares no MobileApp, so there is no EAS project to set ' +\n\t\t\t\t\"up. Declare one: new MobileApp('App', { path: 'apps/app' }).dependsOn([api, auth]).\",\n\t\t);\n\t\tthis.name = 'NoMobileApp';\n\t}\n}\n\n/** Several `MobileApp`s, and nothing to say which. */\nexport class MobileAppNotChosen extends GkmError {\n\tconstructor(readonly apps: readonly string[]) {\n\t\tsuper(\n\t\t\t`The workspace declares ${apps.length} mobile apps (${apps.join(', ')}). ` +\n\t\t\t\t`Run from one's directory, or name it: --app ${apps[0]}.`,\n\t\t);\n\t\tthis.name = 'MobileAppNotChosen';\n\t}\n}\n\n/** `--app` names no `MobileApp`. */\nexport class MobileAppUnknown extends GkmError {\n\tconstructor(\n\t\treadonly app: string,\n\t\treadonly apps: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`'${app}' is not one of the workspace's mobile apps (${apps.join(', ')}).`,\n\t\t);\n\t\tthis.name = 'MobileAppUnknown';\n\t}\n}\n\n/** `eas` is not installed where the app is. */\nexport class EasCliMissing extends GkmError {\n\tconstructor(readonly dir: string) {\n\t\tsuper(\n\t\t\t`No \\`eas\\` to run in ${dir}. Add it to the app: pnpm add -D eas-cli ` +\n\t\t\t\t'(or install it globally: npm install -g eas-cli).',\n\t\t);\n\t\tthis.name = 'EasCliMissing';\n\t}\n}\n\n/** `eas` refused a command. */\nexport class EasCommandFailed extends GkmError {\n\tconstructor(\n\t\treadonly command: string,\n\t\treadonly code: number | null,\n\t\treadonly stderr: string,\n\t) {\n\t\tsuper(\n\t\t\t`\\`eas ${command}\\` failed (exit ${code}): ${stderr.trim() || 'it said nothing'}. ` +\n\t\t\t\t'In CI, EXPO_TOKEN must be set; on this machine, `eas login`.',\n\t\t);\n\t\tthis.name = 'EasCommandFailed';\n\t}\n}\n\n/** A platform EAS builds for. */\nexport type EasPlatform = 'ios' | 'android';\n\n/** What gkm asks of EAS, so tests can stand in. */\nexport interface EasCli {\n\t/** Create or update a variable in one of the project's environments. */\n\tsetEnv(\n\t\tdir: string,\n\t\tvariable: { environment: string; name: string; value: string },\n\t): Promise<void>;\n\t/**\n\t * The fingerprint of the app's native code, as a build with `profile`\n\t * would compute it — what its runtime version is.\n\t */\n\tfingerprint(\n\t\tdir: string,\n\t\toptions: { platform: EasPlatform; profile: string },\n\t): Promise<string>;\n\t/** A finished store build of `profile` with that fingerprint, if any. */\n\tstoreBuild(\n\t\tdir: string,\n\t\toptions: { platform: EasPlatform; profile: string; fingerprint: string },\n\t): Promise<string | undefined>;\n\t/** Start a store build of `profile`, submitted to the store when asked. */\n\tbuild(\n\t\tdir: string,\n\t\toptions: {\n\t\t\tplatform: EasPlatform;\n\t\t\tprofile: string;\n\t\t\tsubmit: boolean;\n\t\t\twait: boolean;\n\t\t},\n\t): Promise<void>;\n\t/** Publish an update of the app's JavaScript on a channel. */\n\tupdate(\n\t\tdir: string,\n\t\toptions: {\n\t\t\tplatform: EasPlatform;\n\t\t\tchannel: string;\n\t\t\tenvironment: string;\n\t\t\tmessage: string;\n\t\t},\n\t): Promise<void>;\n}\n\n/**\n * Run `eas` in `dir`: the app's own, else the workspace's, else the one on\n * PATH. What it prints on stdout is answered; a failure is\n * {@link EasCommandFailed} with what it said.\n */\nfunction runEas(\n\troot: string,\n\tdir: string,\n\targs: readonly string[],\n\toptions: { inherit?: boolean; env?: Readonly<Record<string, string>> } = {},\n): Promise<string> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst child = spawn('eas', [...args, '--non-interactive'], {\n\t\t\tcwd: dir,\n\t\t\t// A build's or an update's progress is the user's to read.\n\t\t\tstdio: ['ignore', options.inherit ? 'inherit' : 'pipe', 'pipe'],\n\t\t\tenv: {\n\t\t\t\t...process.env,\n\t\t\t\t...options.env,\n\t\t\t\tPATH: [\n\t\t\t\t\tjoin(dir, 'node_modules', '.bin'),\n\t\t\t\t\tjoin(root, 'node_modules', '.bin'),\n\t\t\t\t\tprocess.env.PATH,\n\t\t\t\t].join(delimiter),\n\t\t\t},\n\t\t});\n\t\tlet stdout = '';\n\t\tlet stderr = '';\n\t\tchild.stdout?.on('data', (chunk: Buffer) => {\n\t\t\tstdout += chunk.toString();\n\t\t});\n\t\tchild.stderr?.on('data', (chunk: Buffer) => {\n\t\t\tstderr += chunk.toString();\n\t\t\tif (options.inherit) process.stderr.write(chunk);\n\t\t});\n\t\tconst command = args.slice(0, 3).join(' ');\n\t\tchild.on('error', (error: NodeJS.ErrnoException) =>\n\t\t\treject(\n\t\t\t\terror.code === 'ENOENT'\n\t\t\t\t\t? new EasCliMissing(dir)\n\t\t\t\t\t: new EasCommandFailed(command, null, error.message),\n\t\t\t),\n\t\t);\n\t\tchild.on('close', (code) =>\n\t\t\tcode === 0\n\t\t\t\t? resolve(stdout)\n\t\t\t\t: reject(new EasCommandFailed(command, code, stderr)),\n\t\t);\n\t});\n}\n\n/** The JSON `eas … --json` printed, read for `what`. */\nfunction easJson(output: string, what: string): unknown {\n\ttry {\n\t\treturn JSON.parse(output);\n\t} catch {\n\t\tthrow new EasCommandFailed(what, 0, `not JSON: ${output.slice(0, 200)}`);\n\t}\n}\n\n/**\n * The real one — never a prompt (`--non-interactive`). `env` is what the\n * app's `app.config.ts` reads for the stage: every `eas` command evaluates\n * it, and one that reads a value it lacks fails before it starts — and a\n * fingerprint computed here must match the one EAS computes with the\n * stage's values.\n */\nexport function easCli(\n\troot: string,\n\tenv: Readonly<Record<string, string>> = {},\n): EasCli {\n\tconst run = (\n\t\tdir: string,\n\t\targs: readonly string[],\n\t\toptions: { inherit?: boolean } = {},\n\t) => runEas(root, dir, args, { ...options, env });\n\treturn {\n\t\tasync setEnv(dir, { environment, name, value }) {\n\t\t\tawait run(dir, [\n\t\t\t\t'env:set',\n\t\t\t\t'--environment',\n\t\t\t\tenvironment,\n\t\t\t\t'--name',\n\t\t\t\tname,\n\t\t\t\t'--value',\n\t\t\t\tvalue,\n\t\t\t\t'--visibility',\n\t\t\t\t'plaintext',\n\t\t\t\t'--scope',\n\t\t\t\t'project',\n\t\t\t]);\n\t\t},\n\n\t\tasync fingerprint(dir, { platform, profile }) {\n\t\t\tconst output = await run(dir, [\n\t\t\t\t'fingerprint:generate',\n\t\t\t\t'--platform',\n\t\t\t\tplatform,\n\t\t\t\t'--build-profile',\n\t\t\t\tprofile,\n\t\t\t\t'--json',\n\t\t\t]);\n\t\t\tconst hash = (\n\t\t\t\teasJson(output, 'fingerprint:generate') as { hash?: unknown }\n\t\t\t).hash;\n\t\t\tif (typeof hash !== 'string') {\n\t\t\t\tthrow new EasCommandFailed('fingerprint:generate', 0, 'no hash');\n\t\t\t}\n\t\t\treturn hash;\n\t\t},\n\n\t\tasync storeBuild(dir, { platform, profile, fingerprint }) {\n\t\t\tconst output = await run(dir, [\n\t\t\t\t'build:list',\n\t\t\t\t'--platform',\n\t\t\t\tplatform,\n\t\t\t\t'--build-profile',\n\t\t\t\tprofile,\n\t\t\t\t'--fingerprint-hash',\n\t\t\t\tfingerprint,\n\t\t\t\t'--status',\n\t\t\t\t'finished',\n\t\t\t\t'--distribution',\n\t\t\t\t'store',\n\t\t\t\t'--limit',\n\t\t\t\t'1',\n\t\t\t\t'--json',\n\t\t\t]);\n\t\t\tconst builds = easJson(output, 'build:list');\n\t\t\tif (!Array.isArray(builds)) return undefined;\n\t\t\tconst id = (builds[0] as { id?: unknown } | undefined)?.id;\n\t\t\treturn typeof id === 'string' ? id : undefined;\n\t\t},\n\n\t\tasync build(dir, { platform, profile, submit, wait }) {\n\t\t\tawait run(\n\t\t\t\tdir,\n\t\t\t\t[\n\t\t\t\t\t'build',\n\t\t\t\t\t'--platform',\n\t\t\t\t\tplatform,\n\t\t\t\t\t'--profile',\n\t\t\t\t\tprofile,\n\t\t\t\t\t...(submit ? ['--auto-submit'] : []),\n\t\t\t\t\t...(wait ? [] : ['--no-wait']),\n\t\t\t\t],\n\t\t\t\t{ inherit: true },\n\t\t\t);\n\t\t},\n\n\t\tasync update(dir, { platform, channel, environment, message }) {\n\t\t\tawait run(\n\t\t\t\tdir,\n\t\t\t\t[\n\t\t\t\t\t'update',\n\t\t\t\t\t'--platform',\n\t\t\t\t\tplatform,\n\t\t\t\t\t'--channel',\n\t\t\t\t\tchannel,\n\t\t\t\t\t'--environment',\n\t\t\t\t\tenvironment,\n\t\t\t\t\t'--message',\n\t\t\t\t\tmessage,\n\t\t\t\t],\n\t\t\t\t{ inherit: true },\n\t\t\t);\n\t\t},\n\t};\n}\n\n/**\n * The mobile app a command is about: the one `--app` names, else the one\n * this directory is, else the only one.\n *\n * @throws {NoMobileApp}, {MobileAppNotChosen}, {MobileAppUnknown}\n */\nexport function chooseMobileApp(\n\tmanifest: ConstructManifest,\n\tapp: string | undefined,\n\there: string | undefined,\n): MobileAppDeclaration {\n\tconst declarations = Object.values(manifest).filter(\n\t\t(declaration): declaration is MobileAppDeclaration =>\n\t\t\tdeclaration.kind === 'mobile-app',\n\t);\n\tconst names = declarations.map((declaration) => appKey(declaration.id));\n\tif (declarations.length === 0) throw new NoMobileApp();\n\tconst name =\n\t\tapp ??\n\t\t(here && names.includes(here)\n\t\t\t? here\n\t\t\t: names.length === 1\n\t\t\t\t? names[0]\n\t\t\t\t: undefined);\n\tif (!name) throw new MobileAppNotChosen(names);\n\tconst declaration = declarations.find((d) => appKey(d.id) === name);\n\tif (!declaration) throw new MobileAppUnknown(name, names);\n\treturn declaration;\n}\n\n/** `eas.json`, as far as gkm writes it. */\ninterface EasJson {\n\tbuild?: Record<string, Record<string, unknown>>;\n\tsubmit?: Record<string, Record<string, unknown>>;\n\t[key: string]: unknown;\n}\n\n/**\n * `eas.json` with the stage's build and submit profiles as `stage` says:\n * every field gkm does not decide is kept.\n */\nexport function withStageProfiles(\n\teasJson: EasJson,\n\tstage: MobileStage,\n): EasJson {\n\tconst build = easJson.build?.[stage.stage] ?? {};\n\tconst submit = easJson.submit?.[stage.stage] ?? {};\n\tconst android = (submit.android as Record<string, unknown> | undefined) ?? {};\n\treturn {\n\t\t...easJson,\n\t\tbuild: {\n\t\t\t...easJson.build,\n\t\t\t[stage.stage]: {\n\t\t\t\t...build,\n\t\t\t\tenvironment: stage.environment,\n\t\t\t\tchannel: stage.channel,\n\t\t\t},\n\t\t},\n\t\tsubmit: {\n\t\t\t...easJson.submit,\n\t\t\t[stage.stage]: {\n\t\t\t\t...submit,\n\t\t\t\tandroid: {\n\t\t\t\t\t...android,\n\t\t\t\t\ttrack: stage.android.track,\n\t\t\t\t\treleaseStatus: stage.android.releaseStatus,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t};\n}\n\nexport interface EasSyncOptions {\n\tstage: string;\n\t/** The mobile app, by its app key; the one here, or the only one, if not. */\n\tapp?: string;\n\t/** The AWS profile for the stage's account, where its secrets are kept. */\n\tprofile?: string;\n\tcwd?: string;\n\t/** Say what would be written, and write nothing. */\n\tdryRun?: boolean;\n}\n\n/** What a sync wrote — or, dry, would. */\nexport interface EasSyncResult {\n\tapp: string;\n\t/** The workspace's root, where its `eas` may be installed. */\n\troot: string;\n\tdir: string;\n\tstage: MobileStage;\n\tvalues: Record<string, string>;\n\t/** Whether `eas.json` changed. */\n\teasJsonChanged: boolean;\n\t/** The stage's EAS workflow, relative to the app, and whether it changed. */\n\tworkflow: { path: string; changed: boolean };\n}\n\n/**\n * Set up a mobile app's EAS project for a deployed stage.\n *\n * @throws {NoMobileApp}, {MobileAppNotChosen}, {MobileAppUnknown},\n * {MobileAppStageUndeclared}, {EasCliMissing}, {EasCommandFailed} — and\n * what loading the stage throws: {UndeclaredStage}, {ExecStageNotComposed},\n * {ExecStageNotDeployed}\n */\nexport async function easSync(\n\toptions: EasSyncOptions,\n\teas?: EasCli,\n): Promise<EasSyncResult> {\n\tconst loaded = await loadStage(options);\n\tconst { workspace, manifest, stack } = loaded;\n\n\tconst declaration = chooseMobileApp(manifest, options.app, loaded.appName);\n\tconst name = appKey(declaration.id);\n\n\tconst stage = mobileStage(workspace, declaration, options.stage);\n\tconst values =\n\t\tstack.mobileApps.find((candidate) => candidate.name === name)?.values ?? {};\n\tconst dir = join(workspace.root, declaration.app.path);\n\n\tconst path = join(dir, 'eas.json');\n\tconst before = existsSync(path)\n\t\t? (JSON.parse(await readFile(path, 'utf8')) as EasJson)\n\t\t: {};\n\tconst after = withStageProfiles(before, stage);\n\tconst easJsonChanged = JSON.stringify(before) !== JSON.stringify(after);\n\n\tconst workflowPath = easWorkflowPath(stage.stage);\n\tconst workflowFile = join(dir, workflowPath);\n\tconst workflowYaml = easWorkflow(stage, {\n\t\trelease: workspace.stages.protected?.includes(stage.stage) ?? false,\n\t\tiosSubmits: Boolean(\n\t\t\t(after.submit?.[stage.stage]?.ios as { ascAppId?: string } | undefined)\n\t\t\t\t?.ascAppId,\n\t\t),\n\t});\n\tconst workflow = {\n\t\tpath: workflowPath,\n\t\tchanged:\n\t\t\t!existsSync(workflowFile) ||\n\t\t\t(await readFile(workflowFile, 'utf8')) !== workflowYaml,\n\t};\n\n\tconst where = `${name} on '${stage.stage}': EAS environment '${stage.environment}', channel '${stage.channel}', Play track '${stage.android.track}' (${stage.android.releaseStatus})`;\n\tif (options.dryRun) {\n\t\tlogger.log(`📱 ${where} (dry run)`);\n\t\tif (easJsonChanged) {\n\t\t\tlogger.log(\n\t\t\t\t` would write build.${stage.stage} and submit.${stage.stage} in ${path}`,\n\t\t\t);\n\t\t}\n\t\tif (workflow.changed) logger.log(` would write ${workflowFile}`);\n\t\tfor (const [key, value] of Object.entries(values)) {\n\t\t\tlogger.log(` would set ${key}=${value}`);\n\t\t}\n\t\treturn {\n\t\t\tapp: name,\n\t\t\troot: workspace.root,\n\t\t\tdir,\n\t\t\tstage,\n\t\t\tvalues,\n\t\t\teasJsonChanged,\n\t\t\tworkflow,\n\t\t};\n\t}\n\n\tlogger.log(`📱 ${where}`);\n\tif (easJsonChanged) {\n\t\tawait writeFile(path, `${JSON.stringify(after, null, 2)}\\n`);\n\t\tlogger.log(\n\t\t\t` wrote build.${stage.stage} and submit.${stage.stage} in ${path}`,\n\t\t);\n\t}\n\tif (workflow.changed) {\n\t\tawait mkdir(dirname(workflowFile), { recursive: true });\n\t\tawait writeFile(workflowFile, workflowYaml);\n\t\tlogger.log(` wrote ${workflowFile}`);\n\t}\n\tconst cli = eas ?? easCli(workspace.root, values);\n\tfor (const [key, value] of Object.entries(values)) {\n\t\tawait cli.setEnv(dir, { environment: stage.environment, name: key, value });\n\t\tlogger.log(` set ${key}`);\n\t}\n\treturn {\n\t\tapp: name,\n\t\troot: workspace.root,\n\t\tdir,\n\t\tstage,\n\t\tvalues,\n\t\teasJsonChanged,\n\t\tworkflow,\n\t};\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const require_errors = require("./errors-DMC4Umsb.cjs");
|
|
2
2
|
const require_derive = require("./derive-DAbo49sm.cjs");
|
|
3
3
|
const require_index = require("./index.cjs");
|
|
4
|
-
const require_stage = require("./stage-
|
|
4
|
+
const require_stage = require("./stage-fRfFtLwj.cjs");
|
|
5
5
|
let node_path = require("node:path");
|
|
6
6
|
let node_fs = require("node:fs");
|
|
7
7
|
let node_fs_promises = require("node:fs/promises");
|
|
@@ -334,4 +334,4 @@ exports.easCli = easCli;
|
|
|
334
334
|
exports.easSync = easSync;
|
|
335
335
|
exports.withStageProfiles = withStageProfiles;
|
|
336
336
|
|
|
337
|
-
//# sourceMappingURL=eas-
|
|
337
|
+
//# sourceMappingURL=eas-CJoys9yh.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eas-BmBjIFf_.cjs","names":["GkmError","spawn","join","delimiter","appKey","loadStage","mobileStage","existsSync","readFile","easWorkflowPath","easWorkflow","writeFile","mkdir","dirname"],"sources":["../src/mobile/eas.ts"],"sourcesContent":["/**\n * `gkm eas:sync --stage <stage>`: a mobile app's EAS project set up for a\n * deployed stage, from what the workspace declares — so a store build, which\n * EAS runs on its own machines, outside gkm, is built with the same values\n * the stage's servers were deployed with.\n *\n * - **`eas.json`**: the stage's build profile (`build.<stage>`) reads its EAS\n * environment and listens on its channel; its submit profile\n * (`submit.<stage>.android`) has its Play track and release status — see\n * `mobileStage`. Every other field is left as it is.\n * - **The EAS environment**: each value the app's build reads for the stage\n * (`StackMobileApp.values`) — its scheme, its edges' public URLs under\n * `EXPO_PUBLIC_`, and every stage secret already named so — set with\n * `eas env:set`, as `plaintext`: a bundle inlines them, so none is secret.\n * Nothing else is written: no server key reaches EAS.\n */\n\nimport { spawn } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { delimiter, dirname, join } from 'node:path';\nimport type {\n\tConstructManifest,\n\tMobileAppDeclaration,\n} from '@geekmidas/manifest';\nimport { GkmError } from '../errors';\nimport { loadStage } from '../exec/stage.js';\nimport { appKey } from '../workspace/derive.js';\nimport { easWorkflow, easWorkflowPath } from './easWorkflow.js';\nimport { type MobileStage, mobileStage } from './stage.js';\n\nconst logger = console;\n\n/** The workspace declares no `MobileApp`: nothing to set up. */\nexport class NoMobileApp extends GkmError {\n\tconstructor() {\n\t\tsuper(\n\t\t\t'The workspace declares no MobileApp, so there is no EAS project to set ' +\n\t\t\t\t\"up. Declare one: new MobileApp('App', { path: 'apps/app' }).dependsOn([api, auth]).\",\n\t\t);\n\t\tthis.name = 'NoMobileApp';\n\t}\n}\n\n/** Several `MobileApp`s, and nothing to say which. */\nexport class MobileAppNotChosen extends GkmError {\n\tconstructor(readonly apps: readonly string[]) {\n\t\tsuper(\n\t\t\t`The workspace declares ${apps.length} mobile apps (${apps.join(', ')}). ` +\n\t\t\t\t`Run from one's directory, or name it: --app ${apps[0]}.`,\n\t\t);\n\t\tthis.name = 'MobileAppNotChosen';\n\t}\n}\n\n/** `--app` names no `MobileApp`. */\nexport class MobileAppUnknown extends GkmError {\n\tconstructor(\n\t\treadonly app: string,\n\t\treadonly apps: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`'${app}' is not one of the workspace's mobile apps (${apps.join(', ')}).`,\n\t\t);\n\t\tthis.name = 'MobileAppUnknown';\n\t}\n}\n\n/** `eas` is not installed where the app is. */\nexport class EasCliMissing extends GkmError {\n\tconstructor(readonly dir: string) {\n\t\tsuper(\n\t\t\t`No \\`eas\\` to run in ${dir}. Add it to the app: pnpm add -D eas-cli ` +\n\t\t\t\t'(or install it globally: npm install -g eas-cli).',\n\t\t);\n\t\tthis.name = 'EasCliMissing';\n\t}\n}\n\n/** `eas` refused a command. */\nexport class EasCommandFailed extends GkmError {\n\tconstructor(\n\t\treadonly command: string,\n\t\treadonly code: number | null,\n\t\treadonly stderr: string,\n\t) {\n\t\tsuper(\n\t\t\t`\\`eas ${command}\\` failed (exit ${code}): ${stderr.trim() || 'it said nothing'}. ` +\n\t\t\t\t'In CI, EXPO_TOKEN must be set; on this machine, `eas login`.',\n\t\t);\n\t\tthis.name = 'EasCommandFailed';\n\t}\n}\n\n/** A platform EAS builds for. */\nexport type EasPlatform = 'ios' | 'android';\n\n/** What gkm asks of EAS, so tests can stand in. */\nexport interface EasCli {\n\t/** Create or update a variable in one of the project's environments. */\n\tsetEnv(\n\t\tdir: string,\n\t\tvariable: { environment: string; name: string; value: string },\n\t): Promise<void>;\n\t/**\n\t * The fingerprint of the app's native code, as a build with `profile`\n\t * would compute it — what its runtime version is.\n\t */\n\tfingerprint(\n\t\tdir: string,\n\t\toptions: { platform: EasPlatform; profile: string },\n\t): Promise<string>;\n\t/** A finished store build of `profile` with that fingerprint, if any. */\n\tstoreBuild(\n\t\tdir: string,\n\t\toptions: { platform: EasPlatform; profile: string; fingerprint: string },\n\t): Promise<string | undefined>;\n\t/** Start a store build of `profile`, submitted to the store when asked. */\n\tbuild(\n\t\tdir: string,\n\t\toptions: {\n\t\t\tplatform: EasPlatform;\n\t\t\tprofile: string;\n\t\t\tsubmit: boolean;\n\t\t\twait: boolean;\n\t\t},\n\t): Promise<void>;\n\t/** Publish an update of the app's JavaScript on a channel. */\n\tupdate(\n\t\tdir: string,\n\t\toptions: {\n\t\t\tplatform: EasPlatform;\n\t\t\tchannel: string;\n\t\t\tenvironment: string;\n\t\t\tmessage: string;\n\t\t},\n\t): Promise<void>;\n}\n\n/**\n * Run `eas` in `dir`: the app's own, else the workspace's, else the one on\n * PATH. What it prints on stdout is answered; a failure is\n * {@link EasCommandFailed} with what it said.\n */\nfunction runEas(\n\troot: string,\n\tdir: string,\n\targs: readonly string[],\n\toptions: { inherit?: boolean; env?: Readonly<Record<string, string>> } = {},\n): Promise<string> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst child = spawn('eas', [...args, '--non-interactive'], {\n\t\t\tcwd: dir,\n\t\t\t// A build's or an update's progress is the user's to read.\n\t\t\tstdio: ['ignore', options.inherit ? 'inherit' : 'pipe', 'pipe'],\n\t\t\tenv: {\n\t\t\t\t...process.env,\n\t\t\t\t...options.env,\n\t\t\t\tPATH: [\n\t\t\t\t\tjoin(dir, 'node_modules', '.bin'),\n\t\t\t\t\tjoin(root, 'node_modules', '.bin'),\n\t\t\t\t\tprocess.env.PATH,\n\t\t\t\t].join(delimiter),\n\t\t\t},\n\t\t});\n\t\tlet stdout = '';\n\t\tlet stderr = '';\n\t\tchild.stdout?.on('data', (chunk: Buffer) => {\n\t\t\tstdout += chunk.toString();\n\t\t});\n\t\tchild.stderr?.on('data', (chunk: Buffer) => {\n\t\t\tstderr += chunk.toString();\n\t\t\tif (options.inherit) process.stderr.write(chunk);\n\t\t});\n\t\tconst command = args.slice(0, 3).join(' ');\n\t\tchild.on('error', (error: NodeJS.ErrnoException) =>\n\t\t\treject(\n\t\t\t\terror.code === 'ENOENT'\n\t\t\t\t\t? new EasCliMissing(dir)\n\t\t\t\t\t: new EasCommandFailed(command, null, error.message),\n\t\t\t),\n\t\t);\n\t\tchild.on('close', (code) =>\n\t\t\tcode === 0\n\t\t\t\t? resolve(stdout)\n\t\t\t\t: reject(new EasCommandFailed(command, code, stderr)),\n\t\t);\n\t});\n}\n\n/** The JSON `eas … --json` printed, read for `what`. */\nfunction easJson(output: string, what: string): unknown {\n\ttry {\n\t\treturn JSON.parse(output);\n\t} catch {\n\t\tthrow new EasCommandFailed(what, 0, `not JSON: ${output.slice(0, 200)}`);\n\t}\n}\n\n/**\n * The real one — never a prompt (`--non-interactive`). `env` is what the\n * app's `app.config.ts` reads for the stage: every `eas` command evaluates\n * it, and one that reads a value it lacks fails before it starts — and a\n * fingerprint computed here must match the one EAS computes with the\n * stage's values.\n */\nexport function easCli(\n\troot: string,\n\tenv: Readonly<Record<string, string>> = {},\n): EasCli {\n\tconst run = (\n\t\tdir: string,\n\t\targs: readonly string[],\n\t\toptions: { inherit?: boolean } = {},\n\t) => runEas(root, dir, args, { ...options, env });\n\treturn {\n\t\tasync setEnv(dir, { environment, name, value }) {\n\t\t\tawait run(dir, [\n\t\t\t\t'env:set',\n\t\t\t\t'--environment',\n\t\t\t\tenvironment,\n\t\t\t\t'--name',\n\t\t\t\tname,\n\t\t\t\t'--value',\n\t\t\t\tvalue,\n\t\t\t\t'--visibility',\n\t\t\t\t'plaintext',\n\t\t\t\t'--scope',\n\t\t\t\t'project',\n\t\t\t]);\n\t\t},\n\n\t\tasync fingerprint(dir, { platform, profile }) {\n\t\t\tconst output = await run(dir, [\n\t\t\t\t'fingerprint:generate',\n\t\t\t\t'--platform',\n\t\t\t\tplatform,\n\t\t\t\t'--build-profile',\n\t\t\t\tprofile,\n\t\t\t\t'--json',\n\t\t\t]);\n\t\t\tconst hash = (\n\t\t\t\teasJson(output, 'fingerprint:generate') as { hash?: unknown }\n\t\t\t).hash;\n\t\t\tif (typeof hash !== 'string') {\n\t\t\t\tthrow new EasCommandFailed('fingerprint:generate', 0, 'no hash');\n\t\t\t}\n\t\t\treturn hash;\n\t\t},\n\n\t\tasync storeBuild(dir, { platform, profile, fingerprint }) {\n\t\t\tconst output = await run(dir, [\n\t\t\t\t'build:list',\n\t\t\t\t'--platform',\n\t\t\t\tplatform,\n\t\t\t\t'--build-profile',\n\t\t\t\tprofile,\n\t\t\t\t'--fingerprint-hash',\n\t\t\t\tfingerprint,\n\t\t\t\t'--status',\n\t\t\t\t'finished',\n\t\t\t\t'--distribution',\n\t\t\t\t'store',\n\t\t\t\t'--limit',\n\t\t\t\t'1',\n\t\t\t\t'--json',\n\t\t\t]);\n\t\t\tconst builds = easJson(output, 'build:list');\n\t\t\tif (!Array.isArray(builds)) return undefined;\n\t\t\tconst id = (builds[0] as { id?: unknown } | undefined)?.id;\n\t\t\treturn typeof id === 'string' ? id : undefined;\n\t\t},\n\n\t\tasync build(dir, { platform, profile, submit, wait }) {\n\t\t\tawait run(\n\t\t\t\tdir,\n\t\t\t\t[\n\t\t\t\t\t'build',\n\t\t\t\t\t'--platform',\n\t\t\t\t\tplatform,\n\t\t\t\t\t'--profile',\n\t\t\t\t\tprofile,\n\t\t\t\t\t...(submit ? ['--auto-submit'] : []),\n\t\t\t\t\t...(wait ? [] : ['--no-wait']),\n\t\t\t\t],\n\t\t\t\t{ inherit: true },\n\t\t\t);\n\t\t},\n\n\t\tasync update(dir, { platform, channel, environment, message }) {\n\t\t\tawait run(\n\t\t\t\tdir,\n\t\t\t\t[\n\t\t\t\t\t'update',\n\t\t\t\t\t'--platform',\n\t\t\t\t\tplatform,\n\t\t\t\t\t'--channel',\n\t\t\t\t\tchannel,\n\t\t\t\t\t'--environment',\n\t\t\t\t\tenvironment,\n\t\t\t\t\t'--message',\n\t\t\t\t\tmessage,\n\t\t\t\t],\n\t\t\t\t{ inherit: true },\n\t\t\t);\n\t\t},\n\t};\n}\n\n/**\n * The mobile app a command is about: the one `--app` names, else the one\n * this directory is, else the only one.\n *\n * @throws {NoMobileApp}, {MobileAppNotChosen}, {MobileAppUnknown}\n */\nexport function chooseMobileApp(\n\tmanifest: ConstructManifest,\n\tapp: string | undefined,\n\there: string | undefined,\n): MobileAppDeclaration {\n\tconst declarations = Object.values(manifest).filter(\n\t\t(declaration): declaration is MobileAppDeclaration =>\n\t\t\tdeclaration.kind === 'mobile-app',\n\t);\n\tconst names = declarations.map((declaration) => appKey(declaration.id));\n\tif (declarations.length === 0) throw new NoMobileApp();\n\tconst name =\n\t\tapp ??\n\t\t(here && names.includes(here)\n\t\t\t? here\n\t\t\t: names.length === 1\n\t\t\t\t? names[0]\n\t\t\t\t: undefined);\n\tif (!name) throw new MobileAppNotChosen(names);\n\tconst declaration = declarations.find((d) => appKey(d.id) === name);\n\tif (!declaration) throw new MobileAppUnknown(name, names);\n\treturn declaration;\n}\n\n/** `eas.json`, as far as gkm writes it. */\ninterface EasJson {\n\tbuild?: Record<string, Record<string, unknown>>;\n\tsubmit?: Record<string, Record<string, unknown>>;\n\t[key: string]: unknown;\n}\n\n/**\n * `eas.json` with the stage's build and submit profiles as `stage` says:\n * every field gkm does not decide is kept.\n */\nexport function withStageProfiles(\n\teasJson: EasJson,\n\tstage: MobileStage,\n): EasJson {\n\tconst build = easJson.build?.[stage.stage] ?? {};\n\tconst submit = easJson.submit?.[stage.stage] ?? {};\n\tconst android = (submit.android as Record<string, unknown> | undefined) ?? {};\n\treturn {\n\t\t...easJson,\n\t\tbuild: {\n\t\t\t...easJson.build,\n\t\t\t[stage.stage]: {\n\t\t\t\t...build,\n\t\t\t\tenvironment: stage.environment,\n\t\t\t\tchannel: stage.channel,\n\t\t\t},\n\t\t},\n\t\tsubmit: {\n\t\t\t...easJson.submit,\n\t\t\t[stage.stage]: {\n\t\t\t\t...submit,\n\t\t\t\tandroid: {\n\t\t\t\t\t...android,\n\t\t\t\t\ttrack: stage.android.track,\n\t\t\t\t\treleaseStatus: stage.android.releaseStatus,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t};\n}\n\nexport interface EasSyncOptions {\n\tstage: string;\n\t/** The mobile app, by its app key; the one here, or the only one, if not. */\n\tapp?: string;\n\t/** The AWS profile for the stage's account, where its secrets are kept. */\n\tprofile?: string;\n\tcwd?: string;\n\t/** Say what would be written, and write nothing. */\n\tdryRun?: boolean;\n}\n\n/** What a sync wrote — or, dry, would. */\nexport interface EasSyncResult {\n\tapp: string;\n\t/** The workspace's root, where its `eas` may be installed. */\n\troot: string;\n\tdir: string;\n\tstage: MobileStage;\n\tvalues: Record<string, string>;\n\t/** Whether `eas.json` changed. */\n\teasJsonChanged: boolean;\n\t/** The stage's EAS workflow, relative to the app, and whether it changed. */\n\tworkflow: { path: string; changed: boolean };\n}\n\n/**\n * Set up a mobile app's EAS project for a deployed stage.\n *\n * @throws {NoMobileApp}, {MobileAppNotChosen}, {MobileAppUnknown},\n * {MobileAppStageUndeclared}, {EasCliMissing}, {EasCommandFailed} — and\n * what loading the stage throws: {UndeclaredStage}, {ExecStageNotComposed},\n * {ExecStageNotDeployed}\n */\nexport async function easSync(\n\toptions: EasSyncOptions,\n\teas?: EasCli,\n): Promise<EasSyncResult> {\n\tconst loaded = await loadStage(options);\n\tconst { workspace, manifest, stack } = loaded;\n\n\tconst declaration = chooseMobileApp(manifest, options.app, loaded.appName);\n\tconst name = appKey(declaration.id);\n\n\tconst stage = mobileStage(workspace, declaration, options.stage);\n\tconst values =\n\t\tstack.mobileApps.find((candidate) => candidate.name === name)?.values ?? {};\n\tconst dir = join(workspace.root, declaration.app.path);\n\n\tconst path = join(dir, 'eas.json');\n\tconst before = existsSync(path)\n\t\t? (JSON.parse(await readFile(path, 'utf8')) as EasJson)\n\t\t: {};\n\tconst after = withStageProfiles(before, stage);\n\tconst easJsonChanged = JSON.stringify(before) !== JSON.stringify(after);\n\n\tconst workflowPath = easWorkflowPath(stage.stage);\n\tconst workflowFile = join(dir, workflowPath);\n\tconst workflowYaml = easWorkflow(stage, {\n\t\trelease: workspace.stages.protected?.includes(stage.stage) ?? false,\n\t\tiosSubmits: Boolean(\n\t\t\t(after.submit?.[stage.stage]?.ios as { ascAppId?: string } | undefined)\n\t\t\t\t?.ascAppId,\n\t\t),\n\t});\n\tconst workflow = {\n\t\tpath: workflowPath,\n\t\tchanged:\n\t\t\t!existsSync(workflowFile) ||\n\t\t\t(await readFile(workflowFile, 'utf8')) !== workflowYaml,\n\t};\n\n\tconst where = `${name} on '${stage.stage}': EAS environment '${stage.environment}', channel '${stage.channel}', Play track '${stage.android.track}' (${stage.android.releaseStatus})`;\n\tif (options.dryRun) {\n\t\tlogger.log(`📱 ${where} (dry run)`);\n\t\tif (easJsonChanged) {\n\t\t\tlogger.log(\n\t\t\t\t` would write build.${stage.stage} and submit.${stage.stage} in ${path}`,\n\t\t\t);\n\t\t}\n\t\tif (workflow.changed) logger.log(` would write ${workflowFile}`);\n\t\tfor (const [key, value] of Object.entries(values)) {\n\t\t\tlogger.log(` would set ${key}=${value}`);\n\t\t}\n\t\treturn {\n\t\t\tapp: name,\n\t\t\troot: workspace.root,\n\t\t\tdir,\n\t\t\tstage,\n\t\t\tvalues,\n\t\t\teasJsonChanged,\n\t\t\tworkflow,\n\t\t};\n\t}\n\n\tlogger.log(`📱 ${where}`);\n\tif (easJsonChanged) {\n\t\tawait writeFile(path, `${JSON.stringify(after, null, 2)}\\n`);\n\t\tlogger.log(\n\t\t\t` wrote build.${stage.stage} and submit.${stage.stage} in ${path}`,\n\t\t);\n\t}\n\tif (workflow.changed) {\n\t\tawait mkdir(dirname(workflowFile), { recursive: true });\n\t\tawait writeFile(workflowFile, workflowYaml);\n\t\tlogger.log(` wrote ${workflowFile}`);\n\t}\n\tconst cli = eas ?? easCli(workspace.root, values);\n\tfor (const [key, value] of Object.entries(values)) {\n\t\tawait cli.setEnv(dir, { environment: stage.environment, name: key, value });\n\t\tlogger.log(` set ${key}`);\n\t}\n\treturn {\n\t\tapp: name,\n\t\troot: workspace.root,\n\t\tdir,\n\t\tstage,\n\t\tvalues,\n\t\teasJsonChanged,\n\t\tworkflow,\n\t};\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"eas-CJoys9yh.cjs","names":["GkmError","spawn","join","delimiter","appKey","loadStage","mobileStage","existsSync","readFile","easWorkflowPath","easWorkflow","writeFile","mkdir","dirname"],"sources":["../src/mobile/eas.ts"],"sourcesContent":["/**\n * `gkm eas:sync --stage <stage>`: a mobile app's EAS project set up for a\n * deployed stage, from what the workspace declares — so a store build, which\n * EAS runs on its own machines, outside gkm, is built with the same values\n * the stage's servers were deployed with.\n *\n * - **`eas.json`**: the stage's build profile (`build.<stage>`) reads its EAS\n * environment and listens on its channel; its submit profile\n * (`submit.<stage>.android`) has its Play track and release status — see\n * `mobileStage`. Every other field is left as it is.\n * - **The EAS environment**: each value the app's build reads for the stage\n * (`StackMobileApp.values`) — its scheme, its edges' public URLs under\n * `EXPO_PUBLIC_`, and every stage secret already named so — set with\n * `eas env:set`, as `plaintext`: a bundle inlines them, so none is secret.\n * Nothing else is written: no server key reaches EAS.\n */\n\nimport { spawn } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { delimiter, dirname, join } from 'node:path';\nimport type {\n\tConstructManifest,\n\tMobileAppDeclaration,\n} from '@geekmidas/manifest';\nimport { GkmError } from '../errors';\nimport { loadStage } from '../exec/stage.js';\nimport { appKey } from '../workspace/derive.js';\nimport { easWorkflow, easWorkflowPath } from './easWorkflow.js';\nimport { type MobileStage, mobileStage } from './stage.js';\n\nconst logger = console;\n\n/** The workspace declares no `MobileApp`: nothing to set up. */\nexport class NoMobileApp extends GkmError {\n\tconstructor() {\n\t\tsuper(\n\t\t\t'The workspace declares no MobileApp, so there is no EAS project to set ' +\n\t\t\t\t\"up. Declare one: new MobileApp('App', { path: 'apps/app' }).dependsOn([api, auth]).\",\n\t\t);\n\t\tthis.name = 'NoMobileApp';\n\t}\n}\n\n/** Several `MobileApp`s, and nothing to say which. */\nexport class MobileAppNotChosen extends GkmError {\n\tconstructor(readonly apps: readonly string[]) {\n\t\tsuper(\n\t\t\t`The workspace declares ${apps.length} mobile apps (${apps.join(', ')}). ` +\n\t\t\t\t`Run from one's directory, or name it: --app ${apps[0]}.`,\n\t\t);\n\t\tthis.name = 'MobileAppNotChosen';\n\t}\n}\n\n/** `--app` names no `MobileApp`. */\nexport class MobileAppUnknown extends GkmError {\n\tconstructor(\n\t\treadonly app: string,\n\t\treadonly apps: readonly string[],\n\t) {\n\t\tsuper(\n\t\t\t`'${app}' is not one of the workspace's mobile apps (${apps.join(', ')}).`,\n\t\t);\n\t\tthis.name = 'MobileAppUnknown';\n\t}\n}\n\n/** `eas` is not installed where the app is. */\nexport class EasCliMissing extends GkmError {\n\tconstructor(readonly dir: string) {\n\t\tsuper(\n\t\t\t`No \\`eas\\` to run in ${dir}. Add it to the app: pnpm add -D eas-cli ` +\n\t\t\t\t'(or install it globally: npm install -g eas-cli).',\n\t\t);\n\t\tthis.name = 'EasCliMissing';\n\t}\n}\n\n/** `eas` refused a command. */\nexport class EasCommandFailed extends GkmError {\n\tconstructor(\n\t\treadonly command: string,\n\t\treadonly code: number | null,\n\t\treadonly stderr: string,\n\t) {\n\t\tsuper(\n\t\t\t`\\`eas ${command}\\` failed (exit ${code}): ${stderr.trim() || 'it said nothing'}. ` +\n\t\t\t\t'In CI, EXPO_TOKEN must be set; on this machine, `eas login`.',\n\t\t);\n\t\tthis.name = 'EasCommandFailed';\n\t}\n}\n\n/** A platform EAS builds for. */\nexport type EasPlatform = 'ios' | 'android';\n\n/** What gkm asks of EAS, so tests can stand in. */\nexport interface EasCli {\n\t/** Create or update a variable in one of the project's environments. */\n\tsetEnv(\n\t\tdir: string,\n\t\tvariable: { environment: string; name: string; value: string },\n\t): Promise<void>;\n\t/**\n\t * The fingerprint of the app's native code, as a build with `profile`\n\t * would compute it — what its runtime version is.\n\t */\n\tfingerprint(\n\t\tdir: string,\n\t\toptions: { platform: EasPlatform; profile: string },\n\t): Promise<string>;\n\t/** A finished store build of `profile` with that fingerprint, if any. */\n\tstoreBuild(\n\t\tdir: string,\n\t\toptions: { platform: EasPlatform; profile: string; fingerprint: string },\n\t): Promise<string | undefined>;\n\t/** Start a store build of `profile`, submitted to the store when asked. */\n\tbuild(\n\t\tdir: string,\n\t\toptions: {\n\t\t\tplatform: EasPlatform;\n\t\t\tprofile: string;\n\t\t\tsubmit: boolean;\n\t\t\twait: boolean;\n\t\t},\n\t): Promise<void>;\n\t/** Publish an update of the app's JavaScript on a channel. */\n\tupdate(\n\t\tdir: string,\n\t\toptions: {\n\t\t\tplatform: EasPlatform;\n\t\t\tchannel: string;\n\t\t\tenvironment: string;\n\t\t\tmessage: string;\n\t\t},\n\t): Promise<void>;\n}\n\n/**\n * Run `eas` in `dir`: the app's own, else the workspace's, else the one on\n * PATH. What it prints on stdout is answered; a failure is\n * {@link EasCommandFailed} with what it said.\n */\nfunction runEas(\n\troot: string,\n\tdir: string,\n\targs: readonly string[],\n\toptions: { inherit?: boolean; env?: Readonly<Record<string, string>> } = {},\n): Promise<string> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst child = spawn('eas', [...args, '--non-interactive'], {\n\t\t\tcwd: dir,\n\t\t\t// A build's or an update's progress is the user's to read.\n\t\t\tstdio: ['ignore', options.inherit ? 'inherit' : 'pipe', 'pipe'],\n\t\t\tenv: {\n\t\t\t\t...process.env,\n\t\t\t\t...options.env,\n\t\t\t\tPATH: [\n\t\t\t\t\tjoin(dir, 'node_modules', '.bin'),\n\t\t\t\t\tjoin(root, 'node_modules', '.bin'),\n\t\t\t\t\tprocess.env.PATH,\n\t\t\t\t].join(delimiter),\n\t\t\t},\n\t\t});\n\t\tlet stdout = '';\n\t\tlet stderr = '';\n\t\tchild.stdout?.on('data', (chunk: Buffer) => {\n\t\t\tstdout += chunk.toString();\n\t\t});\n\t\tchild.stderr?.on('data', (chunk: Buffer) => {\n\t\t\tstderr += chunk.toString();\n\t\t\tif (options.inherit) process.stderr.write(chunk);\n\t\t});\n\t\tconst command = args.slice(0, 3).join(' ');\n\t\tchild.on('error', (error: NodeJS.ErrnoException) =>\n\t\t\treject(\n\t\t\t\terror.code === 'ENOENT'\n\t\t\t\t\t? new EasCliMissing(dir)\n\t\t\t\t\t: new EasCommandFailed(command, null, error.message),\n\t\t\t),\n\t\t);\n\t\tchild.on('close', (code) =>\n\t\t\tcode === 0\n\t\t\t\t? resolve(stdout)\n\t\t\t\t: reject(new EasCommandFailed(command, code, stderr)),\n\t\t);\n\t});\n}\n\n/** The JSON `eas … --json` printed, read for `what`. */\nfunction easJson(output: string, what: string): unknown {\n\ttry {\n\t\treturn JSON.parse(output);\n\t} catch {\n\t\tthrow new EasCommandFailed(what, 0, `not JSON: ${output.slice(0, 200)}`);\n\t}\n}\n\n/**\n * The real one — never a prompt (`--non-interactive`). `env` is what the\n * app's `app.config.ts` reads for the stage: every `eas` command evaluates\n * it, and one that reads a value it lacks fails before it starts — and a\n * fingerprint computed here must match the one EAS computes with the\n * stage's values.\n */\nexport function easCli(\n\troot: string,\n\tenv: Readonly<Record<string, string>> = {},\n): EasCli {\n\tconst run = (\n\t\tdir: string,\n\t\targs: readonly string[],\n\t\toptions: { inherit?: boolean } = {},\n\t) => runEas(root, dir, args, { ...options, env });\n\treturn {\n\t\tasync setEnv(dir, { environment, name, value }) {\n\t\t\tawait run(dir, [\n\t\t\t\t'env:set',\n\t\t\t\t'--environment',\n\t\t\t\tenvironment,\n\t\t\t\t'--name',\n\t\t\t\tname,\n\t\t\t\t'--value',\n\t\t\t\tvalue,\n\t\t\t\t'--visibility',\n\t\t\t\t'plaintext',\n\t\t\t\t'--scope',\n\t\t\t\t'project',\n\t\t\t]);\n\t\t},\n\n\t\tasync fingerprint(dir, { platform, profile }) {\n\t\t\tconst output = await run(dir, [\n\t\t\t\t'fingerprint:generate',\n\t\t\t\t'--platform',\n\t\t\t\tplatform,\n\t\t\t\t'--build-profile',\n\t\t\t\tprofile,\n\t\t\t\t'--json',\n\t\t\t]);\n\t\t\tconst hash = (\n\t\t\t\teasJson(output, 'fingerprint:generate') as { hash?: unknown }\n\t\t\t).hash;\n\t\t\tif (typeof hash !== 'string') {\n\t\t\t\tthrow new EasCommandFailed('fingerprint:generate', 0, 'no hash');\n\t\t\t}\n\t\t\treturn hash;\n\t\t},\n\n\t\tasync storeBuild(dir, { platform, profile, fingerprint }) {\n\t\t\tconst output = await run(dir, [\n\t\t\t\t'build:list',\n\t\t\t\t'--platform',\n\t\t\t\tplatform,\n\t\t\t\t'--build-profile',\n\t\t\t\tprofile,\n\t\t\t\t'--fingerprint-hash',\n\t\t\t\tfingerprint,\n\t\t\t\t'--status',\n\t\t\t\t'finished',\n\t\t\t\t'--distribution',\n\t\t\t\t'store',\n\t\t\t\t'--limit',\n\t\t\t\t'1',\n\t\t\t\t'--json',\n\t\t\t]);\n\t\t\tconst builds = easJson(output, 'build:list');\n\t\t\tif (!Array.isArray(builds)) return undefined;\n\t\t\tconst id = (builds[0] as { id?: unknown } | undefined)?.id;\n\t\t\treturn typeof id === 'string' ? id : undefined;\n\t\t},\n\n\t\tasync build(dir, { platform, profile, submit, wait }) {\n\t\t\tawait run(\n\t\t\t\tdir,\n\t\t\t\t[\n\t\t\t\t\t'build',\n\t\t\t\t\t'--platform',\n\t\t\t\t\tplatform,\n\t\t\t\t\t'--profile',\n\t\t\t\t\tprofile,\n\t\t\t\t\t...(submit ? ['--auto-submit'] : []),\n\t\t\t\t\t...(wait ? [] : ['--no-wait']),\n\t\t\t\t],\n\t\t\t\t{ inherit: true },\n\t\t\t);\n\t\t},\n\n\t\tasync update(dir, { platform, channel, environment, message }) {\n\t\t\tawait run(\n\t\t\t\tdir,\n\t\t\t\t[\n\t\t\t\t\t'update',\n\t\t\t\t\t'--platform',\n\t\t\t\t\tplatform,\n\t\t\t\t\t'--channel',\n\t\t\t\t\tchannel,\n\t\t\t\t\t'--environment',\n\t\t\t\t\tenvironment,\n\t\t\t\t\t'--message',\n\t\t\t\t\tmessage,\n\t\t\t\t],\n\t\t\t\t{ inherit: true },\n\t\t\t);\n\t\t},\n\t};\n}\n\n/**\n * The mobile app a command is about: the one `--app` names, else the one\n * this directory is, else the only one.\n *\n * @throws {NoMobileApp}, {MobileAppNotChosen}, {MobileAppUnknown}\n */\nexport function chooseMobileApp(\n\tmanifest: ConstructManifest,\n\tapp: string | undefined,\n\there: string | undefined,\n): MobileAppDeclaration {\n\tconst declarations = Object.values(manifest).filter(\n\t\t(declaration): declaration is MobileAppDeclaration =>\n\t\t\tdeclaration.kind === 'mobile-app',\n\t);\n\tconst names = declarations.map((declaration) => appKey(declaration.id));\n\tif (declarations.length === 0) throw new NoMobileApp();\n\tconst name =\n\t\tapp ??\n\t\t(here && names.includes(here)\n\t\t\t? here\n\t\t\t: names.length === 1\n\t\t\t\t? names[0]\n\t\t\t\t: undefined);\n\tif (!name) throw new MobileAppNotChosen(names);\n\tconst declaration = declarations.find((d) => appKey(d.id) === name);\n\tif (!declaration) throw new MobileAppUnknown(name, names);\n\treturn declaration;\n}\n\n/** `eas.json`, as far as gkm writes it. */\ninterface EasJson {\n\tbuild?: Record<string, Record<string, unknown>>;\n\tsubmit?: Record<string, Record<string, unknown>>;\n\t[key: string]: unknown;\n}\n\n/**\n * `eas.json` with the stage's build and submit profiles as `stage` says:\n * every field gkm does not decide is kept.\n */\nexport function withStageProfiles(\n\teasJson: EasJson,\n\tstage: MobileStage,\n): EasJson {\n\tconst build = easJson.build?.[stage.stage] ?? {};\n\tconst submit = easJson.submit?.[stage.stage] ?? {};\n\tconst android = (submit.android as Record<string, unknown> | undefined) ?? {};\n\treturn {\n\t\t...easJson,\n\t\tbuild: {\n\t\t\t...easJson.build,\n\t\t\t[stage.stage]: {\n\t\t\t\t...build,\n\t\t\t\tenvironment: stage.environment,\n\t\t\t\tchannel: stage.channel,\n\t\t\t},\n\t\t},\n\t\tsubmit: {\n\t\t\t...easJson.submit,\n\t\t\t[stage.stage]: {\n\t\t\t\t...submit,\n\t\t\t\tandroid: {\n\t\t\t\t\t...android,\n\t\t\t\t\ttrack: stage.android.track,\n\t\t\t\t\treleaseStatus: stage.android.releaseStatus,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t};\n}\n\nexport interface EasSyncOptions {\n\tstage: string;\n\t/** The mobile app, by its app key; the one here, or the only one, if not. */\n\tapp?: string;\n\t/** The AWS profile for the stage's account, where its secrets are kept. */\n\tprofile?: string;\n\tcwd?: string;\n\t/** Say what would be written, and write nothing. */\n\tdryRun?: boolean;\n}\n\n/** What a sync wrote — or, dry, would. */\nexport interface EasSyncResult {\n\tapp: string;\n\t/** The workspace's root, where its `eas` may be installed. */\n\troot: string;\n\tdir: string;\n\tstage: MobileStage;\n\tvalues: Record<string, string>;\n\t/** Whether `eas.json` changed. */\n\teasJsonChanged: boolean;\n\t/** The stage's EAS workflow, relative to the app, and whether it changed. */\n\tworkflow: { path: string; changed: boolean };\n}\n\n/**\n * Set up a mobile app's EAS project for a deployed stage.\n *\n * @throws {NoMobileApp}, {MobileAppNotChosen}, {MobileAppUnknown},\n * {MobileAppStageUndeclared}, {EasCliMissing}, {EasCommandFailed} — and\n * what loading the stage throws: {UndeclaredStage}, {ExecStageNotComposed},\n * {ExecStageNotDeployed}\n */\nexport async function easSync(\n\toptions: EasSyncOptions,\n\teas?: EasCli,\n): Promise<EasSyncResult> {\n\tconst loaded = await loadStage(options);\n\tconst { workspace, manifest, stack } = loaded;\n\n\tconst declaration = chooseMobileApp(manifest, options.app, loaded.appName);\n\tconst name = appKey(declaration.id);\n\n\tconst stage = mobileStage(workspace, declaration, options.stage);\n\tconst values =\n\t\tstack.mobileApps.find((candidate) => candidate.name === name)?.values ?? {};\n\tconst dir = join(workspace.root, declaration.app.path);\n\n\tconst path = join(dir, 'eas.json');\n\tconst before = existsSync(path)\n\t\t? (JSON.parse(await readFile(path, 'utf8')) as EasJson)\n\t\t: {};\n\tconst after = withStageProfiles(before, stage);\n\tconst easJsonChanged = JSON.stringify(before) !== JSON.stringify(after);\n\n\tconst workflowPath = easWorkflowPath(stage.stage);\n\tconst workflowFile = join(dir, workflowPath);\n\tconst workflowYaml = easWorkflow(stage, {\n\t\trelease: workspace.stages.protected?.includes(stage.stage) ?? false,\n\t\tiosSubmits: Boolean(\n\t\t\t(after.submit?.[stage.stage]?.ios as { ascAppId?: string } | undefined)\n\t\t\t\t?.ascAppId,\n\t\t),\n\t});\n\tconst workflow = {\n\t\tpath: workflowPath,\n\t\tchanged:\n\t\t\t!existsSync(workflowFile) ||\n\t\t\t(await readFile(workflowFile, 'utf8')) !== workflowYaml,\n\t};\n\n\tconst where = `${name} on '${stage.stage}': EAS environment '${stage.environment}', channel '${stage.channel}', Play track '${stage.android.track}' (${stage.android.releaseStatus})`;\n\tif (options.dryRun) {\n\t\tlogger.log(`📱 ${where} (dry run)`);\n\t\tif (easJsonChanged) {\n\t\t\tlogger.log(\n\t\t\t\t` would write build.${stage.stage} and submit.${stage.stage} in ${path}`,\n\t\t\t);\n\t\t}\n\t\tif (workflow.changed) logger.log(` would write ${workflowFile}`);\n\t\tfor (const [key, value] of Object.entries(values)) {\n\t\t\tlogger.log(` would set ${key}=${value}`);\n\t\t}\n\t\treturn {\n\t\t\tapp: name,\n\t\t\troot: workspace.root,\n\t\t\tdir,\n\t\t\tstage,\n\t\t\tvalues,\n\t\t\teasJsonChanged,\n\t\t\tworkflow,\n\t\t};\n\t}\n\n\tlogger.log(`📱 ${where}`);\n\tif (easJsonChanged) {\n\t\tawait writeFile(path, `${JSON.stringify(after, null, 2)}\\n`);\n\t\tlogger.log(\n\t\t\t` wrote build.${stage.stage} and submit.${stage.stage} in ${path}`,\n\t\t);\n\t}\n\tif (workflow.changed) {\n\t\tawait mkdir(dirname(workflowFile), { recursive: true });\n\t\tawait writeFile(workflowFile, workflowYaml);\n\t\tlogger.log(` wrote ${workflowFile}`);\n\t}\n\tconst cli = eas ?? easCli(workspace.root, values);\n\tfor (const [key, value] of Object.entries(values)) {\n\t\tawait cli.setEnv(dir, { environment: stage.environment, name: key, value });\n\t\tlogger.log(` set ${key}`);\n\t}\n\treturn {\n\t\tapp: name,\n\t\troot: workspace.root,\n\t\tdir,\n\t\tstage,\n\t\tvalues,\n\t\teasJsonChanged,\n\t\tworkflow,\n\t};\n}\n"],"mappings":"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"}
|
|
@@ -83,6 +83,22 @@ function withCompute(manifest, compute) {
|
|
|
83
83
|
};
|
|
84
84
|
}
|
|
85
85
|
for (const subscriber of compute.subscribers ?? []) {
|
|
86
|
+
if (subscriber.bucket) {
|
|
87
|
+
const bucket = next[canonicalId(subscriber.bucket)];
|
|
88
|
+
if (bucket?.kind !== "objects") continue;
|
|
89
|
+
next[bucket.id] = {
|
|
90
|
+
...bucket,
|
|
91
|
+
subscribers: [...bucket.subscribers ?? [], {
|
|
92
|
+
id: subscriber.name,
|
|
93
|
+
handler: subscriber.handler,
|
|
94
|
+
events: subscriber.subscribedEvents,
|
|
95
|
+
...subscriber.prefix ? { prefix: subscriber.prefix } : {},
|
|
96
|
+
...subscriber.suffix ? { suffix: subscriber.suffix } : {},
|
|
97
|
+
dependencies: edgesTo(manifest, subscriber.dependencies)
|
|
98
|
+
}]
|
|
99
|
+
};
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
86
102
|
const topicId = subscriber.topic && canonicalId(subscriber.topic);
|
|
87
103
|
const topic = topicId ? next[topicId] : void 0;
|
|
88
104
|
if (topic?.kind !== "topic") continue;
|
|
@@ -190,4 +206,4 @@ function serialise(value) {
|
|
|
190
206
|
//#endregion
|
|
191
207
|
export { withCompute as n, withRoutes as r, manifestModule as t };
|
|
192
208
|
|
|
193
|
-
//# sourceMappingURL=emit-
|
|
209
|
+
//# sourceMappingURL=emit-CNpuV_6Q.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emit-CNpuV_6Q.mjs","names":[],"sources":["../src/reconcile/emit.ts"],"sourcesContent":["/**\n * The manifest, written out as a module.\n *\n * Discovery imports the application's own code — that is how it finds\n * constructs — so anything calling it evaluates the whole runtime graph: React\n * email templates, database clients, modules that read env at import time. That\n * is fine inside `gkm dev`, which is already running the app. It is not fine\n * inside a deploy config, where it fails on the first thing the deploy toolchain\n * does not expect and would keep failing on the next.\n *\n * So discovery happens once, in the build, and everything downstream reads what\n * it wrote. The same split `RestApi` makes by naming a routes glob rather than\n * importing route modules.\n *\n * A module rather than JSON, because a manifest is not just data to this\n * codebase — `as const satisfies ConstructManifest` is what keeps every id,\n * kind and provided key a *literal*, so `IdsOf`, `ProvidedKeys` and the rest\n * can select out of it. `JSON.parse` returns `any` and gives all of that up.\n */\n\nimport type {\n\tConstructManifest,\n\tCronInfo,\n\tDeclaration,\n\tDependency,\n\tFunctionInfo,\n\tQueueInfo,\n\tRestApiEndpoint,\n\tRouteInfo,\n\tSubscriberInfo,\n} from '@geekmidas/manifest';\nimport { canonicalId, provideKey } from '@geekmidas/manifest';\nimport type { CacheBackend, EmailBackend } from '../types.js';\n\n/**\n * Fold the build's generated routes into the surface that serves them.\n *\n * The routes already exist: `gkm build` generates one handler per endpoint and\n * records its path, method and environment. What was missing is that the\n * *construct* manifest never learned about them, so a `rest-api` node said\n * `endpoints: []` while five handlers sat in `.gkm/`. Two documents describing\n * the same routes, one of them wrong.\n *\n * Merging here rather than making the construct name a glob or import its\n * endpoints: the build is the thing that already walked the filesystem and\n * already knows where it wrote each handler. Asking the declaration to restate\n * any of that is how the two come to disagree.\n *\n * Attribution is to the surface the build served — the one whose `path` is\n * the app being built. Without one, routes go to the surface that declared none\n * of its own; a surface with static endpoints (an auth server's single\n * wildcard) keeps them.\n *\n * **Only from a provider that generates one handler per route.** The `server`\n * provider generates a single catch-all — `ALL *` pointing at the Hono app that\n * routes in-process — which is a true description of that build and a false\n * description of the application. Merging it wrote `endpoints: [{ method:\n * 'ALL', path: '*' }]` into a manifest for an app with five routes, and made the\n * document's contents depend on which `--provider` ran last. A manifest must not\n * do that.\n */\nexport function withRoutes(\n\tmanifest: ConstructManifest,\n\troutes: readonly RouteInfo[],\n\toptions: { perRoute: boolean; surface?: string },\n): ConstructManifest {\n\tif (routes.length === 0 || !options.perRoute) return manifest;\n\n\tconst target = Object.entries(manifest).find(([id, declaration]) =>\n\t\toptions.surface\n\t\t\t? id === options.surface\n\t\t\t: declaration.kind === 'rest-api' && declaration.endpoints.length === 0,\n\t);\n\n\tif (!target) return manifest;\n\n\tconst [id, declaration] = target;\n\tif (declaration.kind !== 'rest-api') return manifest;\n\n\treturn {\n\t\t...manifest,\n\t\t[id]: {\n\t\t\t...declaration,\n\t\t\tendpoints: routes.map((route) => asEndpoint(manifest, id, route)),\n\t\t},\n\t};\n}\n\n/**\n * Fold in the compute the build generated: functions, crons, queue workers and\n * topic subscribers.\n *\n * All four are named by their *export*, the same way an endpoint's handler is —\n * `CronGenerator` uses the export key as the name — so none of them can declare\n * themselves and all of them are the build's to add. That is why a `Function`\n * has no `declare()` and never will.\n *\n * A worker and a subscriber **nest** inside the queue or topic that triggers\n * them, because position carries the trigger. A function and a cron are\n * top-level, because nothing triggers them but a caller or a clock.\n */\nexport function withCompute(\n\tmanifest: ConstructManifest,\n\tcompute: {\n\t\tfunctions?: readonly FunctionInfo[];\n\t\tcrons?: readonly CronInfo[];\n\t\tqueues?: readonly QueueInfo[];\n\t\tsubscribers?: readonly SubscriberInfo[];\n\t},\n): ConstructManifest {\n\tconst next: Record<string, Declaration> = { ...manifest };\n\n\tfor (const fn of compute.functions ?? []) {\n\t\tnext[fn.name] = {\n\t\t\tkind: 'function',\n\t\t\tid: fn.name,\n\t\t\thandler: fn.handler,\n\t\t\tdependencies: edgesTo(manifest, fn.dependencies),\n\t\t\tprovides: [provideKey(fn.name, 'url')],\n\t\t};\n\t}\n\n\tfor (const cron of compute.crons ?? []) {\n\t\tnext[cron.name] = {\n\t\t\tkind: 'cron',\n\t\t\tid: cron.name,\n\t\t\thandler: cron.handler,\n\t\t\tschedule: cron.schedule,\n\t\t\tdependencies: edgesTo(manifest, cron.dependencies),\n\t\t};\n\t}\n\n\t// A worker drains exactly one queue, so it is matched by the queue it names\n\t// rather than by anything about the handler.\n\tfor (const worker of compute.queues ?? []) {\n\t\tconst queue = next[canonicalId(worker.name)];\n\t\tif (queue?.kind !== 'queue') continue;\n\n\t\tnext[queue.id] = {\n\t\t\t...queue,\n\t\t\tworker: {\n\t\t\t\tid: `${queue.id}Worker`,\n\t\t\t\thandler: worker.handler,\n\t\t\t\tdependencies: edgesTo(manifest, worker.dependencies),\n\t\t\t},\n\t\t};\n\t}\n\n\tfor (const subscriber of compute.subscribers ?? []) {\n\t\t// A bucket's subscriber nests in the bucket, as a topic's does in the\n\t\t// topic: the storage is what triggers it.\n\t\tif (subscriber.bucket) {\n\t\t\tconst bucket = next[canonicalId(subscriber.bucket)];\n\t\t\tif (bucket?.kind !== 'objects') continue;\n\n\t\t\tnext[bucket.id] = {\n\t\t\t\t...bucket,\n\t\t\t\tsubscribers: [\n\t\t\t\t\t...(bucket.subscribers ?? []),\n\t\t\t\t\t{\n\t\t\t\t\t\tid: subscriber.name,\n\t\t\t\t\t\thandler: subscriber.handler,\n\t\t\t\t\t\tevents: subscriber.subscribedEvents,\n\t\t\t\t\t\t...(subscriber.prefix ? { prefix: subscriber.prefix } : {}),\n\t\t\t\t\t\t...(subscriber.suffix ? { suffix: subscriber.suffix } : {}),\n\t\t\t\t\t\tdependencies: edgesTo(manifest, subscriber.dependencies),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t};\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst topicId = subscriber.topic && canonicalId(subscriber.topic);\n\t\tconst topic = topicId ? next[topicId] : undefined;\n\t\tif (topic?.kind !== 'topic') continue;\n\n\t\tnext[topic.id] = {\n\t\t\t...topic,\n\t\t\tsubscribers: [\n\t\t\t\t...topic.subscribers,\n\t\t\t\t{\n\t\t\t\t\tid: subscriber.name,\n\t\t\t\t\thandler: subscriber.handler,\n\t\t\t\t\tevents: subscriber.subscribedEvents,\n\t\t\t\t\tdependencies: edgesTo(manifest, subscriber.dependencies),\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\t}\n\n\treturn next;\n}\n\n/**\n * Construct ids as manifest edges.\n *\n * A `Dependency` carries the target's kind as well as its id, and the kind is\n * read off the manifest rather than recorded beside the edge — the same rule\n * `edgeTo` follows, for the same reason: a hand-written kind is one fact stated\n * twice, and the copy is what goes stale.\n *\n * An id that names nothing is dropped rather than thrown on. A handler can\n * depend on a construct that declares no node of its own — a lifted `Service`\n * is exactly that — and a build is the wrong place to refuse it; what a target\n * needs from this list is the subset it can actually grant.\n */\nfunction edgesTo(\n\tmanifest: ConstructManifest,\n\tids: readonly string[] = [],\n): Dependency[] {\n\treturn ids.flatMap((target) => {\n\t\tconst declaration = manifest[target];\n\n\t\treturn declaration ? [{ target, kind: declaration.kind }] : [];\n\t});\n}\n\n/** One generated route as the manifest's shape. */\nfunction asEndpoint(\n\tmanifest: ConstructManifest,\n\tsurface: string,\n\troute: RouteInfo,\n): RestApiEndpoint {\n\treturn {\n\t\t// The handler path is already unique per route and is what a target needs\n\t\t// to point a function at, so it is also the natural id.\n\t\tid: `${surface}${route.method}${route.path}`,\n\t\thandler: route.handler,\n\t\tmethod: route.method,\n\t\tpath: route.path,\n\t\t// The edges the handler declared, which is what a target reads to grant\n\t\t// this one function access to exactly those constructs. `environment` is\n\t\t// the same edges' shadow — the keys read — and cannot be turned back into\n\t\t// them, which is why the ids are carried rather than inferred.\n\t\tdependencies: edgesTo(manifest, route.dependencies),\n\t\t...(route.authorizer ? { authorizer: route.authorizer } : {}),\n\t};\n}\n\n/**\n * The manifest as TypeScript source.\n *\n * Pure, so what gets written can be asserted without touching a filesystem.\n */\nexport function manifestModule(\n\tmanifest: ConstructManifest,\n\tbackends: Backends = {},\n): string {\n\treturn `${HEADER}\nexport const constructs = ${serialise(manifest)} as const satisfies ConstructManifest;\n\n/**\n * Where the backends that are *config* rather than declaration resolved to.\n *\n * Recorded because they were being answered twice: once by the build, which\n * registers exactly one cache driver, and once by a deploy config, which told\n * the provisioner something else. The result was a deployed URL the running code\n * had no driver for — two documents disagreeing about one fact, which is what\n * this manifest exists to stop.\n */\nexport const backends = ${serialise(backends)} as const;\n\n// Derived types\nexport type Ids = IdsOf<typeof constructs>;\nexport type Construct<Id extends Ids> = DeclarationOf<typeof constructs, Id>;\nexport type Kind = Construct<Ids>['kind'];\n\n// Useful union types\nexport type ProvidedKeys = AllProvidedKeys<typeof constructs>;\nexport type Surfaces = IdsOfKind<typeof constructs, 'rest-api'>;\nexport type CacheBackend = (typeof backends)['cache'];\nexport type EmailBackend = (typeof backends)['email'];\n`;\n}\n\n/** The choices that are deployment config rather than declaration. */\nexport interface Backends {\n\tcache?: CacheBackend;\n\temail?: EmailBackend;\n}\n\nconst HEADER = `// Generated by \\`gkm build\\`. Do not edit.\n//\n// Discovery imports application code, so it runs once here rather than in every\n// consumer — a deploy config that discovered for itself would evaluate the whole\n// runtime graph inside its own toolchain. This is the result, as literals.\n\nimport type {\n\tAllProvidedKeys,\n\tConstructManifest,\n\tDeclarationOf,\n\tIdsOf,\n\tIdsOfKind,\n} from '@geekmidas/manifest';\n`;\n\n/**\n * JSON, re-indented with tabs and with keys left unquoted where they can be.\n *\n * Cosmetic, and worth it: this file is read by people trying to work out what\n * their app declares, and a wall of quoted keys reads as output rather than as\n * something anyone is expected to understand.\n */\nfunction serialise(value: object): string {\n\treturn JSON.stringify(value, null, '\\t')\n\t\t.replace(/^(\\t*)\"([A-Za-z_$][\\w$]*)\":/gm, '$1$2:')\n\t\t.replace(/\"/g, \"'\");\n}\n"],"mappings":"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"}
|
|
@@ -83,6 +83,22 @@ function withCompute(manifest, compute) {
|
|
|
83
83
|
};
|
|
84
84
|
}
|
|
85
85
|
for (const subscriber of compute.subscribers ?? []) {
|
|
86
|
+
if (subscriber.bucket) {
|
|
87
|
+
const bucket = next[(0, _geekmidas_manifest.canonicalId)(subscriber.bucket)];
|
|
88
|
+
if (bucket?.kind !== "objects") continue;
|
|
89
|
+
next[bucket.id] = {
|
|
90
|
+
...bucket,
|
|
91
|
+
subscribers: [...bucket.subscribers ?? [], {
|
|
92
|
+
id: subscriber.name,
|
|
93
|
+
handler: subscriber.handler,
|
|
94
|
+
events: subscriber.subscribedEvents,
|
|
95
|
+
...subscriber.prefix ? { prefix: subscriber.prefix } : {},
|
|
96
|
+
...subscriber.suffix ? { suffix: subscriber.suffix } : {},
|
|
97
|
+
dependencies: edgesTo(manifest, subscriber.dependencies)
|
|
98
|
+
}]
|
|
99
|
+
};
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
86
102
|
const topicId = subscriber.topic && (0, _geekmidas_manifest.canonicalId)(subscriber.topic);
|
|
87
103
|
const topic = topicId ? next[topicId] : void 0;
|
|
88
104
|
if (topic?.kind !== "topic") continue;
|
|
@@ -207,4 +223,4 @@ Object.defineProperty(exports, "withRoutes", {
|
|
|
207
223
|
}
|
|
208
224
|
});
|
|
209
225
|
|
|
210
|
-
//# sourceMappingURL=emit-
|
|
226
|
+
//# sourceMappingURL=emit-qvHxMV9b.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emit-qvHxMV9b.cjs","names":["provideKey","canonicalId"],"sources":["../src/reconcile/emit.ts"],"sourcesContent":["/**\n * The manifest, written out as a module.\n *\n * Discovery imports the application's own code — that is how it finds\n * constructs — so anything calling it evaluates the whole runtime graph: React\n * email templates, database clients, modules that read env at import time. That\n * is fine inside `gkm dev`, which is already running the app. It is not fine\n * inside a deploy config, where it fails on the first thing the deploy toolchain\n * does not expect and would keep failing on the next.\n *\n * So discovery happens once, in the build, and everything downstream reads what\n * it wrote. The same split `RestApi` makes by naming a routes glob rather than\n * importing route modules.\n *\n * A module rather than JSON, because a manifest is not just data to this\n * codebase — `as const satisfies ConstructManifest` is what keeps every id,\n * kind and provided key a *literal*, so `IdsOf`, `ProvidedKeys` and the rest\n * can select out of it. `JSON.parse` returns `any` and gives all of that up.\n */\n\nimport type {\n\tConstructManifest,\n\tCronInfo,\n\tDeclaration,\n\tDependency,\n\tFunctionInfo,\n\tQueueInfo,\n\tRestApiEndpoint,\n\tRouteInfo,\n\tSubscriberInfo,\n} from '@geekmidas/manifest';\nimport { canonicalId, provideKey } from '@geekmidas/manifest';\nimport type { CacheBackend, EmailBackend } from '../types.js';\n\n/**\n * Fold the build's generated routes into the surface that serves them.\n *\n * The routes already exist: `gkm build` generates one handler per endpoint and\n * records its path, method and environment. What was missing is that the\n * *construct* manifest never learned about them, so a `rest-api` node said\n * `endpoints: []` while five handlers sat in `.gkm/`. Two documents describing\n * the same routes, one of them wrong.\n *\n * Merging here rather than making the construct name a glob or import its\n * endpoints: the build is the thing that already walked the filesystem and\n * already knows where it wrote each handler. Asking the declaration to restate\n * any of that is how the two come to disagree.\n *\n * Attribution is to the surface the build served — the one whose `path` is\n * the app being built. Without one, routes go to the surface that declared none\n * of its own; a surface with static endpoints (an auth server's single\n * wildcard) keeps them.\n *\n * **Only from a provider that generates one handler per route.** The `server`\n * provider generates a single catch-all — `ALL *` pointing at the Hono app that\n * routes in-process — which is a true description of that build and a false\n * description of the application. Merging it wrote `endpoints: [{ method:\n * 'ALL', path: '*' }]` into a manifest for an app with five routes, and made the\n * document's contents depend on which `--provider` ran last. A manifest must not\n * do that.\n */\nexport function withRoutes(\n\tmanifest: ConstructManifest,\n\troutes: readonly RouteInfo[],\n\toptions: { perRoute: boolean; surface?: string },\n): ConstructManifest {\n\tif (routes.length === 0 || !options.perRoute) return manifest;\n\n\tconst target = Object.entries(manifest).find(([id, declaration]) =>\n\t\toptions.surface\n\t\t\t? id === options.surface\n\t\t\t: declaration.kind === 'rest-api' && declaration.endpoints.length === 0,\n\t);\n\n\tif (!target) return manifest;\n\n\tconst [id, declaration] = target;\n\tif (declaration.kind !== 'rest-api') return manifest;\n\n\treturn {\n\t\t...manifest,\n\t\t[id]: {\n\t\t\t...declaration,\n\t\t\tendpoints: routes.map((route) => asEndpoint(manifest, id, route)),\n\t\t},\n\t};\n}\n\n/**\n * Fold in the compute the build generated: functions, crons, queue workers and\n * topic subscribers.\n *\n * All four are named by their *export*, the same way an endpoint's handler is —\n * `CronGenerator` uses the export key as the name — so none of them can declare\n * themselves and all of them are the build's to add. That is why a `Function`\n * has no `declare()` and never will.\n *\n * A worker and a subscriber **nest** inside the queue or topic that triggers\n * them, because position carries the trigger. A function and a cron are\n * top-level, because nothing triggers them but a caller or a clock.\n */\nexport function withCompute(\n\tmanifest: ConstructManifest,\n\tcompute: {\n\t\tfunctions?: readonly FunctionInfo[];\n\t\tcrons?: readonly CronInfo[];\n\t\tqueues?: readonly QueueInfo[];\n\t\tsubscribers?: readonly SubscriberInfo[];\n\t},\n): ConstructManifest {\n\tconst next: Record<string, Declaration> = { ...manifest };\n\n\tfor (const fn of compute.functions ?? []) {\n\t\tnext[fn.name] = {\n\t\t\tkind: 'function',\n\t\t\tid: fn.name,\n\t\t\thandler: fn.handler,\n\t\t\tdependencies: edgesTo(manifest, fn.dependencies),\n\t\t\tprovides: [provideKey(fn.name, 'url')],\n\t\t};\n\t}\n\n\tfor (const cron of compute.crons ?? []) {\n\t\tnext[cron.name] = {\n\t\t\tkind: 'cron',\n\t\t\tid: cron.name,\n\t\t\thandler: cron.handler,\n\t\t\tschedule: cron.schedule,\n\t\t\tdependencies: edgesTo(manifest, cron.dependencies),\n\t\t};\n\t}\n\n\t// A worker drains exactly one queue, so it is matched by the queue it names\n\t// rather than by anything about the handler.\n\tfor (const worker of compute.queues ?? []) {\n\t\tconst queue = next[canonicalId(worker.name)];\n\t\tif (queue?.kind !== 'queue') continue;\n\n\t\tnext[queue.id] = {\n\t\t\t...queue,\n\t\t\tworker: {\n\t\t\t\tid: `${queue.id}Worker`,\n\t\t\t\thandler: worker.handler,\n\t\t\t\tdependencies: edgesTo(manifest, worker.dependencies),\n\t\t\t},\n\t\t};\n\t}\n\n\tfor (const subscriber of compute.subscribers ?? []) {\n\t\t// A bucket's subscriber nests in the bucket, as a topic's does in the\n\t\t// topic: the storage is what triggers it.\n\t\tif (subscriber.bucket) {\n\t\t\tconst bucket = next[canonicalId(subscriber.bucket)];\n\t\t\tif (bucket?.kind !== 'objects') continue;\n\n\t\t\tnext[bucket.id] = {\n\t\t\t\t...bucket,\n\t\t\t\tsubscribers: [\n\t\t\t\t\t...(bucket.subscribers ?? []),\n\t\t\t\t\t{\n\t\t\t\t\t\tid: subscriber.name,\n\t\t\t\t\t\thandler: subscriber.handler,\n\t\t\t\t\t\tevents: subscriber.subscribedEvents,\n\t\t\t\t\t\t...(subscriber.prefix ? { prefix: subscriber.prefix } : {}),\n\t\t\t\t\t\t...(subscriber.suffix ? { suffix: subscriber.suffix } : {}),\n\t\t\t\t\t\tdependencies: edgesTo(manifest, subscriber.dependencies),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t};\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst topicId = subscriber.topic && canonicalId(subscriber.topic);\n\t\tconst topic = topicId ? next[topicId] : undefined;\n\t\tif (topic?.kind !== 'topic') continue;\n\n\t\tnext[topic.id] = {\n\t\t\t...topic,\n\t\t\tsubscribers: [\n\t\t\t\t...topic.subscribers,\n\t\t\t\t{\n\t\t\t\t\tid: subscriber.name,\n\t\t\t\t\thandler: subscriber.handler,\n\t\t\t\t\tevents: subscriber.subscribedEvents,\n\t\t\t\t\tdependencies: edgesTo(manifest, subscriber.dependencies),\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\t}\n\n\treturn next;\n}\n\n/**\n * Construct ids as manifest edges.\n *\n * A `Dependency` carries the target's kind as well as its id, and the kind is\n * read off the manifest rather than recorded beside the edge — the same rule\n * `edgeTo` follows, for the same reason: a hand-written kind is one fact stated\n * twice, and the copy is what goes stale.\n *\n * An id that names nothing is dropped rather than thrown on. A handler can\n * depend on a construct that declares no node of its own — a lifted `Service`\n * is exactly that — and a build is the wrong place to refuse it; what a target\n * needs from this list is the subset it can actually grant.\n */\nfunction edgesTo(\n\tmanifest: ConstructManifest,\n\tids: readonly string[] = [],\n): Dependency[] {\n\treturn ids.flatMap((target) => {\n\t\tconst declaration = manifest[target];\n\n\t\treturn declaration ? [{ target, kind: declaration.kind }] : [];\n\t});\n}\n\n/** One generated route as the manifest's shape. */\nfunction asEndpoint(\n\tmanifest: ConstructManifest,\n\tsurface: string,\n\troute: RouteInfo,\n): RestApiEndpoint {\n\treturn {\n\t\t// The handler path is already unique per route and is what a target needs\n\t\t// to point a function at, so it is also the natural id.\n\t\tid: `${surface}${route.method}${route.path}`,\n\t\thandler: route.handler,\n\t\tmethod: route.method,\n\t\tpath: route.path,\n\t\t// The edges the handler declared, which is what a target reads to grant\n\t\t// this one function access to exactly those constructs. `environment` is\n\t\t// the same edges' shadow — the keys read — and cannot be turned back into\n\t\t// them, which is why the ids are carried rather than inferred.\n\t\tdependencies: edgesTo(manifest, route.dependencies),\n\t\t...(route.authorizer ? { authorizer: route.authorizer } : {}),\n\t};\n}\n\n/**\n * The manifest as TypeScript source.\n *\n * Pure, so what gets written can be asserted without touching a filesystem.\n */\nexport function manifestModule(\n\tmanifest: ConstructManifest,\n\tbackends: Backends = {},\n): string {\n\treturn `${HEADER}\nexport const constructs = ${serialise(manifest)} as const satisfies ConstructManifest;\n\n/**\n * Where the backends that are *config* rather than declaration resolved to.\n *\n * Recorded because they were being answered twice: once by the build, which\n * registers exactly one cache driver, and once by a deploy config, which told\n * the provisioner something else. The result was a deployed URL the running code\n * had no driver for — two documents disagreeing about one fact, which is what\n * this manifest exists to stop.\n */\nexport const backends = ${serialise(backends)} as const;\n\n// Derived types\nexport type Ids = IdsOf<typeof constructs>;\nexport type Construct<Id extends Ids> = DeclarationOf<typeof constructs, Id>;\nexport type Kind = Construct<Ids>['kind'];\n\n// Useful union types\nexport type ProvidedKeys = AllProvidedKeys<typeof constructs>;\nexport type Surfaces = IdsOfKind<typeof constructs, 'rest-api'>;\nexport type CacheBackend = (typeof backends)['cache'];\nexport type EmailBackend = (typeof backends)['email'];\n`;\n}\n\n/** The choices that are deployment config rather than declaration. */\nexport interface Backends {\n\tcache?: CacheBackend;\n\temail?: EmailBackend;\n}\n\nconst HEADER = `// Generated by \\`gkm build\\`. Do not edit.\n//\n// Discovery imports application code, so it runs once here rather than in every\n// consumer — a deploy config that discovered for itself would evaluate the whole\n// runtime graph inside its own toolchain. This is the result, as literals.\n\nimport type {\n\tAllProvidedKeys,\n\tConstructManifest,\n\tDeclarationOf,\n\tIdsOf,\n\tIdsOfKind,\n} from '@geekmidas/manifest';\n`;\n\n/**\n * JSON, re-indented with tabs and with keys left unquoted where they can be.\n *\n * Cosmetic, and worth it: this file is read by people trying to work out what\n * their app declares, and a wall of quoted keys reads as output rather than as\n * something anyone is expected to understand.\n */\nfunction serialise(value: object): string {\n\treturn JSON.stringify(value, null, '\\t')\n\t\t.replace(/^(\\t*)\"([A-Za-z_$][\\w$]*)\":/gm, '$1$2:')\n\t\t.replace(/\"/g, \"'\");\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as GkmError } from "./errors-D8Gwp5kU.mjs";
|
|
2
|
-
import { N as assertDeployedStage } from "./workspace-
|
|
3
|
-
import { Et as providerOf, Lt as secretsStoreFor, Wt as withCustomSecret } from "./seeds-
|
|
2
|
+
import { N as assertDeployedStage } from "./workspace-DUnnVjqC.mjs";
|
|
3
|
+
import { Et as providerOf, Lt as secretsStoreFor, Wt as withCustomSecret } from "./seeds-rG-Evfz9.mjs";
|
|
4
4
|
import { loadWorkspaceSettings } from "./config.mjs";
|
|
5
5
|
import { canonicalId, provideKey } from "@geekmidas/manifest";
|
|
6
6
|
import { parseKeyring, retireKey, rotateKeyring } from "@geekmidas/constructs/encryption";
|
|
@@ -86,4 +86,4 @@ async function updateKeyring(construct, { stage }, context, update) {
|
|
|
86
86
|
//#endregion
|
|
87
87
|
export { encryptionRetireCommand, encryptionRotateCommand };
|
|
88
88
|
|
|
89
|
-
//# sourceMappingURL=encryption-
|
|
89
|
+
//# sourceMappingURL=encryption-B8Bk1Mvu.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"encryption-
|
|
1
|
+
{"version":3,"file":"encryption-B8Bk1Mvu.mjs","names":[],"sources":["../src/encryption/index.ts"],"sourcesContent":["/**\n * `gkm encryption:rotate` and `gkm encryption:retire` — the keyring of an\n * `Encryption` construct on a server stage.\n *\n * Rotating adds a key in front and keeps every older one, so what they wrote\n * still opens. Retiring removes one, and is the half that makes rotating worth\n * doing: until a key is gone, it still opens everything it ever wrote. It is\n * never automatic, because the only thing that knows nothing is left under a\n * key is the sweep that re-encrypted the column — so it is done after one,\n * deliberately, by name.\n *\n * Neither applies everywhere, and each says so rather than doing something\n * that looks right: a local stage's keyring is derived and would be replaced on\n * the next `gkm dev`, and on AWS the key is KMS's, which rotates it itself and\n * never strands a value.\n */\n\nimport {\n\tparseKeyring,\n\tretireKey,\n\trotateKeyring,\n} from '@geekmidas/constructs/encryption';\nimport { canonicalId, provideKey } from '@geekmidas/manifest';\nimport { loadWorkspaceSettings } from '../config';\nimport { GkmError } from '../errors';\nimport { withCustomSecret } from '../secrets/storage.js';\nimport { type SecretsStore, secretsStoreFor } from '../secrets/store.js';\nimport { providerOf } from '../workspace/backends.js';\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\n\nconst logger = console;\n\nexport class LocalKeyringIsDerived extends GkmError {\n\tconstructor(\n\t\treadonly construct: string,\n\t\treadonly stage: string,\n\t) {\n\t\tsuper(\n\t\t\t`'${stage}' is the local stage, where ${construct}'s keyring is derived from the project and stage — nothing stored there would be read. Rotate a deployed stage: gkm encryption:rotate ${construct} --stage <stage>.`,\n\t\t);\n\t\tthis.name = 'LocalKeyringIsDerived';\n\t}\n}\n\nexport class KmsRotatesItself extends GkmError {\n\tconstructor(readonly construct: string) {\n\t\tsuper(\n\t\t\t`${construct} is a KMS key on AWS: KMS rotates it yearly and keeps every version, so no value is ever stranded and there is no key to retire. Nothing to do here.`,\n\t\t);\n\t\tthis.name = 'KmsRotatesItself';\n\t}\n}\n\nexport class NoKeyringYet extends GkmError {\n\tconstructor(\n\t\treadonly construct: string,\n\t\treadonly stage: string,\n\t\treadonly key: string,\n\t) {\n\t\tsuper(\n\t\t\t`'${stage}' has no ${key} yet. The first deploy generates ${construct}'s keyring: gkm deploy --stage ${stage}.`,\n\t\t);\n\t\tthis.name = 'NoKeyringYet';\n\t}\n}\n\nexport interface EncryptionCommandOptions {\n\tstage: string;\n}\n\n/** Where a command reads and writes; the real ones unless a test says. */\nexport interface EncryptionCommandContext {\n\tworkspace?: NormalizedWorkspace;\n\tstore?: SecretsStore;\n}\n\nexport async function encryptionRotateCommand(\n\tconstruct: string,\n\toptions: EncryptionCommandOptions,\n\tcontext: EncryptionCommandContext = {},\n): Promise<string> {\n\treturn updateKeyring(construct, options, context, (id, url) => {\n\t\tconst next = rotateKeyring(id, url);\n\t\tconst [current, ...older] = parseKeyring(id, next).keys.map(({ id }) => id);\n\n\t\tlogger.log(`\\n✓ ${id} on '${options.stage}' now writes with '${current}'.`);\n\t\tlogger.log(\n\t\t\t` Still opening: ${older.join(', ')}. Redeploy to start writing with '${current}', reencrypt what is stored, then: gkm encryption:retire ${id} <key> --stage ${options.stage}`,\n\t\t);\n\t\treturn next;\n\t});\n}\n\nexport async function encryptionRetireCommand(\n\tconstruct: string,\n\tkeyId: string,\n\toptions: EncryptionCommandOptions,\n\tcontext: EncryptionCommandContext = {},\n): Promise<string> {\n\treturn updateKeyring(construct, options, context, (id, url) => {\n\t\tconst next = retireKey(id, url, keyId);\n\n\t\tlogger.log(\n\t\t\t`\\n✓ ${id} on '${options.stage}' no longer holds '${keyId}'. Anything still under it cannot be read after the next deploy.`,\n\t\t);\n\t\treturn next;\n\t});\n}\n\nasync function updateKeyring(\n\tconstruct: string,\n\t{ stage }: EncryptionCommandOptions,\n\tcontext: EncryptionCommandContext,\n\tupdate: (id: string, url: string) => string,\n): Promise<string> {\n\tconst workspace = context.workspace ?? (await loadWorkspaceSettings());\n\tconst id = canonicalId(construct);\n\n\tif (stage === workspace.stages.local) {\n\t\tthrow new LocalKeyringIsDerived(id, stage);\n\t}\n\tassertDeployedStage(workspace.stages, stage);\n\tif (providerOf(workspace) === 'aws') throw new KmsRotatesItself(id);\n\n\tconst store = context.store ?? (await secretsStoreFor(workspace, stage));\n\tconst key = provideKey(id, 'url');\n\tconst secrets = await store.read(stage);\n\tconst url = secrets?.custom[key];\n\tif (!secrets || !url) throw new NoKeyringYet(id, stage, key);\n\n\tconst next = update(id, url);\n\tawait store.write(stage, withCustomSecret(secrets, key, next));\n\treturn next;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AA+BA,MAAM,SAAS;AAEf,IAAa,wBAAb,cAA2C,SAAS;CAEzC;CACA;CAFV,YACC,WACA,OACC;EACD,MACC,IAAI,MAAM,8BAA8B,UAAU,wIAAwI,UAAU,kBACrM;EALS,KAAA,YAAA;EACA,KAAA,QAAA;EAKT,KAAK,OAAO;CACb;AACD;AAEA,IAAa,mBAAb,cAAsC,SAAS;CACzB;CAArB,YAAY,WAA4B;EACvC,MACC,GAAG,UAAU,qJACd;EAHoB,KAAA,YAAA;EAIpB,KAAK,OAAO;CACb;AACD;AAEA,IAAa,eAAb,cAAkC,SAAS;CAEhC;CACA;CACA;CAHV,YACC,WACA,OACA,KACC;EACD,MACC,IAAI,MAAM,WAAW,IAAI,mCAAmC,UAAU,iCAAiC,MAAM,EAC9G;EANS,KAAA,YAAA;EACA,KAAA,QAAA;EACA,KAAA,MAAA;EAKT,KAAK,OAAO;CACb;AACD;AAYA,eAAsB,wBACrB,WACA,SACA,UAAoC,CAAC,GACnB;CAClB,OAAO,cAAc,WAAW,SAAS,UAAU,IAAI,QAAQ;EAC9D,MAAM,OAAO,cAAc,IAAI,GAAG;EAClC,MAAM,CAAC,SAAS,GAAG,SAAS,aAAa,IAAI,IAAI,CAAC,CAAC,KAAK,KAAK,EAAE,SAAS,EAAE;EAE1E,OAAO,IAAI,OAAO,GAAG,OAAO,QAAQ,MAAM,qBAAqB,QAAQ,GAAG;EAC1E,OAAO,IACN,oBAAoB,MAAM,KAAK,IAAI,EAAE,oCAAoC,QAAQ,2DAA2D,GAAG,iBAAiB,QAAQ,OACzK;EACA,OAAO;CACR,CAAC;AACF;AAEA,eAAsB,wBACrB,WACA,OACA,SACA,UAAoC,CAAC,GACnB;CAClB,OAAO,cAAc,WAAW,SAAS,UAAU,IAAI,QAAQ;EAC9D,MAAM,OAAO,UAAU,IAAI,KAAK,KAAK;EAErC,OAAO,IACN,OAAO,GAAG,OAAO,QAAQ,MAAM,qBAAqB,MAAM,iEAC3D;EACA,OAAO;CACR,CAAC;AACF;AAEA,eAAe,cACd,WACA,EAAE,SACF,SACA,QACkB;CAClB,MAAM,YAAY,QAAQ,aAAc,MAAM,sBAAsB;CACpE,MAAM,KAAK,YAAY,SAAS;CAEhC,IAAI,UAAU,UAAU,OAAO,OAC9B,MAAM,IAAI,sBAAsB,IAAI,KAAK;CAE1C,oBAAoB,UAAU,QAAQ,KAAK;CAC3C,IAAI,WAAW,SAAS,MAAM,OAAO,MAAM,IAAI,iBAAiB,EAAE;CAElE,MAAM,QAAQ,QAAQ,SAAU,MAAM,gBAAgB,WAAW,KAAK;CACtE,MAAM,MAAM,WAAW,IAAI,KAAK;CAChC,MAAM,UAAU,MAAM,MAAM,KAAK,KAAK;CACtC,MAAM,MAAM,SAAS,OAAO;CAC5B,IAAI,CAAC,WAAW,CAAC,KAAK,MAAM,IAAI,aAAa,IAAI,OAAO,GAAG;CAE3D,MAAM,OAAO,OAAO,IAAI,GAAG;CAC3B,MAAM,MAAM,MAAM,OAAO,iBAAiB,SAAS,KAAK,IAAI,CAAC;CAC7D,OAAO;AACR"}
|