@gpc-cli/cli 0.9.44 → 0.9.46

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 (168) hide show
  1. package/dist/{anomalies-NU2IN2GJ.js → anomalies-UDE4NGHJ.js} +19 -24
  2. package/dist/anomalies-UDE4NGHJ.js.map +1 -0
  3. package/dist/{apps-J2446UDA.js → apps-FKD3ZG5X.js} +31 -35
  4. package/dist/apps-FKD3ZG5X.js.map +1 -0
  5. package/dist/{audit-N2CRHWUN.js → audit-JASSHRWN.js} +47 -62
  6. package/dist/audit-JASSHRWN.js.map +1 -0
  7. package/dist/{auth-XGSTT5G5.js → auth-OTA3SV3J.js} +145 -103
  8. package/dist/auth-OTA3SV3J.js.map +1 -0
  9. package/dist/bin.js +6 -4
  10. package/dist/bin.js.map +1 -1
  11. package/dist/bundle-F7MUVC5J.js +204 -0
  12. package/dist/bundle-F7MUVC5J.js.map +1 -0
  13. package/dist/{cache-SLNFRTI2.js → cache-XKPLZYEB.js} +4 -5
  14. package/dist/cache-XKPLZYEB.js.map +1 -0
  15. package/dist/changelog-7COFZO7Q.js +48 -0
  16. package/dist/changelog-7COFZO7Q.js.map +1 -0
  17. package/dist/{chunk-4O4D5SGL.js → chunk-3SJ6OXCZ.js} +4 -5
  18. package/dist/chunk-3SJ6OXCZ.js.map +1 -0
  19. package/dist/{chunk-7LURVNQV.js → chunk-6OWN6S6X.js} +53 -49
  20. package/dist/{chunk-7LURVNQV.js.map → chunk-6OWN6S6X.js.map} +1 -1
  21. package/dist/{chunk-U6ZTQ34I.js → chunk-BCBXQC7J.js} +45 -11
  22. package/dist/chunk-BCBXQC7J.js.map +1 -0
  23. package/dist/{chunk-AA577WVQ.js → chunk-NQH4G7BI.js} +9 -3
  24. package/dist/chunk-NQH4G7BI.js.map +1 -0
  25. package/dist/chunk-SLNJEAMK.js +23 -0
  26. package/dist/chunk-SLNJEAMK.js.map +1 -0
  27. package/dist/{chunk-NV75I5VP.js → chunk-YFUBD2XB.js} +10 -8
  28. package/dist/chunk-YFUBD2XB.js.map +1 -0
  29. package/dist/{config-222P3MKK.js → config-2FTCYEGD.js} +8 -5
  30. package/dist/config-2FTCYEGD.js.map +1 -0
  31. package/dist/{data-safety-Q7FTCEWU.js → data-safety-AFMD6MYI.js} +12 -27
  32. package/dist/data-safety-AFMD6MYI.js.map +1 -0
  33. package/dist/{device-tiers-MIOQEXYY.js → device-tiers-AQAMUQXI.js} +23 -38
  34. package/dist/device-tiers-AQAMUQXI.js.map +1 -0
  35. package/dist/diff-6EO4ID6W.js +91 -0
  36. package/dist/diff-6EO4ID6W.js.map +1 -0
  37. package/dist/{docs-7DUXIKA3.js → docs-4D2SJ4LY.js} +4 -3
  38. package/dist/docs-4D2SJ4LY.js.map +1 -0
  39. package/dist/doctor-H4X7Q57B.js +691 -0
  40. package/dist/doctor-H4X7Q57B.js.map +1 -0
  41. package/dist/{enterprise-7THXNBTC.js → enterprise-7PWXMSUN.js} +11 -21
  42. package/dist/enterprise-7PWXMSUN.js.map +1 -0
  43. package/dist/{external-transactions-2GWIMUVM.js → external-transactions-LCZALS3V.js} +12 -28
  44. package/dist/external-transactions-LCZALS3V.js.map +1 -0
  45. package/dist/{feedback-2W2XJGZX.js → feedback-XP765TOO.js} +4 -4
  46. package/dist/{games-BT777WUO.js → games-ZSNGEI7A.js} +17 -32
  47. package/dist/games-ZSNGEI7A.js.map +1 -0
  48. package/dist/{generated-apks-RJWTIX7L.js → generated-apks-RX2IUWSF.js} +30 -38
  49. package/dist/generated-apks-RX2IUWSF.js.map +1 -0
  50. package/dist/{grants-TKQJ3IER.js → grants-EBPECI26.js} +22 -40
  51. package/dist/grants-EBPECI26.js.map +1 -0
  52. package/dist/{iap-ICAEQLK5.js → iap-OUI5YYN4.js} +30 -51
  53. package/dist/iap-OUI5YYN4.js.map +1 -0
  54. package/dist/index.js +1 -1
  55. package/dist/{init-JZ2THPMS.js → init-WSTQTJOD.js} +5 -4
  56. package/dist/init-WSTQTJOD.js.map +1 -0
  57. package/dist/{install-skills-OV4HVANW.js → install-skills-6QDUXI5F.js} +5 -6
  58. package/dist/{install-skills-OV4HVANW.js.map → install-skills-6QDUXI5F.js.map} +1 -1
  59. package/dist/{internal-sharing-3U2XFHA4.js → internal-sharing-ONNIWIAT.js} +3 -4
  60. package/dist/{internal-sharing-3U2XFHA4.js.map → internal-sharing-ONNIWIAT.js.map} +1 -1
  61. package/dist/{listings-77HZW4S5.js → listings-7SGQ4SRX.js} +118 -157
  62. package/dist/listings-7SGQ4SRX.js.map +1 -0
  63. package/dist/migrate-ZQCJGQQS.js +138 -0
  64. package/dist/migrate-ZQCJGQQS.js.map +1 -0
  65. package/dist/{one-time-products-LHZAXQES.js → one-time-products-MGZTU7OM.js} +65 -120
  66. package/dist/one-time-products-MGZTU7OM.js.map +1 -0
  67. package/dist/{preflight-H3HEBYQW.js → preflight-N7ZRG2JI.js} +58 -55
  68. package/dist/preflight-N7ZRG2JI.js.map +1 -0
  69. package/dist/{pricing-XQSDTTK5.js → pricing-JJZFICFL.js} +8 -8
  70. package/dist/{pricing-XQSDTTK5.js.map → pricing-JJZFICFL.js.map} +1 -1
  71. package/dist/{prompt-BSV22CQZ.js → prompt-GXC2JSLA.js} +2 -2
  72. package/dist/{publish-Q5ZKEKZ5.js → publish-JPTI4EBT.js} +34 -30
  73. package/dist/publish-JPTI4EBT.js.map +1 -0
  74. package/dist/{purchase-options-CKRN4VIW.js → purchase-options-KFWW4JW2.js} +16 -11
  75. package/dist/purchase-options-KFWW4JW2.js.map +1 -0
  76. package/dist/purchases-DAWTMXP6.js +383 -0
  77. package/dist/purchases-DAWTMXP6.js.map +1 -0
  78. package/dist/{quickstart-4HB62YEL.js → quickstart-Z5Y3FYJU.js} +5 -3
  79. package/dist/quickstart-Z5Y3FYJU.js.map +1 -0
  80. package/dist/{quota-UHIQQYOY.js → quota-MZRWYJGR.js} +5 -15
  81. package/dist/quota-MZRWYJGR.js.map +1 -0
  82. package/dist/{recovery-5EV2R476.js → recovery-YE3Z7NIN.js} +32 -61
  83. package/dist/recovery-YE3Z7NIN.js.map +1 -0
  84. package/dist/{releases-C2WC2K4E.js → releases-2I3WBULC.js} +184 -185
  85. package/dist/releases-2I3WBULC.js.map +1 -0
  86. package/dist/{reports-2YX3RDOS.js → reports-CIB2T3XT.js} +19 -21
  87. package/dist/reports-CIB2T3XT.js.map +1 -0
  88. package/dist/reviews-BCCXIQ6C.js +188 -0
  89. package/dist/reviews-BCCXIQ6C.js.map +1 -0
  90. package/dist/{status-WHGLODGV.js → status-6LH5W4FU.js} +105 -83
  91. package/dist/status-6LH5W4FU.js.map +1 -0
  92. package/dist/{subscriptions-CI3JH3VQ.js → subscriptions-DZP3Y7O7.js} +142 -232
  93. package/dist/subscriptions-DZP3Y7O7.js.map +1 -0
  94. package/dist/{testers-NZOFA3EF.js → testers-LSMBXCA2.js} +24 -44
  95. package/dist/testers-LSMBXCA2.js.map +1 -0
  96. package/dist/tracks-YHMO2A6B.js +98 -0
  97. package/dist/tracks-YHMO2A6B.js.map +1 -0
  98. package/dist/{train-XKE4JN3Y.js → train-MDD2EBHS.js} +35 -55
  99. package/dist/train-MDD2EBHS.js.map +1 -0
  100. package/dist/{update-QMPRL5Y6.js → update-OMALGIBR.js} +30 -15
  101. package/dist/update-OMALGIBR.js.map +1 -0
  102. package/dist/{users-2YTC4Q36.js → users-UKG7VIQH.js} +45 -67
  103. package/dist/users-UKG7VIQH.js.map +1 -0
  104. package/dist/{validate-UOVTM6L3.js → validate-QIYSA3N7.js} +8 -10
  105. package/dist/validate-QIYSA3N7.js.map +1 -0
  106. package/dist/{version-NK5SJLHJ.js → version-NCSNXNVN.js} +4 -4
  107. package/dist/{vitals-A4CS4MSS.js → vitals-C23L2Y2E.js} +153 -172
  108. package/dist/vitals-C23L2Y2E.js.map +1 -0
  109. package/package.json +6 -6
  110. package/dist/anomalies-NU2IN2GJ.js.map +0 -1
  111. package/dist/apps-J2446UDA.js.map +0 -1
  112. package/dist/audit-N2CRHWUN.js.map +0 -1
  113. package/dist/auth-XGSTT5G5.js.map +0 -1
  114. package/dist/bundle-F43TD2BQ.js +0 -218
  115. package/dist/bundle-F43TD2BQ.js.map +0 -1
  116. package/dist/cache-SLNFRTI2.js.map +0 -1
  117. package/dist/changelog-OYUZOCOL.js +0 -53
  118. package/dist/changelog-OYUZOCOL.js.map +0 -1
  119. package/dist/chunk-4O4D5SGL.js.map +0 -1
  120. package/dist/chunk-AA577WVQ.js.map +0 -1
  121. package/dist/chunk-FWKYRLKY.js +0 -19
  122. package/dist/chunk-FWKYRLKY.js.map +0 -1
  123. package/dist/chunk-NV75I5VP.js.map +0 -1
  124. package/dist/chunk-U6ZTQ34I.js.map +0 -1
  125. package/dist/config-222P3MKK.js.map +0 -1
  126. package/dist/data-safety-Q7FTCEWU.js.map +0 -1
  127. package/dist/device-tiers-MIOQEXYY.js.map +0 -1
  128. package/dist/diff-V77SMKAQ.js +0 -96
  129. package/dist/diff-V77SMKAQ.js.map +0 -1
  130. package/dist/docs-7DUXIKA3.js.map +0 -1
  131. package/dist/doctor-3Z4ARPM2.js +0 -372
  132. package/dist/doctor-3Z4ARPM2.js.map +0 -1
  133. package/dist/enterprise-7THXNBTC.js.map +0 -1
  134. package/dist/external-transactions-2GWIMUVM.js.map +0 -1
  135. package/dist/games-BT777WUO.js.map +0 -1
  136. package/dist/generated-apks-RJWTIX7L.js.map +0 -1
  137. package/dist/grants-TKQJ3IER.js.map +0 -1
  138. package/dist/iap-ICAEQLK5.js.map +0 -1
  139. package/dist/init-JZ2THPMS.js.map +0 -1
  140. package/dist/listings-77HZW4S5.js.map +0 -1
  141. package/dist/migrate-SQT6RD6T.js +0 -143
  142. package/dist/migrate-SQT6RD6T.js.map +0 -1
  143. package/dist/one-time-products-LHZAXQES.js.map +0 -1
  144. package/dist/preflight-H3HEBYQW.js.map +0 -1
  145. package/dist/publish-Q5ZKEKZ5.js.map +0 -1
  146. package/dist/purchase-options-CKRN4VIW.js.map +0 -1
  147. package/dist/purchases-HSMCOG4A.js +0 -330
  148. package/dist/purchases-HSMCOG4A.js.map +0 -1
  149. package/dist/quickstart-4HB62YEL.js.map +0 -1
  150. package/dist/quota-UHIQQYOY.js.map +0 -1
  151. package/dist/recovery-5EV2R476.js.map +0 -1
  152. package/dist/releases-C2WC2K4E.js.map +0 -1
  153. package/dist/reports-2YX3RDOS.js.map +0 -1
  154. package/dist/reviews-2CWOI5CV.js +0 -213
  155. package/dist/reviews-2CWOI5CV.js.map +0 -1
  156. package/dist/status-WHGLODGV.js.map +0 -1
  157. package/dist/subscriptions-CI3JH3VQ.js.map +0 -1
  158. package/dist/testers-NZOFA3EF.js.map +0 -1
  159. package/dist/tracks-NERFFEDT.js +0 -107
  160. package/dist/tracks-NERFFEDT.js.map +0 -1
  161. package/dist/train-XKE4JN3Y.js.map +0 -1
  162. package/dist/update-QMPRL5Y6.js.map +0 -1
  163. package/dist/users-2YTC4Q36.js.map +0 -1
  164. package/dist/validate-UOVTM6L3.js.map +0 -1
  165. package/dist/vitals-A4CS4MSS.js.map +0 -1
  166. /package/dist/{feedback-2W2XJGZX.js.map → feedback-XP765TOO.js.map} +0 -0
  167. /package/dist/{prompt-BSV22CQZ.js.map → prompt-GXC2JSLA.js.map} +0 -0
  168. /package/dist/{version-NK5SJLHJ.js.map → version-NCSNXNVN.js.map} +0 -0
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  resolvePackageName
4
- } from "./chunk-AA577WVQ.js";
4
+ } from "./chunk-NQH4G7BI.js";
5
5
  import {
6
6
  isDryRun
7
7
  } from "./chunk-Y3QZDAKS.js";
@@ -12,7 +12,7 @@ import {
12
12
  isInteractive,
13
13
  promptInput,
14
14
  promptSelect
15
- } from "./chunk-NV75I5VP.js";
15
+ } from "./chunk-YFUBD2XB.js";
16
16
 
17
17
  // src/commands/publish.ts
18
18
  import { appendFile, stat } from "fs/promises";
@@ -24,7 +24,8 @@ import {
24
24
  generateNotesFromGit,
25
25
  writeAuditLog,
26
26
  createAuditEntry,
27
- formatOutput
27
+ formatOutput,
28
+ GpcError
28
29
  } from "@gpc-cli/core";
29
30
  var PASS = "\u2713";
30
31
  var FAIL = "\u2717";
@@ -83,15 +84,21 @@ function registerPublishCommand(program) {
83
84
  try {
84
85
  await stat(file);
85
86
  } catch {
86
- console.error(`Error: File not found: ${file}`);
87
- process.exit(2);
87
+ throw new GpcError(
88
+ `File not found: ${file}`,
89
+ "PUBLISH_USAGE_ERROR",
90
+ 2,
91
+ "Check the file path and try again."
92
+ );
88
93
  }
89
94
  const noteSources = [options.notes, options.notesDir, options.notesFromGit].filter(Boolean);
90
95
  if (noteSources.length > 1) {
91
- console.error(
92
- "Error: Cannot combine --notes, --notes-dir, and --notes-from-git. Use only one."
96
+ throw new GpcError(
97
+ "Cannot combine --notes, --notes-dir, and --notes-from-git. Use only one.",
98
+ "PUBLISH_USAGE_ERROR",
99
+ 2,
100
+ "Pick one release notes source."
93
101
  );
94
- process.exit(2);
95
102
  }
96
103
  const config = await loadConfig();
97
104
  const packageName = resolvePackageName(program.opts()["app"], config);
@@ -118,10 +125,12 @@ function registerPublishCommand(program) {
118
125
  if (options.rollout !== void 0) {
119
126
  const rollout = Number(options.rollout);
120
127
  if (!Number.isFinite(rollout) || rollout < 1 || rollout > 100) {
121
- console.error(
122
- `Error: --rollout must be a number between 1 and 100 (got: ${options.rollout})`
128
+ throw new GpcError(
129
+ `--rollout must be a number between 1 and 100 (got: ${options.rollout})`,
130
+ "PUBLISH_USAGE_ERROR",
131
+ 2,
132
+ "Use a percentage between 1 and 100."
123
133
  );
124
- process.exit(2);
125
134
  }
126
135
  }
127
136
  if (options.notesFromGit) {
@@ -146,21 +155,16 @@ function registerPublishCommand(program) {
146
155
  });
147
156
  const client = createApiClient({ auth, onRetry });
148
157
  if (isDryRun(program)) {
149
- try {
150
- const result = await publish(client, packageName, file, {
151
- track: options.track,
152
- rolloutPercent: options.rollout ? Number(options.rollout) : void 0,
153
- notes: options.notes,
154
- notesDir: options.notesDir,
155
- releaseName: options.name,
156
- mappingFile: options.mapping,
157
- dryRun: true
158
- });
159
- console.log(formatDryRunOutput(result, format));
160
- } catch (error) {
161
- console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
162
- process.exit(4);
163
- }
158
+ const result = await publish(client, packageName, file, {
159
+ track: options.track,
160
+ rolloutPercent: options.rollout ? Number(options.rollout) : void 0,
161
+ notes: options.notes,
162
+ notesDir: options.notesDir,
163
+ releaseName: options.name,
164
+ mappingFile: options.mapping,
165
+ dryRun: true
166
+ });
167
+ console.log(formatDryRunOutput(result, format));
164
168
  return;
165
169
  }
166
170
  const auditEntry = createAuditEntry(
@@ -181,15 +185,15 @@ function registerPublishCommand(program) {
181
185
  console.log(formatValidationOutput(result, format));
182
186
  auditEntry.success = false;
183
187
  auditEntry.error = "Validation failed";
184
- process.exit(1);
188
+ process.exitCode = 1;
189
+ return;
185
190
  }
186
191
  console.log(formatPublishOutput(result, format));
187
192
  auditEntry.success = true;
188
193
  } catch (error) {
189
194
  auditEntry.success = false;
190
195
  auditEntry.error = error instanceof Error ? error.message : String(error);
191
- console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
192
- process.exit(4);
196
+ throw error;
193
197
  } finally {
194
198
  auditEntry.durationMs = Date.now() - new Date(auditEntry.timestamp).getTime();
195
199
  writeAuditLog(auditEntry).catch(() => {
@@ -200,4 +204,4 @@ function registerPublishCommand(program) {
200
204
  export {
201
205
  registerPublishCommand
202
206
  };
203
- //# sourceMappingURL=publish-Q5ZKEKZ5.js.map
207
+ //# sourceMappingURL=publish-JPTI4EBT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/publish.ts"],"sourcesContent":["import { resolvePackageName } from \"../resolve.js\";\nimport { appendFile, stat } from \"node:fs/promises\";\nimport type { OutputFormat } from \"@gpc-cli/config\";\nimport type { Command } from \"commander\";\nimport { loadConfig, getCacheDir } from \"@gpc-cli/config\";\nimport { resolveAuth } from \"@gpc-cli/auth\";\nimport { createApiClient } from \"@gpc-cli/api\";\nimport type { RetryLogEntry } from \"@gpc-cli/api\";\nimport {\n publish,\n generateNotesFromGit,\n writeAuditLog,\n createAuditEntry,\n formatOutput,\n GpcError,\n} from \"@gpc-cli/core\";\nimport type { PublishResult, DryRunPublishResult } from \"@gpc-cli/core\";\nimport { getOutputFormat } from \"../format.js\";\nimport { isDryRun } from \"../dry-run.js\";\nimport { isInteractive, promptSelect, promptInput } from \"../prompt.js\";\n\nconst PASS = \"\\u2713\";\nconst FAIL = \"\\u2717\";\nconst WARN = \"\\u26A0\";\n\n\n// ---------------------------------------------------------------------------\n// Output formatters\n// ---------------------------------------------------------------------------\n\nfunction formatChecks(checks: { name: string; passed: boolean; message: string }[]): string[] {\n return checks.map((c) => ` ${c.passed ? PASS : FAIL} ${c.message}`);\n}\n\nfunction formatValidationOutput(result: PublishResult, format: OutputFormat): string {\n if (format !== \"table\") {\n return formatOutput({ success: false, validation: result.validation }, format);\n }\n const lines = [\"Validation failed:\\n\", ...formatChecks(result.validation.checks)];\n for (const w of result.validation.warnings) {\n lines.push(` ${WARN} ${w}`);\n }\n return lines.join(\"\\n\");\n}\n\nfunction formatPublishOutput(result: PublishResult, format: OutputFormat): string {\n if (format !== \"table\") return formatOutput(result, format);\n\n const upload = result.upload;\n if (!upload) return formatOutput(result, format);\n const rollout =\n upload.status === \"inProgress\" && \"userFraction\" in upload\n ? ` (${Math.round(Number((upload as Record<string, unknown>)[\"userFraction\"]) * 100)}% rollout)`\n : \"\";\n\n const lines = [\"Published successfully\\n\", ...formatChecks(result.validation.checks)];\n for (const w of result.validation.warnings) {\n lines.push(` ${WARN} ${w}`);\n }\n lines.push(\"\");\n lines.push(` versionCode ${upload.versionCode}`);\n lines.push(` track ${upload.track}`);\n lines.push(` status ${upload.status}${rollout}`);\n return lines.join(\"\\n\");\n}\n\nfunction formatDryRunOutput(result: DryRunPublishResult, format: OutputFormat): string {\n if (format !== \"table\") return formatOutput(result, format);\n\n const lines = [\"Dry run — no changes made\\n\", ...formatChecks(result.validation.checks)];\n for (const w of result.validation.warnings) {\n lines.push(` ${WARN} ${w}`);\n }\n\n const u = result.upload;\n lines.push(\"\");\n lines.push(` Track ${u.track}`);\n\n if (u.currentReleases.length === 0) {\n lines.push(` Current (no releases on this track)`);\n } else {\n for (const r of u.currentReleases) {\n const fraction =\n r.userFraction !== undefined ? ` (${Math.round(r.userFraction * 100)}%)` : \"\";\n lines.push(` Current ${r.versionCodes.join(\", \")} · ${r.status}${fraction}`);\n }\n }\n const plannedFraction =\n u.plannedRelease.userFraction !== undefined\n ? ` (${Math.round(u.plannedRelease.userFraction * 100)}%)`\n : \"\";\n lines.push(` Would be (new bundle) · ${u.plannedRelease.status}${plannedFraction}`);\n return lines.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Command\n// ---------------------------------------------------------------------------\n\nexport function registerPublishCommand(program: Command): void {\n program\n .command(\"publish <file>\")\n .description(\"Validate, upload, and release in one step\")\n .option(\"--track <track>\", \"Target track\", \"internal\")\n .option(\"--rollout <percent>\", \"Staged rollout percentage (1-100)\")\n .option(\"--notes <text>\", \"Release notes (en-US)\")\n .option(\"--notes-dir <dir>\", \"Read release notes from directory (<dir>/<lang>.txt)\")\n .option(\"--notes-from-git\", \"Generate release notes from git commit history\")\n .option(\"--since <ref>\", \"Git ref to start from (tag, SHA) — used with --notes-from-git\")\n .option(\"--name <name>\", \"Release name\")\n .option(\"--mapping <file>\", \"ProGuard/R8 mapping file for deobfuscation\")\n .option(\"--retry-log <path>\", \"Write retry log entries to file (JSONL)\")\n .action(async (file: string, options) => {\n try {\n await stat(file);\n } catch {\n throw new GpcError(\n `File not found: ${file}`,\n \"PUBLISH_USAGE_ERROR\",\n 2,\n \"Check the file path and try again.\",\n );\n }\n\n const noteSources = [options.notes, options.notesDir, options.notesFromGit].filter(Boolean);\n if (noteSources.length > 1) {\n throw new GpcError(\n \"Cannot combine --notes, --notes-dir, and --notes-from-git. Use only one.\",\n \"PUBLISH_USAGE_ERROR\",\n 2,\n \"Pick one release notes source.\",\n );\n }\n\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const format = getOutputFormat(program, config);\n\n // Interactive mode: prompt for missing options\n if (isInteractive(program)) {\n if (!options.track || options.track === \"internal\") {\n const tracks = [\"internal\", \"alpha\", \"beta\", \"production\"];\n options.track = await promptSelect(\"Select track:\", tracks, \"internal\");\n }\n\n if (!options.rollout && options.track === \"production\") {\n const rolloutStr = await promptInput(\n \"Staged rollout percentage (1-100, blank for full)\",\n \"100\",\n );\n if (rolloutStr && rolloutStr !== \"100\") {\n options.rollout = rolloutStr;\n }\n }\n\n if (!options.notes && !options.notesDir && !options.notesFromGit) {\n const notes = await promptInput(\"Release notes (en-US, blank to skip)\");\n if (notes) options.notes = notes;\n }\n }\n\n // Rollout range guard\n if (options.rollout !== undefined) {\n const rollout = Number(options.rollout);\n if (!Number.isFinite(rollout) || rollout < 1 || rollout > 100) {\n throw new GpcError(\n `--rollout must be a number between 1 and 100 (got: ${options.rollout})`,\n \"PUBLISH_USAGE_ERROR\",\n 2,\n \"Use a percentage between 1 and 100.\",\n );\n }\n }\n\n // Resolve git-based release notes before calling publish\n if (options.notesFromGit) {\n const gitNotes = await generateNotesFromGit({ since: options.since });\n options.notes = gitNotes.text;\n if (gitNotes.truncated) {\n console.error(\n `${WARN} Release notes truncated to 500 characters (${gitNotes.commitCount} commits from ${gitNotes.since}).`,\n );\n }\n }\n\n let onRetry: ((entry: RetryLogEntry) => void) | undefined;\n if (options.retryLog) {\n onRetry = (entry: RetryLogEntry) => {\n appendFile(options.retryLog, JSON.stringify(entry) + \"\\n\").catch(() => {});\n };\n }\n\n const auth = await resolveAuth({\n serviceAccountPath: config.auth?.serviceAccount,\n cachePath: getCacheDir(),\n });\n const client = createApiClient({ auth, onRetry });\n\n if (isDryRun(program)) {\n const result = await publish(client, packageName, file, {\n track: options.track,\n rolloutPercent: options.rollout ? Number(options.rollout) : undefined,\n notes: options.notes,\n notesDir: options.notesDir,\n releaseName: options.name,\n mappingFile: options.mapping,\n dryRun: true,\n });\n console.log(formatDryRunOutput(result as DryRunPublishResult, format));\n return;\n }\n\n const auditEntry = createAuditEntry(\n \"publish\",\n { file, track: options.track, rollout: options.rollout },\n packageName,\n );\n\n try {\n const result = await publish(client, packageName, file, {\n track: options.track,\n rolloutPercent: options.rollout ? Number(options.rollout) : undefined,\n notes: options.notes,\n notesDir: options.notesDir,\n releaseName: options.name,\n mappingFile: options.mapping,\n });\n\n if (!result.upload) {\n console.log(formatValidationOutput(result as PublishResult, format));\n auditEntry.success = false;\n auditEntry.error = \"Validation failed\";\n process.exitCode = 1;\n return;\n }\n\n console.log(formatPublishOutput(result as PublishResult, format));\n auditEntry.success = true;\n } catch (error) {\n auditEntry.success = false;\n auditEntry.error = error instanceof Error ? error.message : String(error);\n throw error;\n } finally {\n auditEntry.durationMs = Date.now() - new Date(auditEntry.timestamp).getTime();\n writeAuditLog(auditEntry).catch(() => {});\n }\n });\n}\n"],"mappings":"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","names":[]}
@@ -14,27 +14,32 @@ See: gpc otp offers --help`;
14
14
  function registerPurchaseOptionsCommands(program) {
15
15
  const po = program.command("purchase-options").description("Manage purchase options (use 'otp offers' commands)");
16
16
  po.command("list").description("List purchase options").option("--sort <field>", "Sort by field").action(async () => {
17
- console.log(REDIRECT_MESSAGE);
18
- process.exit(2);
17
+ const err = new Error(REDIRECT_MESSAGE);
18
+ Object.assign(err, { code: "USAGE_ERROR", exitCode: 2 });
19
+ throw err;
19
20
  });
20
21
  po.command("get <id>").description("Get a purchase option").action(async () => {
21
- console.log(REDIRECT_MESSAGE);
22
- process.exit(2);
22
+ const err = new Error(REDIRECT_MESSAGE);
23
+ Object.assign(err, { code: "USAGE_ERROR", exitCode: 2 });
24
+ throw err;
23
25
  });
24
26
  po.command("create").description("Create a purchase option").option("--file <path>", "JSON file with purchase option data").action(async () => {
25
- console.log(REDIRECT_MESSAGE);
26
- process.exit(2);
27
+ const err = new Error(REDIRECT_MESSAGE);
28
+ Object.assign(err, { code: "USAGE_ERROR", exitCode: 2 });
29
+ throw err;
27
30
  });
28
31
  po.command("activate <id>").description("Activate a purchase option").action(async () => {
29
- console.log(REDIRECT_MESSAGE);
30
- process.exit(2);
32
+ const err = new Error(REDIRECT_MESSAGE);
33
+ Object.assign(err, { code: "USAGE_ERROR", exitCode: 2 });
34
+ throw err;
31
35
  });
32
36
  po.command("deactivate <id>").description("Deactivate a purchase option").action(async () => {
33
- console.log(REDIRECT_MESSAGE);
34
- process.exit(2);
37
+ const err = new Error(REDIRECT_MESSAGE);
38
+ Object.assign(err, { code: "USAGE_ERROR", exitCode: 2 });
39
+ throw err;
35
40
  });
36
41
  }
37
42
  export {
38
43
  registerPurchaseOptionsCommands
39
44
  };
40
- //# sourceMappingURL=purchase-options-CKRN4VIW.js.map
45
+ //# sourceMappingURL=purchase-options-KFWW4JW2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/purchase-options.ts"],"sourcesContent":["import type { Command } from \"commander\";\n\nconst REDIRECT_MESSAGE = `Purchase options are managed through one-time product offers.\n\nUse the following commands instead:\n gpc otp offers list <product-id> List purchase options for a product\n gpc otp offers get <product-id> <id> Get a specific purchase option\n gpc otp offers create <product-id> Create a purchase option\n gpc otp offers activate <product-id> <id>\n gpc otp offers deactivate <product-id> <id>\n\nSee: gpc otp offers --help`;\n\nexport function registerPurchaseOptionsCommands(program: Command): void {\n const po = program\n .command(\"purchase-options\")\n .description(\"Manage purchase options (use 'otp offers' commands)\");\n\n po.command(\"list\")\n .description(\"List purchase options\")\n .option(\"--sort <field>\", \"Sort by field\")\n .action(async () => {\n const err = new Error(REDIRECT_MESSAGE);\n Object.assign(err, { code: \"USAGE_ERROR\", exitCode: 2 });\n throw err;\n });\n\n po.command(\"get <id>\")\n .description(\"Get a purchase option\")\n .action(async () => {\n const err = new Error(REDIRECT_MESSAGE);\n Object.assign(err, { code: \"USAGE_ERROR\", exitCode: 2 });\n throw err;\n });\n\n po.command(\"create\")\n .description(\"Create a purchase option\")\n .option(\"--file <path>\", \"JSON file with purchase option data\")\n .action(async () => {\n const err = new Error(REDIRECT_MESSAGE);\n Object.assign(err, { code: \"USAGE_ERROR\", exitCode: 2 });\n throw err;\n });\n\n po.command(\"activate <id>\")\n .description(\"Activate a purchase option\")\n .action(async () => {\n const err = new Error(REDIRECT_MESSAGE);\n Object.assign(err, { code: \"USAGE_ERROR\", exitCode: 2 });\n throw err;\n });\n\n po.command(\"deactivate <id>\")\n .description(\"Deactivate a purchase option\")\n .action(async () => {\n const err = new Error(REDIRECT_MESSAGE);\n Object.assign(err, { code: \"USAGE_ERROR\", exitCode: 2 });\n throw err;\n });\n}\n"],"mappings":";;;AAEA,IAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWlB,SAAS,gCAAgC,SAAwB;AACtE,QAAM,KAAK,QACR,QAAQ,kBAAkB,EAC1B,YAAY,qDAAqD;AAEpE,KAAG,QAAQ,MAAM,EACd,YAAY,uBAAuB,EACnC,OAAO,kBAAkB,eAAe,EACxC,OAAO,YAAY;AAClB,UAAM,MAAM,IAAI,MAAM,gBAAgB;AACtC,WAAO,OAAO,KAAK,EAAE,MAAM,eAAe,UAAU,EAAE,CAAC;AACvD,UAAM;AAAA,EACR,CAAC;AAEH,KAAG,QAAQ,UAAU,EAClB,YAAY,uBAAuB,EACnC,OAAO,YAAY;AAClB,UAAM,MAAM,IAAI,MAAM,gBAAgB;AACtC,WAAO,OAAO,KAAK,EAAE,MAAM,eAAe,UAAU,EAAE,CAAC;AACvD,UAAM;AAAA,EACR,CAAC;AAEH,KAAG,QAAQ,QAAQ,EAChB,YAAY,0BAA0B,EACtC,OAAO,iBAAiB,qCAAqC,EAC7D,OAAO,YAAY;AAClB,UAAM,MAAM,IAAI,MAAM,gBAAgB;AACtC,WAAO,OAAO,KAAK,EAAE,MAAM,eAAe,UAAU,EAAE,CAAC;AACvD,UAAM;AAAA,EACR,CAAC;AAEH,KAAG,QAAQ,eAAe,EACvB,YAAY,4BAA4B,EACxC,OAAO,YAAY;AAClB,UAAM,MAAM,IAAI,MAAM,gBAAgB;AACtC,WAAO,OAAO,KAAK,EAAE,MAAM,eAAe,UAAU,EAAE,CAAC;AACvD,UAAM;AAAA,EACR,CAAC;AAEH,KAAG,QAAQ,iBAAiB,EACzB,YAAY,8BAA8B,EAC1C,OAAO,YAAY;AAClB,UAAM,MAAM,IAAI,MAAM,gBAAgB;AACtC,WAAO,OAAO,KAAK,EAAE,MAAM,eAAe,UAAU,EAAE,CAAC;AACvD,UAAM;AAAA,EACR,CAAC;AACL;","names":[]}
@@ -0,0 +1,383 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ getClient,
4
+ resolvePackageName
5
+ } from "./chunk-NQH4G7BI.js";
6
+ import {
7
+ isDryRun,
8
+ printDryRun
9
+ } from "./chunk-Y3QZDAKS.js";
10
+ import {
11
+ getOutputFormat
12
+ } from "./chunk-ELXAK7GI.js";
13
+ import {
14
+ isInteractive,
15
+ requireConfirm,
16
+ requireOption
17
+ } from "./chunk-YFUBD2XB.js";
18
+
19
+ // src/commands/purchases.ts
20
+ import { Option } from "commander";
21
+ import { loadConfig } from "@gpc-cli/config";
22
+ import {
23
+ getProductPurchase,
24
+ getProductPurchaseV2,
25
+ acknowledgeProductPurchase,
26
+ consumeProductPurchase,
27
+ getSubscriptionPurchase,
28
+ cancelSubscriptionPurchase,
29
+ cancelSubscriptionV2,
30
+ deferSubscriptionPurchase,
31
+ deferSubscriptionV2,
32
+ revokeSubscriptionPurchase,
33
+ refundSubscriptionV2,
34
+ listVoidedPurchases,
35
+ refundOrder,
36
+ getOrderDetails,
37
+ batchGetOrders,
38
+ formatOutput
39
+ } from "@gpc-cli/core";
40
+ function registerPurchasesCommands(program) {
41
+ const purchases = program.command("purchases").description("Manage purchases and orders");
42
+ purchases.command("get <product-id> <token>").description("Get a product purchase").action(async (productId, token) => {
43
+ const config = await loadConfig();
44
+ const packageName = resolvePackageName(program.opts()["app"], config);
45
+ const client = await getClient(config);
46
+ const format = getOutputFormat(program, config);
47
+ const result = await getProductPurchase(client, packageName, productId, token);
48
+ if (format !== "json") {
49
+ const r = result;
50
+ const row = {
51
+ orderId: r["orderId"] || "-",
52
+ purchaseState: r["purchaseState"] ?? "-",
53
+ consumptionState: r["consumptionState"] ?? "-",
54
+ purchaseTime: r["purchaseTimeMillis"] ? new Date(Number(r["purchaseTimeMillis"])).toISOString() : "-",
55
+ acknowledged: r["acknowledgementState"] ?? "-"
56
+ };
57
+ console.log(formatOutput(row, format));
58
+ } else {
59
+ console.log(formatOutput(result, format));
60
+ }
61
+ });
62
+ purchases.command("acknowledge <product-id> <token>").description("Acknowledge a product purchase").option("--payload <text>", "Developer payload").action(async (productId, token, options) => {
63
+ const config = await loadConfig();
64
+ const packageName = resolvePackageName(program.opts()["app"], config);
65
+ if (isDryRun(program)) {
66
+ const format = getOutputFormat(program, config);
67
+ printDryRun(
68
+ {
69
+ command: "purchases acknowledge",
70
+ action: "acknowledge",
71
+ target: `${productId}/${token}`,
72
+ details: { payload: options.payload }
73
+ },
74
+ format,
75
+ formatOutput
76
+ );
77
+ return;
78
+ }
79
+ const client = await getClient(config);
80
+ await acknowledgeProductPurchase(client, packageName, productId, token, options.payload);
81
+ console.log(`Purchase acknowledged.`);
82
+ });
83
+ purchases.command("consume <product-id> <token>").description("Consume a product purchase").action(async (productId, token) => {
84
+ const config = await loadConfig();
85
+ const packageName = resolvePackageName(program.opts()["app"], config);
86
+ if (isDryRun(program)) {
87
+ const format = getOutputFormat(program, config);
88
+ printDryRun(
89
+ {
90
+ command: "purchases consume",
91
+ action: "consume",
92
+ target: `${productId}/${token}`
93
+ },
94
+ format,
95
+ formatOutput
96
+ );
97
+ return;
98
+ }
99
+ const client = await getClient(config);
100
+ await consumeProductPurchase(client, packageName, productId, token);
101
+ console.log(`Purchase consumed.`);
102
+ });
103
+ const sub = purchases.command("subscription").description("Manage subscription purchases");
104
+ sub.command("get <token>").description("Get a subscription purchase (v2)").action(async (token) => {
105
+ const config = await loadConfig();
106
+ const packageName = resolvePackageName(program.opts()["app"], config);
107
+ const client = await getClient(config);
108
+ const format = getOutputFormat(program, config);
109
+ const result = await getSubscriptionPurchase(client, packageName, token);
110
+ if (format !== "json") {
111
+ const r = result;
112
+ const lineItems = r["lineItems"];
113
+ const row = {
114
+ subscriptionState: r["subscriptionState"] || "-",
115
+ startTime: r["startTime"] || "-",
116
+ expiryTime: r["expiryTime"] || "-",
117
+ linkedPurchaseToken: r["linkedPurchaseToken"] ? "yes" : "no",
118
+ lineItems: lineItems?.length || 0,
119
+ acknowledgement: r["acknowledgementState"] || "-"
120
+ };
121
+ console.log(formatOutput(row, format));
122
+ } else {
123
+ console.log(formatOutput(result, format));
124
+ }
125
+ });
126
+ sub.command("cancel <subscription-id> <token>").description("Cancel a subscription (v1)").action(async (subscriptionId, token) => {
127
+ const config = await loadConfig();
128
+ const packageName = resolvePackageName(program.opts()["app"], config);
129
+ if (isDryRun(program)) {
130
+ const format = getOutputFormat(program, config);
131
+ printDryRun(
132
+ {
133
+ command: "purchases subscription cancel",
134
+ action: "cancel subscription",
135
+ target: `${subscriptionId}/${token}`
136
+ },
137
+ format,
138
+ formatOutput
139
+ );
140
+ return;
141
+ }
142
+ const client = await getClient(config);
143
+ await cancelSubscriptionPurchase(client, packageName, subscriptionId, token);
144
+ console.log(`Subscription cancelled.`);
145
+ });
146
+ sub.command("defer <subscription-id> <token>").description("Defer a subscription expiry").option("--expiry <iso-date>", "Desired new expiry date (ISO 8601)").action(async (subscriptionId, token, options) => {
147
+ const config = await loadConfig();
148
+ const packageName = resolvePackageName(program.opts()["app"], config);
149
+ const format = getOutputFormat(program, config);
150
+ const interactive = isInteractive(program);
151
+ options.expiry = await requireOption(
152
+ "expiry",
153
+ options.expiry,
154
+ {
155
+ message: "New expiry date (ISO 8601, e.g. 2026-12-31T23:59:59Z):"
156
+ },
157
+ interactive
158
+ );
159
+ if (isDryRun(program)) {
160
+ printDryRun(
161
+ {
162
+ command: "purchases subscription defer",
163
+ action: "defer subscription",
164
+ target: `${subscriptionId}/${token}`,
165
+ details: { expiry: options.expiry }
166
+ },
167
+ format,
168
+ formatOutput
169
+ );
170
+ return;
171
+ }
172
+ const client = await getClient(config);
173
+ const result = await deferSubscriptionPurchase(
174
+ client,
175
+ packageName,
176
+ subscriptionId,
177
+ token,
178
+ options.expiry
179
+ );
180
+ console.log(formatOutput(result, format));
181
+ });
182
+ sub.command("refund <token>").description("Refund a subscription purchase (v2)").action(async (token) => {
183
+ const config = await loadConfig();
184
+ const packageName = resolvePackageName(program.opts()["app"], config);
185
+ await requireConfirm(`Refund subscription for token "${token.slice(0, 16)}..."?`, program);
186
+ if (isDryRun(program)) {
187
+ const format = getOutputFormat(program, config);
188
+ printDryRun(
189
+ {
190
+ command: "purchases subscription refund",
191
+ action: "refund subscription",
192
+ target: token
193
+ },
194
+ format,
195
+ formatOutput
196
+ );
197
+ return;
198
+ }
199
+ const client = await getClient(config);
200
+ await refundSubscriptionV2(client, packageName, token);
201
+ console.log(`Subscription refunded.`);
202
+ });
203
+ sub.command("revoke <token>").description("Revoke a subscription (v2)").action(async (token) => {
204
+ const config = await loadConfig();
205
+ const packageName = resolvePackageName(program.opts()["app"], config);
206
+ if (isDryRun(program)) {
207
+ const format = getOutputFormat(program, config);
208
+ printDryRun(
209
+ {
210
+ command: "purchases subscription revoke",
211
+ action: "revoke subscription",
212
+ target: token
213
+ },
214
+ format,
215
+ formatOutput
216
+ );
217
+ return;
218
+ }
219
+ const client = await getClient(config);
220
+ await revokeSubscriptionPurchase(client, packageName, token);
221
+ console.log(`Subscription revoked.`);
222
+ });
223
+ purchases.command("voided").description("List voided purchases").option("--start-time <time>", "Start time (milliseconds)").option("--end-time <time>", "End time (milliseconds)").addOption(
224
+ new Option("--max-results <n>", "Maximum results per page").argParser(parseInt).hideHelp()
225
+ ).option("--limit <n>", "Maximum total results", parseInt).option("--next-page <token>", "Resume from page token").action(async (options) => {
226
+ const config = await loadConfig();
227
+ const packageName = resolvePackageName(program.opts()["app"], config);
228
+ const client = await getClient(config);
229
+ const format = getOutputFormat(program, config);
230
+ const result = await listVoidedPurchases(client, packageName, {
231
+ startTime: options.startTime,
232
+ endTime: options.endTime,
233
+ maxResults: options.maxResults,
234
+ limit: options.limit,
235
+ nextPage: options.nextPage
236
+ });
237
+ if (format !== "json") {
238
+ const purchases2 = result["voidedPurchases"];
239
+ if (purchases2 && purchases2.length > 0) {
240
+ const rows = purchases2.map((p) => ({
241
+ orderId: p["orderId"] || "-",
242
+ purchaseToken: String(p["purchaseToken"] || "-").slice(0, 16) + "...",
243
+ voidedTime: p["voidedTimeMillis"] ? new Date(Number(p["voidedTimeMillis"])).toISOString() : "-",
244
+ voidedSource: p["voidedSource"] ?? "-",
245
+ voidedReason: p["voidedReason"] ?? "-"
246
+ }));
247
+ console.log(formatOutput(rows, format));
248
+ } else {
249
+ console.log("No voided purchases found.");
250
+ }
251
+ } else {
252
+ console.log(formatOutput(result, format));
253
+ }
254
+ });
255
+ const orders = purchases.command("orders").description("Manage orders");
256
+ orders.command("refund <order-id>").description("Refund an order").option("--full-refund", "Full refund").option("--prorated-refund", "Prorated refund").action(async (orderId, options) => {
257
+ const config = await loadConfig();
258
+ const packageName = resolvePackageName(program.opts()["app"], config);
259
+ await requireConfirm(`Refund order "${orderId}"?`, program);
260
+ if (isDryRun(program)) {
261
+ const format = getOutputFormat(program, config);
262
+ printDryRun(
263
+ {
264
+ command: "purchases orders refund",
265
+ action: "refund",
266
+ target: orderId,
267
+ details: { fullRefund: options.fullRefund, proratedRefund: options.proratedRefund }
268
+ },
269
+ format,
270
+ formatOutput
271
+ );
272
+ return;
273
+ }
274
+ const client = await getClient(config);
275
+ await refundOrder(client, packageName, orderId, {
276
+ fullRefund: options.fullRefund,
277
+ proratedRefund: options.proratedRefund
278
+ });
279
+ console.log(`Order ${orderId} refunded.`);
280
+ });
281
+ orders.command("get <order-id>").description("Get order details").action(async (orderId) => {
282
+ const config = await loadConfig();
283
+ const packageName = resolvePackageName(program.opts()["app"], config);
284
+ const client = await getClient(config);
285
+ const format = getOutputFormat(program, config);
286
+ const result = await getOrderDetails(client, packageName, orderId);
287
+ if (format !== "json") {
288
+ const row = {
289
+ orderId: result.orderId,
290
+ state: result.state,
291
+ purchaseToken: result.purchaseToken ? result.purchaseToken.slice(0, 16) + "..." : "-",
292
+ createTime: result.createTime || "-",
293
+ total: result.total ? `${result.total.units || "0"}.${String(result.total.nanos || 0).padStart(9, "0").slice(0, 2)} ${result.total.currencyCode}` : "-",
294
+ lineItems: result.lineItems?.length || 0
295
+ };
296
+ console.log(formatOutput(row, format));
297
+ } else {
298
+ console.log(formatOutput(result, format));
299
+ }
300
+ });
301
+ orders.command("batch-get").description("Get multiple orders at once").requiredOption("--ids <order-ids>", "Comma-separated order IDs (max 1000)").action(async (options) => {
302
+ const config = await loadConfig();
303
+ const packageName = resolvePackageName(program.opts()["app"], config);
304
+ const client = await getClient(config);
305
+ const format = getOutputFormat(program, config);
306
+ const orderIds = options.ids.split(",").map((id) => id.trim()).filter(Boolean);
307
+ const result = await batchGetOrders(client, packageName, orderIds);
308
+ if (format !== "json") {
309
+ if (result.length === 0) {
310
+ console.log("No orders found.");
311
+ } else {
312
+ const rows = result.map((o) => ({
313
+ orderId: o.orderId,
314
+ state: o.state,
315
+ createTime: o.createTime || "-",
316
+ lineItems: o.lineItems?.length || 0
317
+ }));
318
+ console.log(formatOutput(rows, format));
319
+ }
320
+ } else {
321
+ console.log(formatOutput(result, format));
322
+ }
323
+ });
324
+ const product = purchases.command("product").description("Product purchase operations");
325
+ product.command("get-v2 <token>").description("Get product purchase details (v2 \u2014 supports multi-offer OTPs)").action(async (token) => {
326
+ const config = await loadConfig();
327
+ const packageName = resolvePackageName(program.opts()["app"], config);
328
+ const client = await getClient(config);
329
+ const format = getOutputFormat(program, config);
330
+ const result = await getProductPurchaseV2(client, packageName, token);
331
+ if (format !== "json") {
332
+ const row = {
333
+ orderId: result.orderId || "-",
334
+ state: result.purchaseStateContext?.state || "-",
335
+ regionCode: result.regionCode || "-",
336
+ completionTime: result.purchaseCompletionTime || "-",
337
+ acknowledgement: result.acknowledgementState || "-",
338
+ lineItems: result.productLineItem?.length || 0
339
+ };
340
+ console.log(formatOutput(row, format));
341
+ } else {
342
+ console.log(formatOutput(result, format));
343
+ }
344
+ });
345
+ sub.command("cancel-v2 <token>").description("Cancel a subscription (v2 \u2014 supports cancellation types)").option("--type <cancellationType>", "Cancellation type (e.g., USER_CANCELED, SYSTEM_CANCELED, DEVELOPER_CANCELED, REPLACED)").action(async (token, options) => {
346
+ const config = await loadConfig();
347
+ const packageName = resolvePackageName(program.opts()["app"], config);
348
+ await requireConfirm(`Cancel subscription (token: ${token.slice(0, 16)}...)?`, program);
349
+ if (isDryRun(program)) {
350
+ const format = getOutputFormat(program, config);
351
+ printDryRun(
352
+ { command: "purchases subscription cancel-v2", action: "cancel", target: token.slice(0, 16) + "...", details: { cancellationType: options.type } },
353
+ format,
354
+ formatOutput
355
+ );
356
+ return;
357
+ }
358
+ const client = await getClient(config);
359
+ await cancelSubscriptionV2(client, packageName, token, options.type);
360
+ console.log("Subscription cancelled.");
361
+ });
362
+ sub.command("defer-v2 <token>").description("Defer a subscription renewal (v2 \u2014 supports add-on subscriptions)").requiredOption("--until <date>", "Desired expiry time (ISO 8601 date)").action(async (token, options) => {
363
+ const config = await loadConfig();
364
+ const packageName = resolvePackageName(program.opts()["app"], config);
365
+ await requireConfirm(`Defer subscription renewal to ${options.until}?`, program);
366
+ if (isDryRun(program)) {
367
+ const format = getOutputFormat(program, config);
368
+ printDryRun(
369
+ { command: "purchases subscription defer-v2", action: "defer", target: token.slice(0, 16) + "...", details: { desiredExpiryTime: options.until } },
370
+ format,
371
+ formatOutput
372
+ );
373
+ return;
374
+ }
375
+ const client = await getClient(config);
376
+ const result = await deferSubscriptionV2(client, packageName, token, options.until);
377
+ console.log(`Subscription deferred. New expiry: ${result.newExpiryTime}`);
378
+ });
379
+ }
380
+ export {
381
+ registerPurchasesCommands
382
+ };
383
+ //# sourceMappingURL=purchases-DAWTMXP6.js.map