@geekmidas/cli 10.0.0-alpha.110 → 10.0.0-alpha.111

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/dist/{GoDaddyProvider-CjeOduia.mjs → GoDaddyProvider-CPabc3XW.mjs} +2 -2
  2. package/dist/{GoDaddyProvider-CjeOduia.mjs.map → GoDaddyProvider-CPabc3XW.mjs.map} +1 -1
  3. package/dist/{GoDaddyProvider-qNUzTXI7.cjs → GoDaddyProvider-DWrDIXzQ.cjs} +2 -2
  4. package/dist/{GoDaddyProvider-qNUzTXI7.cjs.map → GoDaddyProvider-DWrDIXzQ.cjs.map} +1 -1
  5. package/dist/{LegacyStateStore-pgscYsiG.mjs → LegacyStateStore-BdXoAlYY.mjs} +3 -3
  6. package/dist/{LegacyStateStore-pgscYsiG.mjs.map → LegacyStateStore-BdXoAlYY.mjs.map} +1 -1
  7. package/dist/{LegacyStateStore-MxiSu7KJ.cjs → LegacyStateStore-fsyPGBWu.cjs} +3 -3
  8. package/dist/{LegacyStateStore-MxiSu7KJ.cjs.map → LegacyStateStore-fsyPGBWu.cjs.map} +1 -1
  9. package/dist/{LocalStateStore-Bm8Vmwjb.cjs → LocalStateStore-AtviUBky.cjs} +2 -2
  10. package/dist/{LocalStateStore-Bm8Vmwjb.cjs.map → LocalStateStore-AtviUBky.cjs.map} +1 -1
  11. package/dist/{LocalStateStore-BcvyKbAw.mjs → LocalStateStore-BrAX5yy7.mjs} +2 -2
  12. package/dist/{LocalStateStore-BcvyKbAw.mjs.map → LocalStateStore-BrAX5yy7.mjs.map} +1 -1
  13. package/dist/{S3StateStore-H2XsAzpJ.mjs → S3StateStore-Cx7njkBz.mjs} +2 -2
  14. package/dist/{S3StateStore-H2XsAzpJ.mjs.map → S3StateStore-Cx7njkBz.mjs.map} +1 -1
  15. package/dist/{S3StateStore-DXHSBVsV.cjs → S3StateStore-tn5g80Sx.cjs} +2 -2
  16. package/dist/{S3StateStore-DXHSBVsV.cjs.map → S3StateStore-tn5g80Sx.cjs.map} +1 -1
  17. package/dist/{SSMStateStore-BLPSNl-U.cjs → SSMStateStore-CAFde-Fq.cjs} +2 -2
  18. package/dist/{SSMStateStore-BLPSNl-U.cjs.map → SSMStateStore-CAFde-Fq.cjs.map} +1 -1
  19. package/dist/{SSMStateStore-CENciQEM.mjs → SSMStateStore-sqZ3oDmn.mjs} +2 -2
  20. package/dist/{SSMStateStore-CENciQEM.mjs.map → SSMStateStore-sqZ3oDmn.mjs.map} +1 -1
  21. package/dist/{add-Cdg1kEqN.cjs → add-Cs8yT5iH.cjs} +3 -3
  22. package/dist/{add-Cdg1kEqN.cjs.map → add-Cs8yT5iH.cjs.map} +1 -1
  23. package/dist/{add-snhdVmR-.mjs → add-DJE7zqJ4.mjs} +3 -3
  24. package/dist/{add-snhdVmR-.mjs.map → add-DJE7zqJ4.mjs.map} +1 -1
  25. package/dist/{bundler-Wd9H4QLJ.mjs → bundler-DrySVbXF.mjs} +5 -5
  26. package/dist/{bundler-Wd9H4QLJ.mjs.map → bundler-DrySVbXF.mjs.map} +1 -1
  27. package/dist/{bundler-B4GDxImV.cjs → bundler-NrtItVBp.cjs} +5 -5
  28. package/dist/{bundler-B4GDxImV.cjs.map → bundler-NrtItVBp.cjs.map} +1 -1
  29. package/dist/{cli-BzrDSNSg.mjs → cli-Br5ApIgR.mjs} +4 -4
  30. package/dist/{cli-BzrDSNSg.mjs.map → cli-Br5ApIgR.mjs.map} +1 -1
  31. package/dist/{cli-BZYz55tO.cjs → cli-CHzpFBh-.cjs} +2 -2
  32. package/dist/{cli-BZYz55tO.cjs.map → cli-CHzpFBh-.cjs.map} +1 -1
  33. package/dist/{compose-DN9zySvQ.cjs → compose-ByIbIrzs.cjs} +20 -5
  34. package/dist/compose-ByIbIrzs.cjs.map +1 -0
  35. package/dist/{compose-Gsd1Nq7R.mjs → compose-CDC2bCme.mjs} +20 -5
  36. package/dist/compose-CDC2bCme.mjs.map +1 -0
  37. package/dist/deploy/public.cjs +6 -6
  38. package/dist/deploy/public.mjs +6 -6
  39. package/dist/{deploy-H2S8_mTx.mjs → deploy-DLq9-m6H.mjs} +5 -5
  40. package/dist/{deploy-H2S8_mTx.mjs.map → deploy-DLq9-m6H.mjs.map} +1 -1
  41. package/dist/{deploy-CFvq8daZ.cjs → deploy-Dl_NavYE.cjs} +5 -5
  42. package/dist/{deploy-CFvq8daZ.cjs.map → deploy-Dl_NavYE.cjs.map} +1 -1
  43. package/dist/{dns-BdETzP10.cjs → dns-C2TGXUF4.cjs} +2 -2
  44. package/dist/{dns-BdETzP10.cjs.map → dns-C2TGXUF4.cjs.map} +1 -1
  45. package/dist/{dns-pFMShDqW.mjs → dns-pzftumxg.mjs} +2 -2
  46. package/dist/{dns-pFMShDqW.mjs.map → dns-pzftumxg.mjs.map} +1 -1
  47. package/dist/{dokploy-oDJbnvCb.mjs → dokploy-B7-OwY2s.mjs} +2 -2
  48. package/dist/{dokploy-oDJbnvCb.mjs.map → dokploy-B7-OwY2s.mjs.map} +1 -1
  49. package/dist/{dokploy-HioAjP7K.cjs → dokploy-OqBh8G4g.cjs} +2 -2
  50. package/dist/{dokploy-HioAjP7K.cjs.map → dokploy-OqBh8G4g.cjs.map} +1 -1
  51. package/dist/eas-6ezdJtry.mjs +316 -0
  52. package/dist/eas-6ezdJtry.mjs.map +1 -0
  53. package/dist/eas-BkLA60P2.cjs +319 -0
  54. package/dist/eas-BkLA60P2.cjs.map +1 -0
  55. package/dist/{encryption-D5pDJyp2.mjs → encryption-DJv_jEXi.mjs} +2 -2
  56. package/dist/{encryption-D5pDJyp2.mjs.map → encryption-DJv_jEXi.mjs.map} +1 -1
  57. package/dist/{encryption-DVCxD-gV.cjs → encryption-DoVnVU82.cjs} +2 -2
  58. package/dist/{encryption-DVCxD-gV.cjs.map → encryption-DoVnVU82.cjs.map} +1 -1
  59. package/dist/{engine-BaJGPzte.cjs → engine-CJhQb50j.cjs} +3 -3
  60. package/dist/{engine-BaJGPzte.cjs.map → engine-CJhQb50j.cjs.map} +1 -1
  61. package/dist/{engine-CfAKgr2_.mjs → engine-Db71-OZL.mjs} +3 -3
  62. package/dist/{engine-CfAKgr2_.mjs.map → engine-Db71-OZL.mjs.map} +1 -1
  63. package/dist/{github-E-ZxqFPq.mjs → github-D2eABlxM.mjs} +4 -4
  64. package/dist/{github-E-ZxqFPq.mjs.map → github-D2eABlxM.mjs.map} +1 -1
  65. package/dist/{github-D902Vwr9.cjs → github-DDOHjdS9.cjs} +4 -4
  66. package/dist/{github-D902Vwr9.cjs.map → github-DDOHjdS9.cjs.map} +1 -1
  67. package/dist/googlePlay-DNgybe5t.cjs +931 -0
  68. package/dist/googlePlay-DNgybe5t.cjs.map +1 -0
  69. package/dist/googlePlay-DY9vZQ1p.mjs +931 -0
  70. package/dist/googlePlay-DY9vZQ1p.mjs.map +1 -0
  71. package/dist/index.cjs +157 -65
  72. package/dist/index.cjs.map +1 -1
  73. package/dist/index.mjs +158 -67
  74. package/dist/index.mjs.map +1 -1
  75. package/dist/{migrate-DCXwHcWX.mjs → migrate-B9GswIAr.mjs} +3 -3
  76. package/dist/{migrate-DCXwHcWX.mjs.map → migrate-B9GswIAr.mjs.map} +1 -1
  77. package/dist/{migrate-BZ0GnSZQ.mjs → migrate-CQUGbptv.mjs} +2 -2
  78. package/dist/{migrate-BZ0GnSZQ.mjs.map → migrate-CQUGbptv.mjs.map} +1 -1
  79. package/dist/{migrate-Df81ew0k.cjs → migrate-GLBZlrlJ.cjs} +2 -2
  80. package/dist/{migrate-Df81ew0k.cjs.map → migrate-GLBZlrlJ.cjs.map} +1 -1
  81. package/dist/{migrate-CTqhWtev.cjs → migrate-bDQzFvBN.cjs} +2 -2
  82. package/dist/{migrate-CTqhWtev.cjs.map → migrate-bDQzFvBN.cjs.map} +1 -1
  83. package/dist/publish-B-EIjfQ0.mjs +123 -0
  84. package/dist/publish-B-EIjfQ0.mjs.map +1 -0
  85. package/dist/publish-CPgSbtcs.cjs +124 -0
  86. package/dist/publish-CPgSbtcs.cjs.map +1 -0
  87. package/dist/{resolve-BMpPd8x3.mjs → resolve-CxDqDmCD.mjs} +3 -3
  88. package/dist/{resolve-BMpPd8x3.mjs.map → resolve-CxDqDmCD.mjs.map} +1 -1
  89. package/dist/{resolve-D043YmW9.cjs → resolve-wG49Woex.cjs} +3 -3
  90. package/dist/{resolve-D043YmW9.cjs.map → resolve-wG49Woex.cjs.map} +1 -1
  91. package/dist/{rollback-CDs_XNb0.mjs → rollback-BQgEdr_p.mjs} +4 -4
  92. package/dist/{rollback-CDs_XNb0.mjs.map → rollback-BQgEdr_p.mjs.map} +1 -1
  93. package/dist/{rollback-CBiYsQ3T.cjs → rollback-Cw-Z0mWn.cjs} +4 -4
  94. package/dist/{rollback-CBiYsQ3T.cjs.map → rollback-Cw-Z0mWn.cjs.map} +1 -1
  95. package/dist/{seeds-BjcO4DDa.mjs → seeds-CIk0l4SC.mjs} +13 -10
  96. package/dist/seeds-CIk0l4SC.mjs.map +1 -0
  97. package/dist/{seeds-DNqRA1aQ.cjs → seeds-Dk8YkLkM.cjs} +18 -15
  98. package/dist/seeds-Dk8YkLkM.cjs.map +1 -0
  99. package/dist/{sniffer-DipSS8r8.cjs → sniffer-2aK7cB2u.cjs} +6 -6
  100. package/dist/{sniffer-DipSS8r8.cjs.map → sniffer-2aK7cB2u.cjs.map} +1 -1
  101. package/dist/{sniffer-b0XPmSyT.mjs → sniffer-CABMO-Bh.mjs} +6 -6
  102. package/dist/{sniffer-b0XPmSyT.mjs.map → sniffer-CABMO-Bh.mjs.map} +1 -1
  103. package/dist/{stage-DrDmZusd.mjs → stage-DjxR_7C0.mjs} +69 -33
  104. package/dist/stage-DjxR_7C0.mjs.map +1 -0
  105. package/dist/{stage-C_y2UovO.cjs → stage-DrCS0vKD.cjs} +79 -32
  106. package/dist/stage-DrCS0vKD.cjs.map +1 -0
  107. package/dist/target/public.cjs +1 -1
  108. package/dist/target/public.mjs +1 -1
  109. package/dist/{terminal-CvRLxz6g.mjs → terminal-mXM-Lqc6.mjs} +2 -2
  110. package/dist/{terminal-CvRLxz6g.mjs.map → terminal-mXM-Lqc6.mjs.map} +1 -1
  111. package/package.json +15 -15
  112. package/dist/compose-DN9zySvQ.cjs.map +0 -1
  113. package/dist/compose-Gsd1Nq7R.mjs.map +0 -1
  114. package/dist/seeds-BjcO4DDa.mjs.map +0 -1
  115. package/dist/seeds-DNqRA1aQ.cjs.map +0 -1
  116. package/dist/stage-C_y2UovO.cjs.map +0 -1
  117. package/dist/stage-DrDmZusd.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"googlePlay-DY9vZQ1p.mjs","names":[],"sources":["../src/mobile/googlePlay/easApi.ts","../src/mobile/googlePlay/google.ts","../src/mobile/googlePlay/gcp.ts","../src/mobile/googlePlay/play.ts","../src/mobile/googlePlay/index.ts"],"sourcesContent":["/**\n * EAS, as `gkm deploy:google-play` reaches it: the GraphQL API eas-cli itself\n * calls, with an `EXPO_TOKEN` — to upload a Google service account key and\n * assign it to the app's Android credentials for EAS Submit, which is what\n * `eas credentials` does interactively.\n *\n * The documents are eas-cli's own (24.x), each selecting only what gkm reads:\n * eas-cli's credentials fragment also brings back the app's keystore and its\n * passwords, which gkm has no reason to hold.\n */\n\nimport { GkmError } from '../../errors';\n\nexport const EAS_GRAPHQL = 'https://api.expo.dev/graphql';\n\n/** No `EXPO_TOKEN` to call EAS with. */\nexport class ExpoTokenMissing extends GkmError {\n\tconstructor() {\n\t\tsuper(\n\t\t\t'gkm uploads the key to EAS with an access token, and EXPO_TOKEN is not ' +\n\t\t\t\t'set. Create one at https://expo.dev/settings/access-tokens and export ' +\n\t\t\t\t'it as EXPO_TOKEN.',\n\t\t);\n\t\tthis.name = 'ExpoTokenMissing';\n\t}\n}\n\n/** EAS refused a call. */\nexport class EasApiError extends GkmError {\n\tconstructor(\n\t\treadonly operation: string,\n\t\treadonly detail: string,\n\t) {\n\t\tsuper(`EAS refused ${operation}: ${detail}`);\n\t\tthis.name = 'EasApiError';\n\t}\n}\n\n/** A Google service account key, as EAS holds it. */\nexport interface EasGoogleKey {\n\tid: string;\n\tclientEmail: string;\n\tprivateKeyIdentifier: string;\n}\n\nconst KEY_FIELDS = 'id clientEmail privateKeyIdentifier';\n\nexport interface EasApi {\n\t/** The project's owner account. */\n\tproject(projectId: string): Promise<{\n\t\taccountId: string;\n\t\taccountName: string;\n\t\tfullName: string;\n\t}>;\n\t/** The app's Android credentials for a package, created when absent. */\n\tandroidCredentials(\n\t\tproject: { projectId: string; fullName: string },\n\t\tpackageName: string,\n\t): Promise<{ id: string; submissionsKey?: EasGoogleKey }>;\n\t/** Upload a key file's JSON to the account. */\n\tcreateKey(\n\t\taccountId: string,\n\t\tjson: Record<string, unknown>,\n\t): Promise<EasGoogleKey>;\n\t/** Make a key the one EAS Submit uses for the credentials. */\n\tuseForSubmissions(credentialsId: string, keyId: string): Promise<void>;\n\t/** Every key on the account. */\n\tkeys(accountName: string): Promise<EasGoogleKey[]>;\n\tdeleteKey(id: string): Promise<void>;\n}\n\nexport function easApi(\n\ttoken: string | undefined = process.env.EXPO_TOKEN,\n\tendpoint: string = EAS_GRAPHQL,\n): EasApi {\n\tconst call = async <T>(\n\t\toperation: string,\n\t\tquery: string,\n\t\tvariables: Record<string, unknown>,\n\t): Promise<T> => {\n\t\tif (!token) throw new ExpoTokenMissing();\n\t\tconst response = await fetch(endpoint, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\tauthorization: `Bearer ${token}`,\n\t\t\t\t'content-type': 'application/json',\n\t\t\t},\n\t\t\tbody: JSON.stringify({ query, variables, operationName: operation }),\n\t\t});\n\t\tconst body = (await response.json().catch(() => ({}))) as {\n\t\t\tdata?: T;\n\t\t\terrors?: { message: string }[];\n\t\t};\n\t\tif (!response.ok || body.errors?.length || !body.data) {\n\t\t\tthrow new EasApiError(\n\t\t\t\toperation,\n\t\t\t\tbody.errors?.map((error) => error.message).join('; ') ||\n\t\t\t\t\t`HTTP ${response.status}`,\n\t\t\t);\n\t\t}\n\t\treturn body.data;\n\t};\n\n\tconst credentialsFields = `id googleServiceAccountKeyForSubmissions { ${KEY_FIELDS} }`;\n\ttype Credentials = {\n\t\tid: string;\n\t\tgoogleServiceAccountKeyForSubmissions: EasGoogleKey | null;\n\t};\n\tconst asCredentials = (credentials: Credentials) => ({\n\t\tid: credentials.id,\n\t\t...(credentials.googleServiceAccountKeyForSubmissions\n\t\t\t? { submissionsKey: credentials.googleServiceAccountKeyForSubmissions }\n\t\t\t: {}),\n\t});\n\n\treturn {\n\t\tasync project(projectId) {\n\t\t\tconst data = await call<{\n\t\t\t\tapp: {\n\t\t\t\t\tbyId: {\n\t\t\t\t\t\tfullName: string;\n\t\t\t\t\t\townerAccount: { id: string; name: string };\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t}>(\n\t\t\t\t'AppByIdQuery',\n\t\t\t\t`query AppByIdQuery($appId: String!) {\n app { byId(appId: $appId) { id fullName ownerAccount { id name } } }\n}`,\n\t\t\t\t{ appId: projectId },\n\t\t\t);\n\t\t\tconst app = data.app.byId;\n\t\t\treturn {\n\t\t\t\taccountId: app.ownerAccount.id,\n\t\t\t\taccountName: app.ownerAccount.name,\n\t\t\t\tfullName: app.fullName,\n\t\t\t};\n\t\t},\n\n\t\tasync androidCredentials(project, packageName) {\n\t\t\tconst found = await call<{\n\t\t\t\tapp: { byFullName: { androidAppCredentials: Credentials[] } };\n\t\t\t}>(\n\t\t\t\t'AndroidAppCredentialsByApplicationIdentifierQuery',\n\t\t\t\t`query AndroidAppCredentialsByApplicationIdentifierQuery(\n $projectFullName: String!\n $applicationIdentifier: String\n $legacyOnly: Boolean\n) {\n app {\n byFullName(fullName: $projectFullName) {\n id\n androidAppCredentials(\n filter: { applicationIdentifier: $applicationIdentifier, legacyOnly: $legacyOnly }\n ) { ${credentialsFields} }\n }\n }\n}`,\n\t\t\t\t{\n\t\t\t\t\tprojectFullName: project.fullName,\n\t\t\t\t\tapplicationIdentifier: packageName,\n\t\t\t\t\tlegacyOnly: false,\n\t\t\t\t},\n\t\t\t);\n\t\t\tconst existing = found.app.byFullName.androidAppCredentials[0];\n\t\t\tif (existing) return asCredentials(existing);\n\n\t\t\tconst created = await call<{\n\t\t\t\tandroidAppCredentials: { createAndroidAppCredentials: Credentials };\n\t\t\t}>(\n\t\t\t\t'CreateAndroidAppCredentialsMutation',\n\t\t\t\t`mutation CreateAndroidAppCredentialsMutation(\n $androidAppCredentialsInput: AndroidAppCredentialsInput!\n $appId: ID!\n $applicationIdentifier: String!\n) {\n androidAppCredentials {\n createAndroidAppCredentials(\n androidAppCredentialsInput: $androidAppCredentialsInput\n appId: $appId\n applicationIdentifier: $applicationIdentifier\n ) { ${credentialsFields} }\n }\n}`,\n\t\t\t\t{\n\t\t\t\t\tandroidAppCredentialsInput: {},\n\t\t\t\t\tappId: project.projectId,\n\t\t\t\t\tapplicationIdentifier: packageName,\n\t\t\t\t},\n\t\t\t);\n\t\t\treturn asCredentials(\n\t\t\t\tcreated.androidAppCredentials.createAndroidAppCredentials,\n\t\t\t);\n\t\t},\n\n\t\tasync createKey(accountId, json) {\n\t\t\tconst data = await call<{\n\t\t\t\tgoogleServiceAccountKey: {\n\t\t\t\t\tcreateGoogleServiceAccountKey: EasGoogleKey;\n\t\t\t\t};\n\t\t\t}>(\n\t\t\t\t'CreateGoogleServiceAccountKeyMutation',\n\t\t\t\t`mutation CreateGoogleServiceAccountKeyMutation(\n $googleServiceAccountKeyInput: GoogleServiceAccountKeyInput!\n $accountId: ID!\n) {\n googleServiceAccountKey {\n createGoogleServiceAccountKey(\n googleServiceAccountKeyInput: $googleServiceAccountKeyInput\n accountId: $accountId\n ) { ${KEY_FIELDS} }\n }\n}`,\n\t\t\t\t{ googleServiceAccountKeyInput: { jsonKey: json }, accountId },\n\t\t\t);\n\t\t\treturn data.googleServiceAccountKey.createGoogleServiceAccountKey;\n\t\t},\n\n\t\tasync useForSubmissions(credentialsId, keyId) {\n\t\t\tawait call(\n\t\t\t\t'SetGoogleServiceAccountKeyForSubmissionsMutation',\n\t\t\t\t`mutation SetGoogleServiceAccountKeyForSubmissionsMutation(\n $androidAppCredentialsId: ID!\n $googleServiceAccountKeyId: ID!\n) {\n androidAppCredentials {\n setGoogleServiceAccountKeyForSubmissions(\n id: $androidAppCredentialsId\n googleServiceAccountKeyId: $googleServiceAccountKeyId\n ) { id }\n }\n}`,\n\t\t\t\t{\n\t\t\t\t\tandroidAppCredentialsId: credentialsId,\n\t\t\t\t\tgoogleServiceAccountKeyId: keyId,\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\n\t\tasync keys(accountName) {\n\t\t\tconst keys: EasGoogleKey[] = [];\n\t\t\tlet after: string | null = null;\n\t\t\tdo {\n\t\t\t\tconst data: {\n\t\t\t\t\taccount: {\n\t\t\t\t\t\tbyName: {\n\t\t\t\t\t\t\tgoogleServiceAccountKeysPaginated: {\n\t\t\t\t\t\t\t\tedges: { node: EasGoogleKey }[];\n\t\t\t\t\t\t\t\tpageInfo: { hasNextPage: boolean; endCursor: string | null };\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t};\n\t\t\t\t\t};\n\t\t\t\t} = await call(\n\t\t\t\t\t'GoogleServiceAccountKeysPaginatedByAccountQuery',\n\t\t\t\t\t`query GoogleServiceAccountKeysPaginatedByAccountQuery(\n $accountName: String!\n $after: String\n $first: Int\n) {\n account {\n byName(accountName: $accountName) {\n id\n googleServiceAccountKeysPaginated(after: $after, first: $first) {\n edges { node { ${KEY_FIELDS} } }\n pageInfo { hasNextPage endCursor }\n }\n }\n }\n}`,\n\t\t\t\t\t{ accountName, after, first: 50 },\n\t\t\t\t);\n\t\t\t\tconst page = data.account.byName.googleServiceAccountKeysPaginated;\n\t\t\t\tkeys.push(...page.edges.map((edge) => edge.node));\n\t\t\t\tafter = page.pageInfo.hasNextPage ? page.pageInfo.endCursor : null;\n\t\t\t} while (after);\n\t\t\treturn keys;\n\t\t},\n\n\t\tasync deleteKey(id) {\n\t\t\tawait call(\n\t\t\t\t'DeleteGoogleServiceAccountKeyMutation',\n\t\t\t\t`mutation DeleteGoogleServiceAccountKeyMutation($googleServiceAccountKeyId: ID!) {\n googleServiceAccountKey { deleteGoogleServiceAccountKey(id: $googleServiceAccountKeyId) { id } }\n}`,\n\t\t\t\t{ googleServiceAccountKeyId: id },\n\t\t\t);\n\t\t},\n\t};\n}\n","/**\n * Google, as `gkm deploy:google-play` reaches it: a token for the person\n * running it, and the Cloud APIs that make a project, a service account and\n * its key.\n *\n * The token is gcloud's own application-default credential, with the\n * `cloud-platform` and `androidpublisher` scopes — no OAuth client of gkm's —\n * kept in gkm's own gcloud config (`<gkm home>/gcloud`), so the person's own\n * gcloud setup is never read or changed. Every call carries the project as\n * its quota project (`x-goog-user-project`), which the Play Developer API\n * requires of a user credential.\n */\n\nimport { spawn } from 'node:child_process';\nimport { join } from 'node:path';\nimport { GkmError } from '../../errors';\nimport { gkmHome } from '../../home.js';\n\n/** The scopes the sign-in asks for. */\nexport const GOOGLE_SCOPES = [\n\t'https://www.googleapis.com/auth/cloud-platform',\n\t'https://www.googleapis.com/auth/androidpublisher',\n];\n\n/** `gcloud` is not installed. */\nexport class GcloudMissing extends GkmError {\n\tconstructor() {\n\t\tsuper(\n\t\t\t'gkm signs in to Google with gcloud, and there is no `gcloud` here. ' +\n\t\t\t\t'Install the Google Cloud CLI: https://cloud.google.com/sdk/docs/install.',\n\t\t);\n\t\tthis.name = 'GcloudMissing';\n\t}\n}\n\n/** Not signed in, and no terminal to sign in from. */\nexport class GoogleSignInRequired extends GkmError {\n\tconstructor(readonly config: string) {\n\t\tsuper(\n\t\t\t'Signing in to Google opens a browser, and this run has no terminal. ' +\n\t\t\t\t'Run it once where you can sign in, as a Play Console admin: ' +\n\t\t\t\t`CLOUDSDK_CONFIG=${config} gcloud auth application-default login ` +\n\t\t\t\t`--scopes=${GOOGLE_SCOPES.join(',')}.`,\n\t\t);\n\t\tthis.name = 'GoogleSignInRequired';\n\t}\n}\n\n/** A Google API refused a call. */\nexport class GoogleApiError extends GkmError {\n\tconstructor(\n\t\treadonly method: string,\n\t\treadonly url: string,\n\t\treadonly status: number,\n\t\treadonly detail: string,\n\t) {\n\t\tsuper(`Google answered ${status} to ${method} ${url}: ${detail}`);\n\t\tthis.name = 'GoogleApiError';\n\t}\n}\n\n/** A long-running Google operation did not finish in time. */\nexport class GoogleOperationTimedOut extends GkmError {\n\tconstructor(readonly operation: string) {\n\t\tsuper(\n\t\t\t`Google's operation ${operation} had not finished after a minute. ` +\n\t\t\t\t'Run the command again: what it finished is kept.',\n\t\t);\n\t\tthis.name = 'GoogleOperationTimedOut';\n\t}\n}\n\n/** Where gkm keeps its gcloud sign-in. */\nexport function gcloudConfig(env: NodeJS.ProcessEnv = process.env): string {\n\treturn join(gkmHome(env), 'gcloud');\n}\n\n/** How a token is had: the real one runs gcloud. */\nexport interface GoogleSignIn {\n\t/** An access token for the signed-in person, signing in if allowed. */\n\ttoken(options: { interactive: boolean }): Promise<string>;\n}\n\nfunction gcloud(\n\targs: readonly string[],\n\tconfig: string,\n\tinherit: boolean,\n): Promise<{ code: number | null; stdout: string }> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst child = spawn('gcloud', [...args], {\n\t\t\tstdio: [\n\t\t\t\t'inherit',\n\t\t\t\tinherit ? 'inherit' : 'pipe',\n\t\t\t\tinherit ? 'inherit' : 'pipe',\n\t\t\t],\n\t\t\tenv: { ...process.env, CLOUDSDK_CONFIG: config },\n\t\t});\n\t\tlet stdout = '';\n\t\tchild.stdout?.on('data', (chunk: Buffer) => {\n\t\t\tstdout += chunk.toString();\n\t\t});\n\t\tchild.on('error', (error: NodeJS.ErrnoException) =>\n\t\t\treject(error.code === 'ENOENT' ? new GcloudMissing() : error),\n\t\t);\n\t\tchild.on('close', (code) => resolve({ code, stdout }));\n\t});\n}\n\n/** gcloud's application-default credential, in gkm's own gcloud config. */\nexport function gcloudSignIn(config: string = gcloudConfig()): GoogleSignIn {\n\treturn {\n\t\tasync token({ interactive }) {\n\t\t\tconst print = () =>\n\t\t\t\tgcloud(\n\t\t\t\t\t['auth', 'application-default', 'print-access-token'],\n\t\t\t\t\tconfig,\n\t\t\t\t\tfalse,\n\t\t\t\t);\n\t\t\tlet printed = await print();\n\t\t\tif (printed.code !== 0) {\n\t\t\t\tif (!interactive) throw new GoogleSignInRequired(config);\n\t\t\t\tawait gcloud(\n\t\t\t\t\t[\n\t\t\t\t\t\t'auth',\n\t\t\t\t\t\t'application-default',\n\t\t\t\t\t\t'login',\n\t\t\t\t\t\t`--scopes=${GOOGLE_SCOPES.join(',')}`,\n\t\t\t\t\t],\n\t\t\t\t\tconfig,\n\t\t\t\t\ttrue,\n\t\t\t\t);\n\t\t\t\tprinted = await print();\n\t\t\t\tif (printed.code !== 0) throw new GoogleSignInRequired(config);\n\t\t\t}\n\t\t\treturn printed.stdout.trim();\n\t\t},\n\t};\n}\n\n/** One authenticated Google API caller, for one quota project. */\nexport interface GoogleClient {\n\trequest<T>(\n\t\tmethod: string,\n\t\turl: string,\n\t\tbody?: unknown,\n\t\toptions?: { raw?: { contentType: string; data: Uint8Array } },\n\t): Promise<T>;\n}\n\nexport function googleClient(\n\ttoken: string,\n\tquotaProject?: string,\n): GoogleClient {\n\treturn {\n\t\tasync request(method, url, body, options) {\n\t\t\tconst headers: Record<string, string> = {\n\t\t\t\tauthorization: `Bearer ${token}`,\n\t\t\t\t...(quotaProject ? { 'x-goog-user-project': quotaProject } : {}),\n\t\t\t};\n\t\t\tlet payload: string | Uint8Array | undefined;\n\t\t\tif (options?.raw) {\n\t\t\t\theaders['content-type'] = options.raw.contentType;\n\t\t\t\tpayload = options.raw.data;\n\t\t\t} else if (body !== undefined) {\n\t\t\t\theaders['content-type'] = 'application/json';\n\t\t\t\tpayload = JSON.stringify(body);\n\t\t\t}\n\t\t\tconst response = await fetch(url, {\n\t\t\t\tmethod,\n\t\t\t\theaders,\n\t\t\t\t...(payload !== undefined ? { body: payload } : {}),\n\t\t\t});\n\t\t\tconst text = await response.text();\n\t\t\tif (!response.ok) {\n\t\t\t\tlet detail = text;\n\t\t\t\ttry {\n\t\t\t\t\tconst parsed = JSON.parse(text) as { error?: { message?: string } };\n\t\t\t\t\tdetail = parsed.error?.message ?? text;\n\t\t\t\t} catch {\n\t\t\t\t\t// Not JSON: the text is the detail.\n\t\t\t\t}\n\t\t\t\tthrow new GoogleApiError(method, url, response.status, detail);\n\t\t\t}\n\t\t\treturn (text ? JSON.parse(text) : {}) as never;\n\t\t},\n\t};\n}\n\n/** A long-running operation, polled until it is done. */\nexport async function finished(\n\tclient: GoogleClient,\n\tbase: string,\n\toperation: { name?: string; done?: boolean; error?: { message?: string } },\n\toptions: { intervalMs?: number; attempts?: number } = {},\n): Promise<void> {\n\tlet current = operation;\n\tfor (let attempt = 0; !current.done; attempt++) {\n\t\tif (attempt >= (options.attempts ?? 30)) {\n\t\t\tthrow new GoogleOperationTimedOut(current.name ?? '(unnamed)');\n\t\t}\n\t\tawait new Promise((resolve) =>\n\t\t\tsetTimeout(resolve, options.intervalMs ?? 2_000),\n\t\t);\n\t\tcurrent = await client.request('GET', `${base}/${current.name}`);\n\t}\n\tif (current.error) {\n\t\tthrow new GoogleApiError(\n\t\t\t'GET',\n\t\t\t`${base}/${current.name}`,\n\t\t\t500,\n\t\t\tcurrent.error.message ?? 'the operation failed',\n\t\t);\n\t}\n}\n","/**\n * The Google Cloud side of publishing to Play: a project with the Android\n * Publisher API on, and a service account in it whose JSON key EAS Submit\n * uses. Each step finds what is there before it creates anything, so a\n * second run reuses the project and the account and only makes a new key.\n */\n\nimport { GkmError } from '../../errors';\nimport { finished, GoogleApiError, type GoogleClient } from './google.js';\n\nexport const CLOUD_RESOURCE_MANAGER =\n\t'https://cloudresourcemanager.googleapis.com/v3';\nexport const SERVICE_USAGE = 'https://serviceusage.googleapis.com/v1';\nexport const IAM = 'https://iam.googleapis.com/v1';\n\n/** The API EAS Submit and gkm call Play through. */\nexport const ANDROID_PUBLISHER_API = 'androidpublisher.googleapis.com';\n\n/** A project id or service account id gkm would derive is not a valid one. */\nexport class GoogleIdInvalid extends GkmError {\n\tconstructor(\n\t\treadonly kind: 'project' | 'service account',\n\t\treadonly id: string,\n\t) {\n\t\tsuper(\n\t\t\t`'${id}' is not a valid Google ${kind} id: 6 to 30 lowercase letters, ` +\n\t\t\t\t'digits and hyphens, starting with a letter and not ending in a hyphen. ' +\n\t\t\t\t(kind === 'project' ? 'Name one with --project.' : ''),\n\t\t);\n\t\tthis.name = 'GoogleIdInvalid';\n\t}\n}\n\n/** The project could not be created — most often, its id is someone else's. */\nexport class GoogleProjectUnavailable extends GkmError {\n\tconstructor(\n\t\treadonly project: string,\n\t\treadonly detail: string,\n\t) {\n\t\tsuper(\n\t\t\t`Could not create the Google Cloud project '${project}' (${detail}). ` +\n\t\t\t\t'Project ids are global: name another, or one you already have, with --project.',\n\t\t);\n\t\tthis.name = 'GoogleProjectUnavailable';\n\t}\n}\n\nconst ID = /^[a-z][a-z0-9-]{4,28}[a-z0-9]$/;\n\n/**\n * An id Google accepts, from a workspace's name: `shop` → `shop-play`.\n * Lowercased, anything else made a hyphen, cut to 30.\n */\nexport function playId(workspace: string): string {\n\tconst base = workspace\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9]+/g, '-')\n\t\t.replace(/^[^a-z]+|-+$/g, '');\n\treturn `${base.slice(0, 25).replace(/-+$/, '')}-play`;\n}\n\n/** Throws unless `id` is a valid project or service account id. */\nexport function assertGoogleId(\n\tkind: 'project' | 'service account',\n\tid: string,\n): void {\n\tif (!ID.test(id)) throw new GoogleIdInvalid(kind, id);\n}\n\n/**\n * The project, created when it is not there. Answers whether it was made.\n *\n * @throws {GoogleProjectUnavailable}\n */\nexport async function ensureProject(\n\tclient: GoogleClient,\n\tproject: string,\n\tdisplayName: string,\n\toptions: { intervalMs?: number } = {},\n): Promise<boolean> {\n\tassertGoogleId('project', project);\n\ttry {\n\t\tawait client.request(\n\t\t\t'GET',\n\t\t\t`${CLOUD_RESOURCE_MANAGER}/projects/${project}`,\n\t\t);\n\t\treturn false;\n\t} catch (error) {\n\t\t// Absent, or not ours to see — either way, try to create it.\n\t\tif (\n\t\t\t!(error instanceof GoogleApiError) ||\n\t\t\t![403, 404].includes(error.status)\n\t\t) {\n\t\t\tthrow error;\n\t\t}\n\t}\n\ttry {\n\t\tconst operation = await client.request<{ name?: string; done?: boolean }>(\n\t\t\t'POST',\n\t\t\t`${CLOUD_RESOURCE_MANAGER}/projects`,\n\t\t\t{ projectId: project, displayName: displayName.slice(0, 30) },\n\t\t);\n\t\tawait finished(client, CLOUD_RESOURCE_MANAGER, operation, options);\n\t} catch (error) {\n\t\tif (error instanceof GoogleApiError) {\n\t\t\tthrow new GoogleProjectUnavailable(project, error.detail);\n\t\t}\n\t\tthrow error;\n\t}\n\treturn true;\n}\n\n/** The Android Publisher API, on in the project. */\nexport async function enablePublisherApi(\n\tclient: GoogleClient,\n\tproject: string,\n\toptions: { intervalMs?: number } = {},\n): Promise<void> {\n\tconst operation = await client.request<{ name?: string; done?: boolean }>(\n\t\t'POST',\n\t\t`${SERVICE_USAGE}/projects/${project}/services/${ANDROID_PUBLISHER_API}:enable`,\n\t\t{},\n\t);\n\tawait finished(client, SERVICE_USAGE, operation, options);\n}\n\n/** The service account's email, which is what Play and EAS know it by. */\nexport function serviceAccountEmail(project: string, account: string): string {\n\treturn `${account}@${project}.iam.gserviceaccount.com`;\n}\n\n/** The service account, created when it is not there. Answers its email. */\nexport async function ensureServiceAccount(\n\tclient: GoogleClient,\n\tproject: string,\n\taccount: string,\n\tdisplayName: string,\n): Promise<string> {\n\tassertGoogleId('service account', account);\n\tconst email = serviceAccountEmail(project, account);\n\ttry {\n\t\tawait client.request(\n\t\t\t'GET',\n\t\t\t`${IAM}/projects/${project}/serviceAccounts/${email}`,\n\t\t);\n\t\treturn email;\n\t} catch (error) {\n\t\tif (!(error instanceof GoogleApiError) || error.status !== 404) throw error;\n\t}\n\tawait client.request('POST', `${IAM}/projects/${project}/serviceAccounts`, {\n\t\taccountId: account,\n\t\tserviceAccount: { displayName: displayName.slice(0, 100) },\n\t});\n\treturn email;\n}\n\n/** A service account key: its id, and — only when just made — its JSON. */\nexport interface ServiceAccountKey {\n\t/** `projects/<p>/serviceAccounts/<email>/keys/<id>`. */\n\tname: string;\n\t/** The key file's JSON, held in memory and never written anywhere. */\n\tjson?: string;\n}\n\n/** A new key for the account. Its JSON exists only in what this returns. */\nexport async function createKey(\n\tclient: GoogleClient,\n\tproject: string,\n\temail: string,\n): Promise<ServiceAccountKey & { json: string }> {\n\tconst key = await client.request<{ name: string; privateKeyData: string }>(\n\t\t'POST',\n\t\t`${IAM}/projects/${project}/serviceAccounts/${email}/keys`,\n\t\t{},\n\t);\n\treturn {\n\t\tname: key.name,\n\t\tjson: Buffer.from(key.privateKeyData, 'base64').toString('utf8'),\n\t};\n}\n\n/** The account's keys that someone made — not Google's own. */\nexport async function listKeys(\n\tclient: GoogleClient,\n\tproject: string,\n\temail: string,\n): Promise<ServiceAccountKey[]> {\n\tconst { keys = [] } = await client.request<{ keys?: { name: string }[] }>(\n\t\t'GET',\n\t\t`${IAM}/projects/${project}/serviceAccounts/${email}/keys?keyTypes=USER_MANAGED`,\n\t);\n\treturn keys.map(({ name }) => ({ name }));\n}\n\nexport async function deleteKey(\n\tclient: GoogleClient,\n\tkey: ServiceAccountKey,\n): Promise<void> {\n\tawait client.request('DELETE', `${IAM}/${key.name}`);\n}\n\n/** A key's id: the last part of its name. */\nexport function keyId(key: Pick<ServiceAccountKey, 'name'>): string {\n\treturn key.name.split('/').pop()!;\n}\n","/**\n * Play Console, through the Play Developer API (androidpublisher v3): the\n * service account's access to the app, the store listing as code, and a\n * check that the new key can act on the app.\n */\n\nimport { createSign } from 'node:crypto';\nimport { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';\nimport { extname, join } from 'node:path';\nimport type { PlayTrack } from '@geekmidas/manifest';\nimport { GkmError } from '../../errors';\nimport { GoogleApiError, type GoogleClient, googleClient } from './google.js';\n\nexport const PLAY =\n\t'https://androidpublisher.googleapis.com/androidpublisher/v3';\nexport const PLAY_UPLOAD =\n\t'https://androidpublisher.googleapis.com/upload/androidpublisher/v3';\n\n/** What Play answers about a package no app in the account has. */\nconst PACKAGE_MISSING = /packages are not available/i;\n\n/** The app is not in Play Console yet, and Play has no API that makes one. */\nexport class PlayAppMissing extends GkmError {\n\tconstructor(\n\t\treadonly packageName: string,\n\t\treadonly developer: string,\n\t) {\n\t\tsuper(\n\t\t\t`Play Console has no app '${packageName}', and Play has no API that ` +\n\t\t\t\t`creates one. Create it at https://play.google.com/console/developers/` +\n\t\t\t\t`${developer}/create-new-app, with ${packageName} as its package, then ` +\n\t\t\t\t'run this again.',\n\t\t);\n\t\tthis.name = 'PlayAppMissing';\n\t}\n}\n\n/** The new key could not open an edit on the app. */\nexport class PlayKeyUnverified extends GkmError {\n\tconstructor(\n\t\treadonly email: string,\n\t\treadonly detail: string,\n\t) {\n\t\tsuper(\n\t\t\t`The new key for ${email} could not open an edit on the app (${detail}). ` +\n\t\t\t\t'Its access can take a few minutes to reach Play: run this again shortly.',\n\t\t);\n\t\tthis.name = 'PlayKeyUnverified';\n\t}\n}\n\n/**\n * The app-level permissions the service account needs: releasing to the\n * testing tracks, to production only where a stage submits there, and the\n * store listing where the workspace keeps one.\n */\nexport function playPermissions(options: {\n\ttracks: readonly PlayTrack[];\n\tlisting: boolean;\n}): string[] {\n\treturn [\n\t\t'CAN_MANAGE_TRACK_APKS',\n\t\t...(options.tracks.includes('production')\n\t\t\t? ['CAN_MANAGE_PUBLIC_APKS']\n\t\t\t: []),\n\t\t...(options.listing ? ['CAN_MANAGE_PUBLIC_LISTING'] : []),\n\t].sort();\n}\n\ninterface PlayUser {\n\temail: string;\n\tgrants?: { packageName: string; appLevelPermissions?: string[] }[];\n}\n\n/**\n * The service account in Play Console with exactly `permissions` on the\n * app: invited with the grant in one call — Play refuses a user with no\n * permission, and a grant for a user not yet there — and, already there,\n * its grant made what it should be. Answers what changed.\n *\n * @throws {PlayAppMissing}\n */\nexport async function ensurePlayAccess(\n\tclient: GoogleClient,\n\toptions: {\n\t\tdeveloper: string;\n\t\temail: string;\n\t\tpackageName: string;\n\t\tpermissions: readonly string[];\n\t},\n): Promise<'invited' | 'granted' | 'updated' | 'unchanged'> {\n\tconst { developer, email, packageName } = options;\n\tconst permissions = [...options.permissions].sort();\n\tconst users = `${PLAY}/developers/${developer}/users`;\n\tconst missing = (error: unknown) =>\n\t\terror instanceof GoogleApiError && PACKAGE_MISSING.test(error.detail)\n\t\t\t? new PlayAppMissing(packageName, developer)\n\t\t\t: error;\n\n\t// Listing takes no paging: Play says \"Pagination is not currently\n\t// available\" unless asked for every user at once.\n\tconst { users: listed = [] } = await client.request<{ users?: PlayUser[] }>(\n\t\t'GET',\n\t\t`${users}?pageSize=-1`,\n\t);\n\tconst user = listed.find((candidate) => candidate.email === email);\n\n\tif (!user) {\n\t\tawait client\n\t\t\t.request('POST', users, {\n\t\t\t\temail,\n\t\t\t\tgrants: [{ packageName, appLevelPermissions: permissions }],\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tthrow missing(error);\n\t\t\t});\n\t\treturn 'invited';\n\t}\n\n\tconst grant = user.grants?.find((g) => g.packageName === packageName);\n\tconst grantPath = `${users}/${encodeURIComponent(email)}/grants`;\n\tif (!grant) {\n\t\tawait client\n\t\t\t.request('POST', grantPath, {\n\t\t\t\tpackageName,\n\t\t\t\tappLevelPermissions: permissions,\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tthrow missing(error);\n\t\t\t});\n\t\treturn 'granted';\n\t}\n\tconst had = [...(grant.appLevelPermissions ?? [])].sort();\n\tif (had.join() === permissions.join()) return 'unchanged';\n\tawait client.request(\n\t\t'PATCH',\n\t\t`${grantPath}/${packageName}?updateMask=appLevelPermissions`,\n\t\t{ appLevelPermissions: permissions },\n\t);\n\treturn 'updated';\n}\n\n/** A service account key file, as Google writes it. */\nexport interface ServiceAccountJson {\n\tclient_email: string;\n\tprivate_key: string;\n\ttoken_uri?: string;\n}\n\n/** An access token for the key itself: a signed JWT, exchanged. */\nexport async function keyToken(key: ServiceAccountJson): Promise<string> {\n\tconst tokenUri = key.token_uri ?? 'https://oauth2.googleapis.com/token';\n\tconst now = Math.floor(Date.now() / 1000);\n\tconst encode = (value: object) =>\n\t\tBuffer.from(JSON.stringify(value)).toString('base64url');\n\tconst unsigned = `${encode({ alg: 'RS256', typ: 'JWT' })}.${encode({\n\t\tiss: key.client_email,\n\t\tscope: 'https://www.googleapis.com/auth/androidpublisher',\n\t\taud: tokenUri,\n\t\tiat: now,\n\t\texp: now + 600,\n\t})}`;\n\tconst signature = createSign('RSA-SHA256')\n\t\t.update(unsigned)\n\t\t.sign(key.private_key)\n\t\t.toString('base64url');\n\tconst response = await fetch(tokenUri, {\n\t\tmethod: 'POST',\n\t\theaders: { 'content-type': 'application/x-www-form-urlencoded' },\n\t\tbody: new URLSearchParams({\n\t\t\tgrant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',\n\t\t\tassertion: `${unsigned}.${signature}`,\n\t\t}),\n\t});\n\tconst body = (await response.json().catch(() => ({}))) as {\n\t\taccess_token?: string;\n\t\terror_description?: string;\n\t};\n\tif (!response.ok || !body.access_token) {\n\t\tthrow new PlayKeyUnverified(\n\t\t\tkey.client_email,\n\t\t\tbody.error_description ?? `token exchange answered ${response.status}`,\n\t\t);\n\t}\n\treturn body.access_token;\n}\n\n/**\n * Proof the key and its grant work: as the key, open an edit on the app and\n * discard it — what EAS Submit's first call is.\n *\n * @throws {PlayKeyUnverified}\n */\nexport async function verifyKey(\n\tkey: ServiceAccountJson,\n\tpackageName: string,\n): Promise<void> {\n\tconst client = googleClient(await keyToken(key));\n\ttry {\n\t\tconst edit = await client.request<{ id: string }>(\n\t\t\t'POST',\n\t\t\t`${PLAY}/applications/${packageName}/edits`,\n\t\t\t{},\n\t\t);\n\t\tawait client.request(\n\t\t\t'DELETE',\n\t\t\t`${PLAY}/applications/${packageName}/edits/${edit.id}`,\n\t\t);\n\t} catch (error) {\n\t\tif (error instanceof GoogleApiError) {\n\t\t\tthrow new PlayKeyUnverified(key.client_email, error.detail);\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/** The image kinds Play's listing takes, as fastlane supply names them. */\nconst IMAGE_TYPES = [\n\t'icon',\n\t'featureGraphic',\n\t'promoGraphic',\n\t'tvBanner',\n\t'phoneScreenshots',\n\t'sevenInchScreenshots',\n\t'tenInchScreenshots',\n\t'tvScreenshots',\n\t'wearScreenshots',\n] as const;\n\n/** One locale's listing, read from `store/play/listings/<locale>/`. */\nexport interface PlayListing {\n\tlanguage: string;\n\ttitle?: string;\n\tshortDescription?: string;\n\tfullDescription?: string;\n\tvideo?: string;\n\t/** Each image kind's files, in order. */\n\timages: Partial<Record<(typeof IMAGE_TYPES)[number], string[]>>;\n}\n\n/** The store listing kept beside the app, in fastlane supply's layout. */\nexport interface PlayStore {\n\tlistings: PlayListing[];\n\t/** `store/play/data-safety.csv`: Play Console's data safety export. */\n\tdataSafety?: string;\n}\n\n/**\n * `<app>/store/play`, read: undefined where there is none.\n *\n * ```\n * store/play/\n * data-safety.csv\n * listings/en-US/title.txt, short_description.txt, full_description.txt, video.txt\n * listings/en-US/images/icon.png, featureGraphic.png\n * listings/en-US/images/phoneScreenshots/1.png, 2.png …\n * ```\n */\nexport function readPlayStore(appDir: string): PlayStore | undefined {\n\tconst root = join(appDir, 'store', 'play');\n\tif (!existsSync(root)) return undefined;\n\tconst text = (path: string) =>\n\t\texistsSync(path) ? readFileSync(path, 'utf8').trim() : undefined;\n\n\tconst listings: PlayListing[] = [];\n\tconst listingsDir = join(root, 'listings');\n\tfor (const language of existsSync(listingsDir)\n\t\t? readdirSync(listingsDir).sort()\n\t\t: []) {\n\t\tconst dir = join(listingsDir, language);\n\t\tif (!statSync(dir).isDirectory()) continue;\n\t\tconst images: PlayListing['images'] = {};\n\t\tconst imagesDir = join(dir, 'images');\n\t\tfor (const type of IMAGE_TYPES) {\n\t\t\tconst single = ['.png', '.jpg', '.jpeg']\n\t\t\t\t.map((ext) => join(imagesDir, `${type}${ext}`))\n\t\t\t\t.find(existsSync);\n\t\t\tconst many = join(imagesDir, type);\n\t\t\tconst files = single\n\t\t\t\t? [single]\n\t\t\t\t: existsSync(many)\n\t\t\t\t\t? readdirSync(many)\n\t\t\t\t\t\t\t.filter((f) => /\\.(png|jpe?g)$/i.test(f))\n\t\t\t\t\t\t\t.sort()\n\t\t\t\t\t\t\t.map((f) => join(many, f))\n\t\t\t\t\t: [];\n\t\t\tif (files.length > 0) images[type] = files;\n\t\t}\n\t\tconst listing: PlayListing = { language, images };\n\t\tconst title = text(join(dir, 'title.txt'));\n\t\tconst shortDescription = text(join(dir, 'short_description.txt'));\n\t\tconst fullDescription = text(join(dir, 'full_description.txt'));\n\t\tconst video = text(join(dir, 'video.txt'));\n\t\tif (title) listing.title = title;\n\t\tif (shortDescription) listing.shortDescription = shortDescription;\n\t\tif (fullDescription) listing.fullDescription = fullDescription;\n\t\tif (video) listing.video = video;\n\t\tlistings.push(listing);\n\t}\n\n\tconst dataSafety = text(join(root, 'data-safety.csv'));\n\treturn { listings, ...(dataSafety ? { dataSafety } : {}) };\n}\n\n/**\n * Push the listing through one edit, committed: each locale's text, and each\n * image kind replaced by the files kept. Then the data safety form, which\n * Play takes outside an edit. Answers whether the listing was pushed: an\n * app that has never had an upload may refuse an edit, and that is left for\n * the next run, not failed.\n */\nexport async function pushPlayStore(\n\tclient: GoogleClient,\n\tpackageName: string,\n\tstore: PlayStore,\n): Promise<{ listing: boolean; dataSafety: boolean; reason?: string }> {\n\tconst app = `${PLAY}/applications/${packageName}`;\n\tlet listing = false;\n\tlet reason: string | undefined;\n\tif (store.listings.length > 0) {\n\t\tlet edit: { id: string } | undefined;\n\t\ttry {\n\t\t\tedit = await client.request<{ id: string }>('POST', `${app}/edits`, {});\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof GoogleApiError)) throw error;\n\t\t\treason = error.detail;\n\t\t}\n\t\tif (edit) {\n\t\t\tconst base = `${app}/edits/${edit.id}/listings`;\n\t\t\tfor (const entry of store.listings) {\n\t\t\t\tconst { images, ...text } = entry;\n\t\t\t\tif (text.title || text.shortDescription || text.fullDescription) {\n\t\t\t\t\tawait client.request('PUT', `${base}/${entry.language}`, text);\n\t\t\t\t}\n\t\t\t\tfor (const [type, files] of Object.entries(images)) {\n\t\t\t\t\tawait client.request('DELETE', `${base}/${entry.language}/${type}`);\n\t\t\t\t\tfor (const file of files ?? []) {\n\t\t\t\t\t\tawait client.request(\n\t\t\t\t\t\t\t'POST',\n\t\t\t\t\t\t\t`${PLAY_UPLOAD}/applications/${packageName}/edits/${edit.id}/listings/${entry.language}/${type}?uploadType=media`,\n\t\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\traw: {\n\t\t\t\t\t\t\t\t\tcontentType:\n\t\t\t\t\t\t\t\t\t\textname(file).toLowerCase() === '.png'\n\t\t\t\t\t\t\t\t\t\t\t? 'image/png'\n\t\t\t\t\t\t\t\t\t\t\t: 'image/jpeg',\n\t\t\t\t\t\t\t\t\tdata: readFileSync(file),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait client.request('POST', `${app}/edits/${edit.id}:commit`);\n\t\t\tlisting = true;\n\t\t}\n\t}\n\n\tlet dataSafety = false;\n\tif (store.dataSafety) {\n\t\tawait client.request('POST', `${app}/dataSafety`, {\n\t\t\tsafetyLabels: store.dataSafety,\n\t\t});\n\t\tdataSafety = true;\n\t}\n\treturn { listing, dataSafety, ...(reason ? { reason } : {}) };\n}\n\n/** What Play Console only takes by hand, for the person to tick off. */\nexport const PLAY_CONSOLE_CHECKLIST = [\n\t'App content → Privacy policy: the URL of the published policy',\n\t'App content → App access: how a reviewer signs in',\n\t'App content → Ads: whether the app shows ads',\n\t'App content → Content rating: the questionnaire',\n\t'App content → Target audience and content',\n\t'App content → Health apps / Health Connect, where the app reads health data',\n\t'App content → Sensitive permissions, where the app asks for any',\n\t'Release → the first upload of a new app, by hand',\n];\n","/**\n * `gkm deploy:google-play`: what EAS Submit needs to publish a `MobileApp`\n * to Google Play, set up in one run — the counterpart of `gkm deploy:github`\n * for the AWS side.\n *\n * 1. Each deployed stage's track and release status into `eas.json`\n * (`submit.<stage>.android`), from `mobileStage`.\n * 2. Google: the project (`--project`, else `<workspace>-play`, made when\n * absent) with the Android Publisher API on, and the service account\n * `<workspace>-play` in it, found or made.\n * 3. Play Console: the account invited with a grant on the app alone, its\n * permissions following the stages' tracks — production releases only\n * when a stage submits there. An app not in Play Console yet is a step\n * only a person can take; interactively, the run says so and waits.\n * 4. A new JSON key, held in memory: uploaded to EAS, assigned to the app's\n * Android credentials for EAS Submit, and proved by opening and\n * discarding an edit with it. Only then are the account's older keys\n * deleted, from EAS and from Google — a failed run leaves the old key\n * working.\n * 5. The store listing kept beside the app (`store/play/`), pushed.\n * 6. What Play Console only takes by hand, as a checklist.\n *\n * Running it again rotates the key and pushes the listing.\n */\n\nimport { spawn } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { readFile, writeFile } from 'node:fs/promises';\nimport { delimiter, join } from 'node:path';\nimport { loadWorkspaceConfig } from '../../config.js';\nimport { GkmError } from '../../errors';\nimport { loadStage } from '../../exec/stage.js';\nimport { canPrompt } from '../../prompt.js';\nimport { chooseMobileApp, withStageProfiles } from '../eas.js';\nimport { mobileStage } from '../stage.js';\nimport { type EasApi, easApi } from './easApi.js';\nimport {\n\tassertGoogleId,\n\tcreateKey,\n\tdeleteKey,\n\tenablePublisherApi,\n\tensureProject,\n\tensureServiceAccount,\n\tkeyId,\n\tlistKeys,\n\tplayId,\n} from './gcp.js';\nimport {\n\ttype GoogleClient,\n\ttype GoogleSignIn,\n\tgcloudSignIn,\n\tgoogleClient,\n} from './google.js';\nimport {\n\tensurePlayAccess,\n\tPLAY_CONSOLE_CHECKLIST,\n\tPlayAppMissing,\n\tPlayKeyUnverified,\n\tplayPermissions,\n\tpushPlayStore,\n\treadPlayStore,\n\ttype ServiceAccountJson,\n\tverifyKey,\n} from './play.js';\n\nconst logger = console;\n\n/** No Play developer account to act in. */\nexport class PlayDeveloperMissing extends GkmError {\n\tconstructor() {\n\t\tsuper(\n\t\t\t'Name the Play developer account: --developer <id>. It is the number in ' +\n\t\t\t\t\"Play Console's address: https://play.google.com/console/developers/<id>/….\",\n\t\t);\n\t\tthis.name = 'PlayDeveloperMissing';\n\t}\n}\n\n/** The app's Expo config has no Android package, or no EAS project. */\nexport class ExpoConfigIncomplete extends GkmError {\n\tconstructor(\n\t\treadonly app: string,\n\t\treadonly missing: 'android.package' | 'extra.eas.projectId',\n\t) {\n\t\tsuper(\n\t\t\tmissing === 'android.package'\n\t\t\t\t? `'${app}' has no android.package in its Expo config: Play knows an app ` +\n\t\t\t\t\t\t\"by its package. Set it in app.config.ts: android: { package: 'com.acme.shop' }.\"\n\t\t\t\t: `'${app}' has no extra.eas.projectId in its Expo config: it is not ` +\n\t\t\t\t\t\t'linked to an EAS project. Run `eas init` in the app.',\n\t\t);\n\t\tthis.name = 'ExpoConfigIncomplete';\n\t}\n}\n\n/** `expo config` could not read the app's config. */\nexport class ExpoConfigFailed extends GkmError {\n\tconstructor(\n\t\treadonly dir: string,\n\t\treadonly detail: string,\n\t) {\n\t\tsuper(\n\t\t\t`Could not read the Expo config in ${dir} (${detail}). Check that ` +\n\t\t\t\t'`npx expo config --json` works there.',\n\t\t);\n\t\tthis.name = 'ExpoConfigFailed';\n\t}\n}\n\n/** The workspace deploys no stage. */\nexport class NoDeployedStage extends GkmError {\n\tconstructor() {\n\t\tsuper(\n\t\t\t'gkm.config.ts deploys no stage, so there is nothing to publish to: add one to stages.deployed.',\n\t\t);\n\t\tthis.name = 'NoDeployedStage';\n\t}\n}\n\n/** What the run needs of the app's Expo config. */\nexport interface ExpoAppConfig {\n\tname?: string;\n\tandroid?: { package?: string };\n\textra?: { eas?: { projectId?: string } };\n}\n\nexport interface GooglePlayDeps {\n\tsignIn: GoogleSignIn;\n\teas: EasApi;\n\t/** The app's Expo config, evaluated with the values its build reads. */\n\texpoConfig: (\n\t\tdir: string,\n\t\troot: string,\n\t\tenv: Readonly<Record<string, string>>,\n\t) => Promise<ExpoAppConfig>;\n\t/** Whether a person is here: to sign in, and to create the app. */\n\tinteractive: boolean;\n\t/** Wait between two looks for the app, and two tries of the new key. */\n\tsleep: (ms: number) => Promise<void>;\n}\n\n/** `expo config --json`, run with the app's Expo — its own, or the workspace's. */\nexport function expoConfig(\n\tdir: string,\n\troot: string,\n\tenv: Readonly<Record<string, string>>,\n): Promise<ExpoAppConfig> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst child = spawn('expo', ['config', '--json', '--type', 'public'], {\n\t\t\tcwd: dir,\n\t\t\tstdio: ['ignore', 'pipe', 'pipe'],\n\t\t\tenv: {\n\t\t\t\t...process.env,\n\t\t\t\t...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});\n\t\tchild.on('error', (error) =>\n\t\t\treject(new ExpoConfigFailed(dir, error.message)),\n\t\t);\n\t\tchild.on('close', (code) => {\n\t\t\tif (code !== 0) {\n\t\t\t\treject(new ExpoConfigFailed(dir, stderr.trim() || `exit ${code}`));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tresolve(JSON.parse(stdout) as ExpoAppConfig);\n\t\t\t} catch {\n\t\t\t\treject(new ExpoConfigFailed(dir, `no JSON: ${stdout.slice(0, 200)}`));\n\t\t\t}\n\t\t});\n\t});\n}\n\nexport const defaultGooglePlayDeps: GooglePlayDeps = {\n\tsignIn: gcloudSignIn(),\n\teas: easApi(),\n\texpoConfig,\n\tinteractive: canPrompt(),\n\tsleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),\n};\n\nexport interface GooglePlayOptions {\n\t/** The mobile app; the one here, or the only one, if not. */\n\tapp?: string;\n\t/** The Google Cloud project: `<workspace>-play` by default. */\n\tproject?: string;\n\t/** The Play developer account's id. */\n\tdeveloper?: string;\n\t/**\n\t * The stage whose values evaluate the app's Expo config: the protected\n\t * one, else the first deployed.\n\t */\n\tstage?: string;\n\t/** The AWS profile for that stage's account, where its secrets are kept. */\n\tprofile?: string;\n\tcwd?: string;\n}\n\nexport interface GooglePlayResult {\n\tpackageName: string;\n\tproject: string;\n\temail: string;\n\tpermissions: string[];\n\taccess: 'invited' | 'granted' | 'updated' | 'unchanged';\n\t/** The new key's id, in Google and in EAS. */\n\tkey: { google: string; eas: string };\n\t/** The older keys deleted. */\n\tdeleted: { google: string[]; eas: string[] };\n\tlisting:\n\t\t| { listing: boolean; dataSafety: boolean; reason?: string }\n\t\t| undefined;\n}\n\n/**\n * Set up publishing a mobile app to Google Play.\n *\n * @throws {PlayDeveloperMissing}, {ExpoConfigIncomplete}, {PlayAppMissing},\n * {PlayKeyUnverified} — and what each step's API throws\n */\nexport async function deployGooglePlay(\n\toptions: GooglePlayOptions,\n\tdeps: GooglePlayDeps = defaultGooglePlayDeps,\n): Promise<GooglePlayResult> {\n\tif (!options.developer) throw new PlayDeveloperMissing();\n\tconst developer = options.developer;\n\n\t// The stage only evaluates the Expo config: any deployed one's values do.\n\tconst declared = (await loadWorkspaceConfig(options.cwd)).workspace.stages;\n\tconst stageName =\n\t\toptions.stage ?? declared.protected?.[0] ?? declared.deployed[0];\n\tif (!stageName) throw new NoDeployedStage();\n\tconst loaded = await loadStage({\n\t\tstage: stageName,\n\t\t...(options.cwd ? { cwd: options.cwd } : {}),\n\t\t...(options.profile ? { profile: options.profile } : {}),\n\t});\n\tconst { workspace, manifest, stack } = loaded;\n\tconst declaration = chooseMobileApp(manifest, options.app, loaded.appName);\n\tconst name = declaration.id;\n\tconst dir = join(workspace.root, declaration.app.path);\n\tconst values =\n\t\tstack.mobileApps.find((candidate) => candidate.id === declaration.id)\n\t\t\t?.values ?? {};\n\n\tconst config = await deps.expoConfig(dir, workspace.root, values);\n\tconst packageName = config.android?.package;\n\tif (!packageName) throw new ExpoConfigIncomplete(name, 'android.package');\n\tconst projectId = config.extra?.eas?.projectId;\n\tif (!projectId) throw new ExpoConfigIncomplete(name, 'extra.eas.projectId');\n\n\t// 1. Each stage's track, in eas.json — and the tracks the grant follows.\n\tconst stages = workspace.stages.deployed.map((stage) =>\n\t\tmobileStage(workspace, declaration, stage),\n\t);\n\tconst easJsonPath = join(dir, 'eas.json');\n\tconst before = existsSync(easJsonPath)\n\t\t? JSON.parse(await readFile(easJsonPath, 'utf8'))\n\t\t: {};\n\tconst after = stages.reduce(withStageProfiles, before);\n\tif (JSON.stringify(before) !== JSON.stringify(after)) {\n\t\tawait writeFile(easJsonPath, `${JSON.stringify(after, null, 2)}\\n`);\n\t\tlogger.log(`📝 ${easJsonPath}: each stage's build and submit profiles`);\n\t}\n\tconst store = readPlayStore(dir);\n\tconst permissions = playPermissions({\n\t\ttracks: stages.map((stage) => stage.android.track),\n\t\tlisting: (store?.listings.length ?? 0) > 0,\n\t});\n\n\t// 2. Google: signed in as a Play Console admin, the project and account.\n\tconst project = options.project ?? playId(workspace.name);\n\tassertGoogleId('project', project);\n\tconst account = playId(workspace.name);\n\tconst token = await deps.signIn.token({ interactive: deps.interactive });\n\tconst bare: GoogleClient = googleClient(token);\n\tconst created = await ensureProject(bare, project, `${workspace.name} Play`);\n\tlogger.log(\n\t\t`☁️ Google Cloud project ${project}${created ? ' (created)' : ''}`,\n\t);\n\tconst client = googleClient(token, project);\n\tawait enablePublisherApi(client, project);\n\tconst email = await ensureServiceAccount(\n\t\tclient,\n\t\tproject,\n\t\taccount,\n\t\t`${workspace.name} publishing (gkm)`,\n\t);\n\tlogger.log(`🤖 Service account ${email}`);\n\n\t// 3. Play Console: the app grant, waiting for the app where someone can\n\t// make it.\n\tlet access: GooglePlayResult['access'] | undefined;\n\tfor (let attempt = 0; !access; attempt++) {\n\t\ttry {\n\t\t\taccess = await ensurePlayAccess(client, {\n\t\t\t\tdeveloper,\n\t\t\t\temail,\n\t\t\t\tpackageName,\n\t\t\t\tpermissions,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof PlayAppMissing) || !deps.interactive) throw error;\n\t\t\tif (attempt === 0) {\n\t\t\t\tlogger.log(`\\n⏳ ${error.message}\\n Waiting for it…`);\n\t\t\t}\n\t\t\tawait deps.sleep(15_000);\n\t\t}\n\t}\n\tlogger.log(\n\t\t`🎟️ Play access on ${packageName}: ${access} (${permissions.join(', ')})`,\n\t);\n\n\t// 4. A new key: to EAS, proved, and only then the old ones gone.\n\tconst older = await listKeys(client, project, email);\n\tconst key = await createKey(client, project, email);\n\tconst json = JSON.parse(key.json) as ServiceAccountJson &\n\t\tRecord<string, unknown>;\n\tconst easProject = await deps.eas.project(projectId);\n\tconst credentials = await deps.eas.androidCredentials(\n\t\t{ projectId, fullName: easProject.fullName },\n\t\tpackageName,\n\t);\n\tconst easKey = await deps.eas.createKey(easProject.accountId, json);\n\tawait deps.eas.useForSubmissions(credentials.id, easKey.id);\n\tlogger.log(\n\t\t`🔑 New key ${keyId(key)} assigned to EAS Submit for ${packageName}`,\n\t);\n\n\t// A new grant takes a moment to reach Play.\n\tfor (let attempt = 0; ; attempt++) {\n\t\ttry {\n\t\t\tawait verifyKey(json, packageName);\n\t\t\tbreak;\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof PlayKeyUnverified) || attempt >= 5) throw error;\n\t\t\tawait deps.sleep(10_000);\n\t\t}\n\t}\n\tlogger.log('✅ The key opens an edit on the app');\n\n\tconst easOlder = (await deps.eas.keys(easProject.accountName)).filter(\n\t\t(candidate) =>\n\t\t\tcandidate.clientEmail === email && candidate.id !== easKey.id,\n\t);\n\tfor (const old of easOlder) await deps.eas.deleteKey(old.id);\n\tfor (const old of older) await deleteKey(client, old);\n\tif (older.length + easOlder.length > 0) {\n\t\tlogger.log(\n\t\t\t`🧹 Deleted ${older.length} older key(s) from Google, ${easOlder.length} from EAS`,\n\t\t);\n\t}\n\n\t// 5. The listing, where the workspace keeps one.\n\tconst listing = store\n\t\t? await pushPlayStore(client, packageName, store)\n\t\t: undefined;\n\tif (listing?.listing) logger.log('🛍️ Store listing pushed');\n\tif (store && !listing?.listing && listing?.reason) {\n\t\tlogger.log(\n\t\t\t`⏭️ Store listing not pushed yet (${listing.reason}): run this again after the app's first upload`,\n\t\t);\n\t}\n\tif (listing?.dataSafety) logger.log('🛡️ Data safety pushed');\n\n\t// 6. What only a person can do.\n\tlogger.log('\\nLeft to do in Play Console, by hand:');\n\tfor (const item of PLAY_CONSOLE_CHECKLIST) logger.log(` [ ] ${item}`);\n\n\treturn {\n\t\tpackageName,\n\t\tproject,\n\t\temail,\n\t\tpermissions,\n\t\taccess,\n\t\tkey: { google: keyId(key), eas: easKey.id },\n\t\tdeleted: {\n\t\t\tgoogle: older.map(keyId),\n\t\t\teas: easOlder.map((candidate) => candidate.id),\n\t\t},\n\t\tlisting,\n\t};\n}\n"],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -4,25 +4,25 @@ const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
4
4
  const require_errors = require("./errors-DMC4Umsb.cjs");
5
5
  const require_run = require("./run-fVZ-rLnc.cjs");
6
6
  const require_credentials = require("./credentials-Zqr3QQYB.cjs");
7
- const require_compose = require("./compose-DN9zySvQ.cjs");
7
+ const require_compose = require("./compose-ByIbIrzs.cjs");
8
8
  const require_workspace = require("./workspace-BwqQnool.cjs");
9
- const require_dns = require("./dns-BdETzP10.cjs");
10
- const require_seeds = require("./seeds-DNqRA1aQ.cjs");
9
+ const require_dns = require("./dns-C2TGXUF4.cjs");
10
+ const require_seeds = require("./seeds-Dk8YkLkM.cjs");
11
11
  const require_derive = require("./derive-DAbo49sm.cjs");
12
12
  const require_SubscriberGenerator = require("./SubscriberGenerator-CeH7wbYt.cjs");
13
13
  const require_identity = require("./identity-CbLfgQNT.cjs");
14
14
  const require_EndpointGenerator = require("./EndpointGenerator-BwbYTMtB.cjs");
15
15
  const require_discover = require("./discover-Bsr7HjqJ.cjs");
16
16
  const require_config = require("./config.cjs");
17
- const require_deploy = require("./deploy-CFvq8daZ.cjs");
17
+ const require_deploy = require("./deploy-Dl_NavYE.cjs");
18
18
  const require_openapi = require("./openapi.cjs");
19
19
  const require_appPorts = require("./appPorts-IW-OZmRi.cjs");
20
20
  const require_drivers = require("./drivers-GKaA6ITM.cjs");
21
21
  const require_emit = require("./emit-Dmc1nvdS.cjs");
22
22
  const require_local = require("./local-DNhnkugT.cjs");
23
- const require_sniffer = require("./sniffer-DipSS8r8.cjs");
23
+ const require_sniffer = require("./sniffer-2aK7cB2u.cjs");
24
24
  const require_credentialSchemas = require("./credentialSchemas-D_VUCDUh.cjs");
25
- const require_LocalStateStore = require("./LocalStateStore-Bm8Vmwjb.cjs");
25
+ const require_LocalStateStore = require("./LocalStateStore-AtviUBky.cjs");
26
26
  const require_utils = require("./utils-D2Nk1Nv2.cjs");
27
27
  const require_ready = require("./ready-BMFjxBYA.cjs");
28
28
  let node_path = require("node:path");
@@ -57,7 +57,7 @@ crypto = require_rolldown_runtime.__toESM(crypto, 1);
57
57
  let _geekmidas_constructs_functions = require("@geekmidas/constructs/functions");
58
58
  let _geekmidas_constructs_topic = require("@geekmidas/constructs/topic");
59
59
  //#region package.json
60
- var version = "10.0.0-alpha.110";
60
+ var version = "10.0.0-alpha.111";
61
61
  //#endregion
62
62
  //#region src/prompt.ts
63
63
  /**
@@ -140,7 +140,7 @@ const logger$8 = console;
140
140
  * Validate Dokploy token by making a test API call
141
141
  */
142
142
  async function validateDokployToken(endpoint, token) {
143
- const { DokployApi } = await Promise.resolve().then(() => require("./sniffer-DipSS8r8.cjs")).then((n) => n.dokploy_api_exports);
143
+ const { DokployApi } = await Promise.resolve().then(() => require("./sniffer-2aK7cB2u.cjs")).then((n) => n.dokploy_api_exports);
144
144
  return new DokployApi({
145
145
  baseUrl: endpoint,
146
146
  token
@@ -2180,7 +2180,7 @@ async function execCommand(commandArgs, options = {}, stageDeps) {
2180
2180
  const cwd = options.cwd ?? process.cwd();
2181
2181
  if (!commandArgs[0]) throw new NoCommandSpecified();
2182
2182
  if (options.stage && !require_seeds.isImageBuild()) {
2183
- const { execOnStage } = await Promise.resolve().then(() => require("./stage-C_y2UovO.cjs"));
2183
+ const { execOnStage } = await Promise.resolve().then(() => require("./stage-DrCS0vKD.cjs")).then((n) => n.stage_exports);
2184
2184
  const { loadWorkspaceConfig } = await Promise.resolve().then(() => require("./config.cjs"));
2185
2185
  const { workspace } = await loadWorkspaceConfig(cwd);
2186
2186
  if (options.stage !== workspace.stages.local) {
@@ -2753,9 +2753,9 @@ async function workspaceDevCommand(configured, options) {
2753
2753
  for (const { id, port } of fakes) logger$5.log(`🎭 ${id} fake: http://localhost:${port}`);
2754
2754
  logger$5.log(options.migrate || options.seed ? "🗄️ Migrating the databases" : "🗄️ Checking for pending migrations");
2755
2755
  if (options.migrate || options.seed) {
2756
- const { prepareDevDatabases } = await Promise.resolve().then(() => require("./migrate-CTqhWtev.cjs"));
2756
+ const { prepareDevDatabases } = await Promise.resolve().then(() => require("./migrate-bDQzFvBN.cjs"));
2757
2757
  await prepareDevDatabases(workspace, reconciled.env, { seed: !!options.seed });
2758
- } else await Promise.resolve().then(() => require("./migrate-CTqhWtev.cjs")).then(({ reportPendingMigrations }) => reportPendingMigrations(workspace, reconciled.env)).catch((error) => {
2758
+ } else await Promise.resolve().then(() => require("./migrate-bDQzFvBN.cjs")).then(({ reportPendingMigrations }) => reportPendingMigrations(workspace, reconciled.env)).catch((error) => {
2759
2759
  logger$5.log(`⚠️ Could not check for pending migrations: ${error instanceof Error ? error.message : String(error)}`);
2760
2760
  });
2761
2761
  const appUrls = {};
@@ -3831,7 +3831,7 @@ async function buildApp(input) {
3831
3831
  ...production,
3832
3832
  ...telemetry ? { telemetry } : {}
3833
3833
  });
3834
- const { bundleServer } = await Promise.resolve().then(() => require("./bundler-B4GDxImV.cjs"));
3834
+ const { bundleServer } = await Promise.resolve().then(() => require("./bundler-NrtItVBp.cjs"));
3835
3835
  await bundleServer({
3836
3836
  entryPoint,
3837
3837
  outputDir: (0, node_path.join)(outputDir, "dist"),
@@ -4030,7 +4030,7 @@ async function buildWorkers(input) {
4030
4030
  });
4031
4031
  logger$4.log(`Generated worker ${worker.id}: ${crons.length} crons, ${queues.length} queues, ${subscribers.length} subscribers`);
4032
4032
  if (!input.bundle) continue;
4033
- const { bundleServer } = await Promise.resolve().then(() => require("./bundler-B4GDxImV.cjs"));
4033
+ const { bundleServer } = await Promise.resolve().then(() => require("./bundler-NrtItVBp.cjs"));
4034
4034
  const outfile = require_seeds.workerBundleName(worker.id);
4035
4035
  await bundleServer({
4036
4036
  entryPoint,
@@ -4116,7 +4116,7 @@ async function buildForTarget(target, context, appRoot, workspaceRoot, endpointG
4116
4116
  let masterKey;
4117
4117
  if (context.production?.bundle && !skipBundle) {
4118
4118
  logger$4.log(`\n📦 Bundling production server...`);
4119
- const { bundleServer } = await Promise.resolve().then(() => require("./bundler-B4GDxImV.cjs"));
4119
+ const { bundleServer } = await Promise.resolve().then(() => require("./bundler-NrtItVBp.cjs"));
4120
4120
  const allConstructs = [
4121
4121
  ...endpoints.map((e) => e.construct),
4122
4122
  ...functions.map((f) => f.construct),
@@ -5949,25 +5949,25 @@ function loadPackageJson() {
5949
5949
  * Run: pnpm --filter @geekmidas/cli sync-versions
5950
5950
  */
5951
5951
  const GEEKMIDAS_VERSIONS = {
5952
- "@geekmidas/audit": "~10.0.0-alpha.110",
5953
- "@geekmidas/auth": "~10.0.0-alpha.110",
5954
- "@geekmidas/cache": "~10.0.0-alpha.110",
5955
- "@geekmidas/client": "~10.0.0-alpha.110",
5956
- "@geekmidas/cloud": "~10.0.0-alpha.110",
5957
- "@geekmidas/constructs": "~10.0.0-alpha.110",
5958
- "@geekmidas/db": "~10.0.0-alpha.110",
5959
- "@geekmidas/emailkit": "~10.0.0-alpha.110",
5960
- "@geekmidas/envkit": "~10.0.0-alpha.110",
5961
- "@geekmidas/errors": "~10.0.0-alpha.110",
5962
- "@geekmidas/events": "~10.0.0-alpha.110",
5963
- "@geekmidas/logger": "~10.0.0-alpha.110",
5964
- "@geekmidas/manifest": "~10.0.0-alpha.110",
5965
- "@geekmidas/rate-limit": "~10.0.0-alpha.110",
5966
- "@geekmidas/schema": "~10.0.0-alpha.110",
5967
- "@geekmidas/services": "~10.0.0-alpha.110",
5968
- "@geekmidas/storage": "~10.0.0-alpha.110",
5969
- "@geekmidas/telescope": "~10.0.0-alpha.110",
5970
- "@geekmidas/testkit": "~10.0.0-alpha.110",
5952
+ "@geekmidas/audit": "~10.0.0-alpha.111",
5953
+ "@geekmidas/auth": "~10.0.0-alpha.111",
5954
+ "@geekmidas/cache": "~10.0.0-alpha.111",
5955
+ "@geekmidas/client": "~10.0.0-alpha.111",
5956
+ "@geekmidas/cloud": "~10.0.0-alpha.111",
5957
+ "@geekmidas/constructs": "~10.0.0-alpha.111",
5958
+ "@geekmidas/db": "~10.0.0-alpha.111",
5959
+ "@geekmidas/emailkit": "~10.0.0-alpha.111",
5960
+ "@geekmidas/envkit": "~10.0.0-alpha.111",
5961
+ "@geekmidas/errors": "~10.0.0-alpha.111",
5962
+ "@geekmidas/events": "~10.0.0-alpha.111",
5963
+ "@geekmidas/logger": "~10.0.0-alpha.111",
5964
+ "@geekmidas/manifest": "~10.0.0-alpha.111",
5965
+ "@geekmidas/rate-limit": "~10.0.0-alpha.111",
5966
+ "@geekmidas/schema": "~10.0.0-alpha.111",
5967
+ "@geekmidas/services": "~10.0.0-alpha.111",
5968
+ "@geekmidas/storage": "~10.0.0-alpha.111",
5969
+ "@geekmidas/telescope": "~10.0.0-alpha.111",
5970
+ "@geekmidas/testkit": "~10.0.0-alpha.111",
5971
5971
  "@geekmidas/cli": `~${loadPackageJson().version}`
5972
5972
  };
5973
5973
  //#endregion
@@ -7160,7 +7160,7 @@ const COMMIT = /^[0-9a-f]{40}$/;
7160
7160
  * The `uses:` value for the stages action, with a comment saying which CLI
7161
7161
  * release the pinned commit is.
7162
7162
  */
7163
- function stagesActionUses(commit = "1175be1a09457cf9cf3bfb9add2c61969c829df8", version = cliVersion()) {
7163
+ function stagesActionUses(commit = "8a63c47fd347367a03f2dfe1c3ea031556ede424", version = cliVersion()) {
7164
7164
  return commit && COMMIT.test(commit) ? `${STAGES_ACTION}@${commit} # @geekmidas/cli ${version}` : `${STAGES_ACTION}@main # pin to a commit SHA: see the action's README`;
7165
7165
  }
7166
7166
  //#endregion
@@ -7659,6 +7659,26 @@ ${registryLogin(registry)}
7659
7659
  SHA: \${{ steps.commit.outputs.sha }}
7660
7660
  GODADDY_API_TOKEN: \${{ secrets.GODADDY_API_TOKEN }}
7661
7661
  HOSTINGER_API_TOKEN: \${{ secrets.HOSTINGER_API_TOKEN }}
7662
+ ${options.mobileApp ? publishMobile(pm.exec) : ""}`;
7663
+ }
7664
+ /**
7665
+ * The phone app, once its stage's servers run this release: its values
7666
+ * synced to the stage's EAS environment, then an update on the stage's
7667
+ * channel when a store build has its native code, or a store build
7668
+ * submitted to the stores when none does (\`gkm publish\`). The build runs
7669
+ * on EAS; this step only starts it.
7670
+ */
7671
+ function publishMobile(exec) {
7672
+ return `
7673
+ # The phone app, after its servers: an update when a store build has
7674
+ # this native code, a store build submitted to the stores when none
7675
+ # does. EXPO_TOKEN is a secret on the stage's environment (an EAS
7676
+ # access token); the build runs on EAS, not here.
7677
+ - name: Publish the app
7678
+ run: ${exec} gkm publish --stage "$STAGE"
7679
+ env:
7680
+ STAGE: \${{ matrix.stage }}
7681
+ EXPO_TOKEN: \${{ secrets.EXPO_TOKEN }}
7662
7682
  `;
7663
7683
  }
7664
7684
  /**
@@ -7764,6 +7784,42 @@ function generateGithubFiles(options) {
7764
7784
  return files;
7765
7785
  }
7766
7786
  //#endregion
7787
+ //#region src/mobile/stage.ts
7788
+ /** A `MobileApp`'s `stages` names a stage the project does not deploy. */
7789
+ var MobileAppStageUndeclared = class extends require_errors.GkmError {
7790
+ app;
7791
+ stage;
7792
+ deployed;
7793
+ constructor(app, stage, deployed) {
7794
+ super(`'${app}' says how the stage '${stage}' ships it, and '${stage}' is not a deployed stage (gkm.config.ts deploys ${deployed.join(", ") || "nothing"}). Rename it in the MobileApp's stages, or add it to stages.deployed.`);
7795
+ this.app = app;
7796
+ this.stage = stage;
7797
+ this.deployed = deployed;
7798
+ this.name = "MobileAppStageUndeclared";
7799
+ }
7800
+ };
7801
+ /**
7802
+ * `stage`'s way of shipping `app`.
7803
+ *
7804
+ * @throws {MobileAppStageUndeclared} when the app's `stages` names a stage
7805
+ * the workspace does not deploy
7806
+ */
7807
+ function mobileStage(workspace, app, stage) {
7808
+ const { deployed } = workspace.stages;
7809
+ for (const named of Object.keys(app.stages ?? {})) if (!deployed.includes(named)) throw new MobileAppStageUndeclared(app.id, named, deployed);
7810
+ const users = workspace.stages.protected?.includes(stage) ?? false;
7811
+ const given = app.stages?.[stage];
7812
+ return {
7813
+ stage,
7814
+ environment: given?.environment ?? (users ? "production" : "preview"),
7815
+ channel: stage,
7816
+ android: {
7817
+ track: given?.android?.track ?? (users ? "production" : "internal"),
7818
+ releaseStatus: given?.android?.releaseStatus ?? "draft"
7819
+ }
7820
+ };
7821
+ }
7822
+ //#endregion
7767
7823
  //#region src/init/generators/mobile-expo.ts
7768
7824
  /**
7769
7825
  * The fullstack template's mobile app, when it has one (`mobileApp`).
@@ -7903,6 +7959,14 @@ export default function expoConfig({ config: base }: ConfigContext): ExpoConfig
7903
7959
  };
7904
7960
  }
7905
7961
  `;
7962
+ const mobile = {
7963
+ kind: "mobile-app",
7964
+ id: "App",
7965
+ variant: "expo",
7966
+ app: { path: "apps/app" },
7967
+ dependencies: []
7968
+ };
7969
+ const stages = options.stages.deployed.map((stage) => mobileStage({ stages: options.stages }, mobile, stage));
7906
7970
  const easJson = {
7907
7971
  cli: {
7908
7972
  version: ">= 10.2.1",
@@ -7914,25 +7978,16 @@ export default function expoConfig({ config: base }: ConfigContext): ExpoConfig
7914
7978
  distribution: "internal",
7915
7979
  environment: "development"
7916
7980
  },
7917
- preview: {
7918
- distribution: "internal",
7919
- environment: "preview",
7920
- env: {
7921
- APP_SCHEME: scheme,
7922
- EXPO_PUBLIC_API_URL: "https://api.example.com",
7923
- EXPO_PUBLIC_AUTH_URL: "https://auth.example.com"
7924
- }
7925
- },
7926
- production: {
7981
+ ...Object.fromEntries(stages.map((stage) => [stage.stage, {
7927
7982
  autoIncrement: true,
7928
- environment: "production",
7929
- env: {
7930
- APP_SCHEME: scheme,
7931
- EXPO_PUBLIC_API_URL: "https://api.example.com",
7932
- EXPO_PUBLIC_AUTH_URL: "https://auth.example.com"
7933
- }
7934
- }
7935
- }
7983
+ environment: stage.environment,
7984
+ channel: stage.channel
7985
+ }]))
7986
+ },
7987
+ submit: Object.fromEntries(stages.map((stage) => [stage.stage, { android: {
7988
+ track: stage.android.track,
7989
+ releaseStatus: stage.android.releaseStatus
7990
+ } }]))
7936
7991
  };
7937
7992
  const babelConfig = `module.exports = function (api) {
7938
7993
  api.cache(true);
@@ -15860,7 +15915,7 @@ function testDatabases(reconciled) {
15860
15915
  * starts without the reference data the app needs.
15861
15916
  */
15862
15917
  async function migrateTestStage(workspace, env) {
15863
- const { migrateAndSeed } = await Promise.resolve().then(() => require("./migrate-CTqhWtev.cjs"));
15918
+ const { migrateAndSeed } = await Promise.resolve().then(() => require("./migrate-bDQzFvBN.cjs"));
15864
15919
  const { discover } = await Promise.resolve().then(() => require("./discover-Bsr7HjqJ.cjs")).then((n) => n.discover_exports);
15865
15920
  const sources = {};
15866
15921
  const manifest = await discover({
@@ -16280,6 +16335,42 @@ program.command("exec").description("Run a command with secrets injected into Cr
16280
16335
  exitWithError(error);
16281
16336
  }
16282
16337
  });
16338
+ program.command("eas:sync").description("Set up a mobile app's EAS project for a deployed stage: its eas.json profiles, and its public values in the stage's EAS environment").requiredOption("--stage <stage>", "A deployed compose stage").option("--app <app>", "The mobile app (default: the one here, or the only one)").option("--profile <profile>", "AWS profile for the stage's account").option("--dry-run", "Say what would be written, and write nothing").action(async (options) => {
16339
+ try {
16340
+ const globalOptions = program.opts();
16341
+ if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16342
+ const { easSync } = await Promise.resolve().then(() => require("./eas-BkLA60P2.cjs"));
16343
+ await easSync(options);
16344
+ } catch (error) {
16345
+ exitWithError(error);
16346
+ }
16347
+ });
16348
+ program.command("publish").description("Ship a mobile app for a deployed stage: sync its EAS project, then an update when a store build has its native code, a store build submitted to the stores when none does").requiredOption("--stage <stage>", "A deployed compose stage").option("--app <app>", "The mobile app (default: the one here, or the only one)").option("--platform <platform>", "ios, android or all", "all").option("--build", "A store build, whatever the fingerprint says").option("--update", "An update, whatever the fingerprint says").option("--wait", "Wait for a build to finish, and submit, on EAS").option("--message <message>", "An update's message (default: the commit's subject)").option("--profile <profile>", "AWS profile for the stage's account").option("--dry-run", "Say what would be released, and release nothing").action(async (options) => {
16349
+ try {
16350
+ const globalOptions = program.opts();
16351
+ if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16352
+ const { publish, PublishModeConflict } = await Promise.resolve().then(() => require("./publish-CPgSbtcs.cjs"));
16353
+ if (options.build && options.update) throw new PublishModeConflict();
16354
+ const { build, update, ...rest } = options;
16355
+ await publish({
16356
+ ...rest,
16357
+ ...build ? { mode: "build" } : {},
16358
+ ...update ? { mode: "update" } : {}
16359
+ });
16360
+ } catch (error) {
16361
+ exitWithError(error);
16362
+ }
16363
+ });
16364
+ program.command("deploy:google-play").description("Let EAS Submit publish a mobile app to Google Play: a service account with a grant on the app alone, its key in EAS, each stage's track in eas.json, and the store listing from store/play/").option("--developer <id>", "The Play developer account (the number in Play Console's address)").option("--app <app>", "The mobile app (default: the one here, or the only one)").option("--project <project>", "The Google Cloud project (default: <workspace>-play, created when absent)").option("--stage <stage>", "The stage whose values evaluate the app's Expo config (default: the protected one)").option("--profile <profile>", "AWS profile for that stage's account").action(async (options) => {
16365
+ try {
16366
+ const globalOptions = program.opts();
16367
+ if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16368
+ const { deployGooglePlay } = await Promise.resolve().then(() => require("./googlePlay-DNgybe5t.cjs"));
16369
+ await deployGooglePlay(options);
16370
+ } catch (error) {
16371
+ exitWithError(error);
16372
+ }
16373
+ });
16283
16374
  program.command("test").description("Run tests with secrets loaded from environment").option("--stage <stage>", "Stage to load secrets from (default: stages.local)").option("--run", "Run tests once without watch mode").option("--watch", "Enable watch mode").option("--coverage", "Generate coverage report").option("--ui", "Open Vitest UI").option("--auto-setup", "Generate a fresh stage (secrets + key) from the workspace config when none exists (for CI; also via GKM_AUTO_SETUP)").option("--prepare", "Write the test manifest and #test harness, then stop — for a typecheck that runs before the suite").option("--setup", "Reconcile, migrate and seed the test stage and write the harness, then stop — what @geekmidas/cli/vitest runs").option("--teardown", "Drop the test stage's databases the last setup created, then stop — what @geekmidas/cli/vitest runs when the suite ends").argument("[pattern]", "Pattern to filter tests").action(async (pattern, options) => {
16284
16375
  try {
16285
16376
  const globalOptions = program.opts();
@@ -16296,7 +16387,7 @@ program.command("migrate").description("Apply each database construct's migratio
16296
16387
  try {
16297
16388
  const globalOptions = program.opts();
16298
16389
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16299
- const { migrateCommand } = await Promise.resolve().then(() => require("./migrate-CTqhWtev.cjs"));
16390
+ const { migrateCommand } = await Promise.resolve().then(() => require("./migrate-bDQzFvBN.cjs"));
16300
16391
  await migrateCommand({
16301
16392
  ...options,
16302
16393
  ...construct ? { construct } : {}
@@ -16309,7 +16400,7 @@ program.command("seed").description("Migrate, then run each database construct's
16309
16400
  try {
16310
16401
  const globalOptions = program.opts();
16311
16402
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16312
- const { seedCommand } = await Promise.resolve().then(() => require("./migrate-CTqhWtev.cjs"));
16403
+ const { seedCommand } = await Promise.resolve().then(() => require("./migrate-bDQzFvBN.cjs"));
16313
16404
  await seedCommand({
16314
16405
  ...options,
16315
16406
  ...construct ? { construct } : {}
@@ -16322,7 +16413,7 @@ program.command("migration").description("Write a construct's next migration: an
16322
16413
  try {
16323
16414
  const globalOptions = program.opts();
16324
16415
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16325
- const { migrationCommand } = await Promise.resolve().then(() => require("./migrate-CTqhWtev.cjs"));
16416
+ const { migrationCommand } = await Promise.resolve().then(() => require("./migrate-bDQzFvBN.cjs"));
16326
16417
  await migrationCommand({
16327
16418
  construct,
16328
16419
  ...name ? { name } : {}
@@ -16422,7 +16513,7 @@ program.command("secrets:add").description("Build the keys a stage must be given
16422
16513
  try {
16423
16514
  const globalOptions = program.opts();
16424
16515
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16425
- const { secretsAddCommand } = await Promise.resolve().then(() => require("./add-Cdg1kEqN.cjs"));
16516
+ const { secretsAddCommand } = await Promise.resolve().then(() => require("./add-Cs8yT5iH.cjs"));
16426
16517
  await secretsAddCommand(options);
16427
16518
  } catch (error) {
16428
16519
  exitWithError(error);
@@ -16432,7 +16523,7 @@ program.command("encryption:rotate").description("Add a new current key to an En
16432
16523
  try {
16433
16524
  const globalOptions = program.opts();
16434
16525
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16435
- const { encryptionRotateCommand } = await Promise.resolve().then(() => require("./encryption-DVCxD-gV.cjs"));
16526
+ const { encryptionRotateCommand } = await Promise.resolve().then(() => require("./encryption-DoVnVU82.cjs"));
16436
16527
  await encryptionRotateCommand(construct, options);
16437
16528
  } catch (error) {
16438
16529
  exitWithError(error);
@@ -16442,7 +16533,7 @@ program.command("encryption:retire").description("Remove an old key from an Encr
16442
16533
  try {
16443
16534
  const globalOptions = program.opts();
16444
16535
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16445
- const { encryptionRetireCommand } = await Promise.resolve().then(() => require("./encryption-DVCxD-gV.cjs"));
16536
+ const { encryptionRetireCommand } = await Promise.resolve().then(() => require("./encryption-DoVnVU82.cjs"));
16446
16537
  await encryptionRetireCommand(construct, key, options);
16447
16538
  } catch (error) {
16448
16539
  exitWithError(error);
@@ -16481,7 +16572,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
16481
16572
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16482
16573
  const { loadWorkspaceConfig } = await Promise.resolve().then(() => require("./config.cjs"));
16483
16574
  const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-CuCQBa5-.cjs"));
16484
- const { secretsStoreFor } = await Promise.resolve().then(() => require("./seeds-DNqRA1aQ.cjs")).then((n) => n.store_exports);
16575
+ const { secretsStoreFor } = await Promise.resolve().then(() => require("./seeds-Dk8YkLkM.cjs")).then((n) => n.store_exports);
16485
16576
  const { workspace } = await loadWorkspaceConfig();
16486
16577
  const stage = options.stage ?? workspace.stages.local;
16487
16578
  const store = await secretsStoreFor(workspace, stage);
@@ -16490,7 +16581,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
16490
16581
  console.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
16491
16582
  process.exit(1);
16492
16583
  }
16493
- const { derivedContainers } = await Promise.resolve().then(() => require("./seeds-DNqRA1aQ.cjs")).then((n) => n.workspace_exports);
16584
+ const { derivedContainers } = await Promise.resolve().then(() => require("./seeds-Dk8YkLkM.cjs")).then((n) => n.workspace_exports);
16494
16585
  const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
16495
16586
  if (!result) {
16496
16587
  console.log(`\n✓ Secrets for stage "${stage}" are up-to-date`);
@@ -16507,14 +16598,14 @@ program.command("secrets:migrate").description("Copy a deployed stage's secrets
16507
16598
  try {
16508
16599
  const globalOptions = program.opts();
16509
16600
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16510
- const { secretsMigrateCommand } = await Promise.resolve().then(() => require("./migrate-Df81ew0k.cjs"));
16601
+ const { secretsMigrateCommand } = await Promise.resolve().then(() => require("./migrate-GLBZlrlJ.cjs"));
16511
16602
  await secretsMigrateCommand(options);
16512
16603
  } catch (error) {
16513
16604
  exitWithError(error);
16514
16605
  }
16515
16606
  });
16516
16607
  program.command("deploy").description("Deploy a stage through its target").option("--target <name>", "Deploy target: dokploy, compose, sst, or one named in deploy.targets (default: deploy.default)").option("--provider <provider>", "[DEPRECATED] Use --target instead").requiredOption("--stage <stage>", "Deployment stage (e.g., production, staging)").option("--tag <tag>", "Image tag (default: stage-timestamp; compose: the commit). Compose pulls a given tag").option("--skip-push", "Skip pushing image to registry").option("--skip-build", "Skip build step (use existing build)").option("--json", "Write events as JSON lines instead of progress; never prompts").option("--dry-run", "Show what would be created or reused; change, build and push nothing").option("--atomic", "If the release fails, roll back every app it released, not only the failed ones").option("--allow-dev-services", "Server targets: run the dev service (MinIO, Mailpit) for every bucket and mail the stage doesn't account for. Not production-grade").option("--skip-dns", "Compose: neither write the public hosts' DNS records nor check they point at GKM_SERVER_IPV4 (a CDN or proxy in front, or records written elsewhere)").option("--rotate-keys", "Deployed stage: each provider issues its runtime keys a successor (the old one is deleted by the next deploy)").option("--retire-old-keys", "Deployed stage: delete a rotated-out key now instead of on the next deploy").action(async (options) => {
16517
- const { deployCli } = await Promise.resolve().then(() => require("./cli-BZYz55tO.cjs"));
16608
+ const { deployCli } = await Promise.resolve().then(() => require("./cli-CHzpFBh-.cjs"));
16518
16609
  const globalOptions = program.opts();
16519
16610
  const code = await deployCli({
16520
16611
  cwd: (0, node_path.resolve)(globalOptions.cwd ?? process.cwd()),
@@ -16534,7 +16625,7 @@ program.command("deploy").description("Deploy a stage through its target").optio
16534
16625
  });
16535
16626
  program.command("deploy:rollback").description("Put a stage's app back on its previous release (Dokploy): one app with --app, every app with --atomic").requiredOption("--stage <stage>", "A deployed stage").option("--app <app>", "The app to roll back").option("--atomic", "Roll back every app that has an earlier release").action(async (options) => {
16536
16627
  try {
16537
- const { rollbackStage } = await Promise.resolve().then(() => require("./rollback-CBiYsQ3T.cjs")).then((n) => n.rollback_exports);
16628
+ const { rollbackStage } = await Promise.resolve().then(() => require("./rollback-Cw-Z0mWn.cjs")).then((n) => n.rollback_exports);
16538
16629
  const { terminalCredentials } = await Promise.resolve().then(() => require("./terminal-D_NRPW4W.cjs"));
16539
16630
  const globalOptions = program.opts();
16540
16631
  await rollbackStage({
@@ -16568,7 +16659,7 @@ program.command("deploy:github").description("Let GitHub Actions deploy a stage:
16568
16659
  try {
16569
16660
  const globalOptions = program.opts();
16570
16661
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16571
- const { deployGithubCommand } = await Promise.resolve().then(() => require("./github-D902Vwr9.cjs"));
16662
+ const { deployGithubCommand } = await Promise.resolve().then(() => require("./github-DDOHjdS9.cjs"));
16572
16663
  await deployGithubCommand(options);
16573
16664
  } catch (error) {
16574
16665
  exitWithError(error);
@@ -16750,6 +16841,7 @@ program.parse();
16750
16841
  exports.canPrompt = canPrompt;
16751
16842
  exports.formatError = formatError;
16752
16843
  exports.generateFullstackCustomSecrets = generateFullstackCustomSecrets;
16844
+ exports.mobileStage = mobileStage;
16753
16845
  exports.prompt = prompt;
16754
16846
  exports.slug = slug;
16755
16847
  exports.stamp = stamp;