@gpc-cli/core 0.9.39 → 0.9.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -160,6 +160,7 @@ declare function uploadRelease(client: PlayApiClient, packageName: string, fileP
160
160
  }): Promise<UploadResult | DryRunUploadResult>;
161
161
  declare function getReleasesStatus(client: PlayApiClient, packageName: string, trackFilter?: string): Promise<ReleaseStatusResult[]>;
162
162
  declare function promoteRelease(client: PlayApiClient, packageName: string, fromTrack: string, toTrack: string, options?: {
163
+ status?: string;
163
164
  userFraction?: number;
164
165
  releaseNotes?: {
165
166
  language: string;
@@ -425,6 +426,7 @@ interface ReviewsFilterOptions {
425
426
  maxResults?: number;
426
427
  limit?: number;
427
428
  nextPage?: string;
429
+ all?: boolean;
428
430
  }
429
431
  interface ReviewExportOptions extends ReviewsFilterOptions {
430
432
  format?: "json" | "csv";
@@ -529,6 +531,8 @@ declare function refundSubscriptionV2(client: PlayApiClient, packageName: string
529
531
  interface ListVoidedOptions {
530
532
  startTime?: string;
531
533
  endTime?: string;
534
+ type?: number;
535
+ includeQuantityBasedPartialRefund?: boolean;
532
536
  maxResults?: number;
533
537
  limit?: number;
534
538
  nextPage?: string;
@@ -1204,4 +1208,49 @@ declare function fetchChangelog(options?: FetchChangelogOptions): Promise<Change
1204
1208
  */
1205
1209
  declare function formatChangelogEntry(entry: ChangelogEntry): string;
1206
1210
 
1207
- export { ApiError, type AppInfo, type AppStatus, type AuditEntry, type BatchSyncResult, type BundleAnalysis, type BundleComparison, type BundleEntry, type BundleSizeCheckResult, type BundleSizeConfig, type ChangelogEntry, type CommandContext, ConfigError, DEFAULT_LIMITS, DEFAULT_PREFLIGHT_CONFIG, type DiffToken, type DiscoverPluginsOptions, type DryRunPublishResult, type DryRunResult, type DryRunUploadResult, type ExportImagesOptions, type ExportImagesSummary, type FastlaneDetection, type FastlaneLane, type FetchChangelogOptions, type FieldLintResult, type FileValidationResult, type FindingSeverity, GOOGLE_PLAY_LANGUAGES, type GetAppStatusOptions, type GitNotesOptions, type GitReleaseNotes, GpcError, type ImageValidationResult, type InitOptions, type InitResult, type InternalSharingUploadResult, type ListIapOptions, type ListSubscriptionsOptions, type ListUsersOptions, type ListVoidedOptions, type ListingDiff, type ListingFieldLimits, type ListingLintResult, type ListingsResult, type LoadedPlugin, type MigrationResult, NetworkError, type OneTimeProductDiff, PERMISSION_PROPAGATION_WARNING, type ParsedManifest, type ParsedMonth, PluginManager, type PreflightConfig, type PreflightFinding, type PreflightOptions, type PreflightResult, type PreflightScanner, type PublishOptions, type PublishResult, type PushResult, type QuotaUsage, type ReleaseDiff, type ReleaseNotesValidation, type ReleaseStatusResult, type ReviewAnalysis, type ReviewExportOptions, type ReviewsFilterOptions, SENSITIVE_ARG_KEYS, SENSITIVE_KEYS, SEVERITY_ORDER, type ScaffoldOptions, type ScaffoldResult, type Spinner, type StatusDiff, type StatusRelease, type StatusReviews, type StatusVitalMetric, type SubscriptionAnalytics, type SubscriptionDiff, type SyncResult, type ThresholdResult, type TrainConfig, type TrainState, type UploadResult, type ValidateCheck, type ValidateOptions, type ValidateResult, type VersionVitalsComparison, type VersionVitalsRow, type VitalsOverview, type VitalsQueryOptions, type VitalsTrendComparison, type WatchOptions, type WatchVitalsOptions, type WebhookPayload, abortTrain, acknowledgeProductPurchase, activateBasePlan, activateOffer, activatePurchaseOption, addRecoveryTargeting, addTesters, advanceTrain, analyzeBundle, analyzeRemoteListings, analyzeReviews, batchGetOrders, batchSyncInAppProducts, cancelRecoveryAction, cancelSubscriptionPurchase, cancelSubscriptionV2, checkBundleSize, checkThreshold, clearAuditLog, compareBundles, compareVersionVitals, compareVitalsTrend, computeStatusDiff, consumeProductPurchase, convertRegionPrices, createAuditEntry, createDeviceTier, createEnterpriseApp, createExternalTransaction, createGrant, createInAppProduct, createOffer, createOneTimeOffer, createOneTimeProduct, createPurchaseOption, createRecoveryAction, createSpinner, createSubscription, createTrack, deactivateBasePlan, deactivateOffer, deactivatePurchaseOption, deferSubscriptionPurchase, deferSubscriptionV2, deleteBasePlan, deleteGrant, deleteImage, deleteInAppProduct, deleteListing, deleteOffer, deleteOneTimeOffer, deleteOneTimeProduct, deleteSubscription, deployRecoveryAction, detectFastlane, detectOutputFormat, diffListings, diffListingsCommand, diffListingsEnhanced, diffOneTimeProduct, diffReleases, diffSubscription, discoverPlugins, downloadGeneratedApk, downloadReport, exportDataSafety, exportImages, exportReviews, fetchChangelog, fetchReleaseNotes, formatChangelogEntry, formatCustomPayload, formatDiscordPayload, formatJunit, formatOutput, formatSlackPayload, formatStatusDiff, formatStatusSummary, formatStatusTable, formatWordDiff, generateMigrationPlan, generateNotesFromGit, getAllScannerNames, getAppInfo, getAppStatus, getCountryAvailability, getDataSafety, getDeviceTier, getExternalTransaction, getInAppProduct, getListings, getOffer, getOneTimeOffer, getOneTimeProduct, getOrderDetails, getProductPurchase, getProductPurchaseV2, getPurchaseOption, getQuotaUsage, getReleasesStatus, getReview, getSubscription, getSubscriptionAnalytics, getSubscriptionPurchase, getTrainStatus, getUser, getVitalsAnomalies, getVitalsAnr, getVitalsBattery, getVitalsCrashes, getVitalsLmk, getVitalsMemory, getVitalsOverview, getVitalsRendering, getVitalsStartup, importDataSafety, importTestersFromCsv, initAudit, initProject, inviteUser, isFinancialReportType, isStatsReportType, isValidBcp47, isValidReportType, isValidStatsDimension, lintListing, lintListings, lintLocalListings, listAchievements, listAuditEvents, listDeviceTiers, listEnterpriseApps, listEvents, listGeneratedApks, listGrants, listImages, listInAppProducts, listLeaderboards, listOffers, listOneTimeOffers, listOneTimeProducts, listPurchaseOptions, listRecoveryActions, listReports, listReviews, listSubscriptions, listTesters, listTracks, listUsers, listVoidedPurchases, loadPreflightConfig, loadStatusCache, maybePaginate, migratePrices, parseAppfile, parseFastfile, parseGrantArg, parseMonth, pauseTrain, promoteRelease, publish, pullListings, pushListings, readListingsFromDir, readReleaseNotesFromDir, redactAuditArgs, redactSensitive, refundExternalTransaction, refundOrder, refundSubscriptionV2, relativeTime, removeTesters, removeUser, replyToReview, revokeSubscriptionPurchase, runPreflight, runWatchLoop, safePath, safePathWithin, saveStatusCache, scaffoldPlugin, searchAuditEvents, searchVitalsErrors, sendNotification, sendWebhook, sortResults, startTrain, statusHasBreach, syncInAppProducts, topFiles, trackBreachState, updateAppDetails, updateDataSafety, updateGrant, updateInAppProduct, updateListing, updateOffer, updateOneTimeOffer, updateOneTimeProduct, updateRollout, updateSubscription, updateTrackConfig, updateUser, uploadExternallyHosted, uploadImage, uploadInternalSharing, uploadRelease, validateImage, validateLanguageCode, validatePackageName, validatePreSubmission, validateReleaseNotes, validateSku, validateTrackName, validateUploadFile, validateVersionCode, watchVitalsWithAutoHalt, wordDiff, writeAuditLog, writeListingsToDir, writeMigrationOutput };
1211
+ interface RtdnStatus {
1212
+ topicName: string | null;
1213
+ enabled: boolean;
1214
+ }
1215
+ interface DecodedNotification {
1216
+ version: string;
1217
+ packageName: string;
1218
+ eventTimeMillis: string;
1219
+ subscriptionNotification?: {
1220
+ version: string;
1221
+ notificationType: number;
1222
+ purchaseToken: string;
1223
+ subscriptionId: string;
1224
+ };
1225
+ oneTimeProductNotification?: {
1226
+ version: string;
1227
+ notificationType: number;
1228
+ purchaseToken: string;
1229
+ sku: string;
1230
+ };
1231
+ voidedPurchaseNotification?: {
1232
+ purchaseToken: string;
1233
+ orderId: string;
1234
+ productType: number;
1235
+ refundType?: number;
1236
+ };
1237
+ testNotification?: {
1238
+ version: string;
1239
+ };
1240
+ }
1241
+ /**
1242
+ * Get RTDN status by reading the app's notification topic configuration.
1243
+ * Uses the monetization settings endpoint if available, otherwise returns defaults.
1244
+ */
1245
+ declare function getRtdnStatus(client: PlayApiClient, packageName: string): Promise<RtdnStatus>;
1246
+ /**
1247
+ * Decode a base64-encoded RTDN notification payload.
1248
+ * Pub/Sub messages from Google Play are base64-encoded JSON.
1249
+ */
1250
+ declare function decodeNotification(base64Payload: string): DecodedNotification;
1251
+ /**
1252
+ * Format a decoded notification into a human-readable summary.
1253
+ */
1254
+ declare function formatNotification(notification: DecodedNotification): Record<string, unknown>;
1255
+
1256
+ export { ApiError, type AppInfo, type AppStatus, type AuditEntry, type BatchSyncResult, type BundleAnalysis, type BundleComparison, type BundleEntry, type BundleSizeCheckResult, type BundleSizeConfig, type ChangelogEntry, type CommandContext, ConfigError, DEFAULT_LIMITS, DEFAULT_PREFLIGHT_CONFIG, type DecodedNotification, type DiffToken, type DiscoverPluginsOptions, type DryRunPublishResult, type DryRunResult, type DryRunUploadResult, type ExportImagesOptions, type ExportImagesSummary, type FastlaneDetection, type FastlaneLane, type FetchChangelogOptions, type FieldLintResult, type FileValidationResult, type FindingSeverity, GOOGLE_PLAY_LANGUAGES, type GetAppStatusOptions, type GitNotesOptions, type GitReleaseNotes, GpcError, type ImageValidationResult, type InitOptions, type InitResult, type InternalSharingUploadResult, type ListIapOptions, type ListSubscriptionsOptions, type ListUsersOptions, type ListVoidedOptions, type ListingDiff, type ListingFieldLimits, type ListingLintResult, type ListingsResult, type LoadedPlugin, type MigrationResult, NetworkError, type OneTimeProductDiff, PERMISSION_PROPAGATION_WARNING, type ParsedManifest, type ParsedMonth, PluginManager, type PreflightConfig, type PreflightFinding, type PreflightOptions, type PreflightResult, type PreflightScanner, type PublishOptions, type PublishResult, type PushResult, type QuotaUsage, type ReleaseDiff, type ReleaseNotesValidation, type ReleaseStatusResult, type ReviewAnalysis, type ReviewExportOptions, type ReviewsFilterOptions, type RtdnStatus, SENSITIVE_ARG_KEYS, SENSITIVE_KEYS, SEVERITY_ORDER, type ScaffoldOptions, type ScaffoldResult, type Spinner, type StatusDiff, type StatusRelease, type StatusReviews, type StatusVitalMetric, type SubscriptionAnalytics, type SubscriptionDiff, type SyncResult, type ThresholdResult, type TrainConfig, type TrainState, type UploadResult, type ValidateCheck, type ValidateOptions, type ValidateResult, type VersionVitalsComparison, type VersionVitalsRow, type VitalsOverview, type VitalsQueryOptions, type VitalsTrendComparison, type WatchOptions, type WatchVitalsOptions, type WebhookPayload, abortTrain, acknowledgeProductPurchase, activateBasePlan, activateOffer, activatePurchaseOption, addRecoveryTargeting, addTesters, advanceTrain, analyzeBundle, analyzeRemoteListings, analyzeReviews, batchGetOrders, batchSyncInAppProducts, cancelRecoveryAction, cancelSubscriptionPurchase, cancelSubscriptionV2, checkBundleSize, checkThreshold, clearAuditLog, compareBundles, compareVersionVitals, compareVitalsTrend, computeStatusDiff, consumeProductPurchase, convertRegionPrices, createAuditEntry, createDeviceTier, createEnterpriseApp, createExternalTransaction, createGrant, createInAppProduct, createOffer, createOneTimeOffer, createOneTimeProduct, createPurchaseOption, createRecoveryAction, createSpinner, createSubscription, createTrack, deactivateBasePlan, deactivateOffer, deactivatePurchaseOption, decodeNotification, deferSubscriptionPurchase, deferSubscriptionV2, deleteBasePlan, deleteGrant, deleteImage, deleteInAppProduct, deleteListing, deleteOffer, deleteOneTimeOffer, deleteOneTimeProduct, deleteSubscription, deployRecoveryAction, detectFastlane, detectOutputFormat, diffListings, diffListingsCommand, diffListingsEnhanced, diffOneTimeProduct, diffReleases, diffSubscription, discoverPlugins, downloadGeneratedApk, downloadReport, exportDataSafety, exportImages, exportReviews, fetchChangelog, fetchReleaseNotes, formatChangelogEntry, formatCustomPayload, formatDiscordPayload, formatJunit, formatNotification, formatOutput, formatSlackPayload, formatStatusDiff, formatStatusSummary, formatStatusTable, formatWordDiff, generateMigrationPlan, generateNotesFromGit, getAllScannerNames, getAppInfo, getAppStatus, getCountryAvailability, getDataSafety, getDeviceTier, getExternalTransaction, getInAppProduct, getListings, getOffer, getOneTimeOffer, getOneTimeProduct, getOrderDetails, getProductPurchase, getProductPurchaseV2, getPurchaseOption, getQuotaUsage, getReleasesStatus, getReview, getRtdnStatus, getSubscription, getSubscriptionAnalytics, getSubscriptionPurchase, getTrainStatus, getUser, getVitalsAnomalies, getVitalsAnr, getVitalsBattery, getVitalsCrashes, getVitalsLmk, getVitalsMemory, getVitalsOverview, getVitalsRendering, getVitalsStartup, importDataSafety, importTestersFromCsv, initAudit, initProject, inviteUser, isFinancialReportType, isStatsReportType, isValidBcp47, isValidReportType, isValidStatsDimension, lintListing, lintListings, lintLocalListings, listAchievements, listAuditEvents, listDeviceTiers, listEnterpriseApps, listEvents, listGeneratedApks, listGrants, listImages, listInAppProducts, listLeaderboards, listOffers, listOneTimeOffers, listOneTimeProducts, listPurchaseOptions, listRecoveryActions, listReports, listReviews, listSubscriptions, listTesters, listTracks, listUsers, listVoidedPurchases, loadPreflightConfig, loadStatusCache, maybePaginate, migratePrices, parseAppfile, parseFastfile, parseGrantArg, parseMonth, pauseTrain, promoteRelease, publish, pullListings, pushListings, readListingsFromDir, readReleaseNotesFromDir, redactAuditArgs, redactSensitive, refundExternalTransaction, refundOrder, refundSubscriptionV2, relativeTime, removeTesters, removeUser, replyToReview, revokeSubscriptionPurchase, runPreflight, runWatchLoop, safePath, safePathWithin, saveStatusCache, scaffoldPlugin, searchAuditEvents, searchVitalsErrors, sendNotification, sendWebhook, sortResults, startTrain, statusHasBreach, syncInAppProducts, topFiles, trackBreachState, updateAppDetails, updateDataSafety, updateGrant, updateInAppProduct, updateListing, updateOffer, updateOneTimeOffer, updateOneTimeProduct, updateRollout, updateSubscription, updateTrackConfig, updateUser, uploadExternallyHosted, uploadImage, uploadInternalSharing, uploadRelease, validateImage, validateLanguageCode, validatePackageName, validatePreSubmission, validateReleaseNotes, validateSku, validateTrackName, validateUploadFile, validateVersionCode, watchVitalsWithAutoHalt, wordDiff, writeAuditLog, writeListingsToDir, writeMigrationOutput };
package/dist/index.js CHANGED
@@ -564,6 +564,7 @@ async function getAppInfo(client, packageName) {
564
564
 
565
565
  // src/commands/releases.ts
566
566
  import { stat as stat2 } from "fs/promises";
567
+ import { extname as extname2 } from "path";
567
568
  import { PlayApiError } from "@gpc-cli/api";
568
569
 
569
570
  // src/utils/file-validation.ts
@@ -671,6 +672,15 @@ async function withRetryOnConflict(client, packageName, operation) {
671
672
  }
672
673
  }
673
674
  }
675
+ var _consoleEditWarningShown = false;
676
+ function warnAboutConcurrentEdits() {
677
+ if (_consoleEditWarningShown) return;
678
+ _consoleEditWarningShown = true;
679
+ process.emitWarning?.(
680
+ "If the Play Console has pending changes, they may be discarded when this edit is committed. Avoid making changes in the Play Console while CLI operations are in progress.",
681
+ "ConcurrentEditWarning"
682
+ );
683
+ }
674
684
  function warnIfEditExpiring(edit) {
675
685
  if (!edit.expiryTimeSeconds) return;
676
686
  const expiryMs = Number(edit.expiryTimeSeconds) * 1e3;
@@ -735,14 +745,17 @@ ${validation.errors.join("\n")}`,
735
745
  if (options.onProgress) options.onProgress(0, fileSize);
736
746
  const edit = await client.edits.insert(packageName);
737
747
  warnIfEditExpiring(edit);
748
+ warnAboutConcurrentEdits();
738
749
  try {
739
- const bundle = await client.bundles.upload(packageName, edit.id, filePath, {
750
+ const isApk = extname2(filePath).toLowerCase() === ".apk";
751
+ const uploadOpts = {
740
752
  ...options.uploadOptions,
741
753
  onProgress: (event) => {
742
754
  if (options.onProgress) options.onProgress(event.bytesUploaded, event.totalBytes);
743
755
  if (options.onUploadProgress) options.onUploadProgress(event);
744
756
  }
745
- });
757
+ };
758
+ const bundle = isApk ? await client.apks.upload(packageName, edit.id, filePath, uploadOpts) : await client.bundles.upload(packageName, edit.id, filePath, uploadOpts);
746
759
  if (options.mappingFile) {
747
760
  await client.deobfuscation.upload(
748
761
  packageName,
@@ -820,7 +833,7 @@ async function promoteRelease(client, packageName, fromTrack, toTrack, options)
820
833
  }
821
834
  const release = {
822
835
  versionCodes: currentRelease.versionCodes,
823
- status: options?.userFraction ? "inProgress" : "completed",
836
+ status: options?.status || (options?.userFraction ? "inProgress" : "completed"),
824
837
  ...options?.userFraction && { userFraction: options.userFraction },
825
838
  releaseNotes: options?.releaseNotes || currentRelease.releaseNotes || []
826
839
  };
@@ -1139,7 +1152,7 @@ function isValidBcp47(tag) {
1139
1152
 
1140
1153
  // src/utils/image-validation.ts
1141
1154
  import { stat as stat3 } from "fs/promises";
1142
- import { extname as extname2 } from "path";
1155
+ import { extname as extname3 } from "path";
1143
1156
  var IMAGE_SIZE_LIMITS = {
1144
1157
  icon: { maxBytes: 1024 * 1024, label: "1 MB" },
1145
1158
  featureGraphic: { maxBytes: 1024 * 1024, label: "1 MB" },
@@ -1155,7 +1168,7 @@ var LARGE_IMAGE_THRESHOLD = 2 * 1024 * 1024;
1155
1168
  async function validateImage(filePath, imageType) {
1156
1169
  const errors = [];
1157
1170
  const warnings = [];
1158
- const ext = extname2(filePath).toLowerCase();
1171
+ const ext = extname3(filePath).toLowerCase();
1159
1172
  if (!VALID_EXTENSIONS.has(ext)) {
1160
1173
  errors.push(`Unsupported image format "${ext}". Use PNG or JPEG.`);
1161
1174
  }
@@ -1993,7 +2006,7 @@ function validateSku(sku) {
1993
2006
 
1994
2007
  // src/utils/release-notes.ts
1995
2008
  import { readdir as readdir3, readFile as readFile3, stat as stat5 } from "fs/promises";
1996
- import { extname as extname3, basename, join as join3 } from "path";
2009
+ import { extname as extname4, basename, join as join3 } from "path";
1997
2010
  var MAX_NOTES_LENGTH = 500;
1998
2011
  async function readReleaseNotesFromDir(dir) {
1999
2012
  let entries;
@@ -2009,7 +2022,7 @@ async function readReleaseNotesFromDir(dir) {
2009
2022
  }
2010
2023
  const notes = [];
2011
2024
  for (const entry of entries) {
2012
- if (extname3(entry) !== ".txt") continue;
2025
+ if (extname4(entry) !== ".txt") continue;
2013
2026
  const language = basename(entry, ".txt");
2014
2027
  const filePath = join3(dir, entry);
2015
2028
  const stats = await stat5(filePath);
@@ -2042,6 +2055,7 @@ function validateReleaseNotes(notes) {
2042
2055
  import { stat as stat6 } from "fs/promises";
2043
2056
  var STANDARD_TRACKS = /* @__PURE__ */ new Set([
2044
2057
  "internal",
2058
+ "qa",
2045
2059
  "alpha",
2046
2060
  "beta",
2047
2061
  "production",
@@ -2061,7 +2075,11 @@ var STANDARD_TRACKS = /* @__PURE__ */ new Set([
2061
2075
  "android_xr:internal",
2062
2076
  "android_xr:alpha",
2063
2077
  "android_xr:beta",
2064
- "android_xr:production"
2078
+ "android_xr:production",
2079
+ "google_play_games_pc:internal",
2080
+ "google_play_games_pc:alpha",
2081
+ "google_play_games_pc:beta",
2082
+ "google_play_games_pc:production"
2065
2083
  ]);
2066
2084
  var TRACK_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_:-]*$/;
2067
2085
  async function validatePreSubmission(options) {
@@ -2404,11 +2422,27 @@ function analyzeReviews(reviews) {
2404
2422
 
2405
2423
  // src/commands/reviews.ts
2406
2424
  async function listReviews(client, packageName, options) {
2407
- const apiOptions = {};
2408
- if (options?.translationLanguage) apiOptions.translationLanguage = options.translationLanguage;
2409
- if (options?.maxResults) apiOptions.maxResults = options.maxResults;
2410
- const response = await client.reviews.list(packageName, apiOptions);
2411
- let reviews = response.reviews || [];
2425
+ let reviews;
2426
+ if (options?.all) {
2427
+ const { items } = await paginateAll(async (pageToken) => {
2428
+ const apiOptions = { token: pageToken };
2429
+ if (options?.translationLanguage) apiOptions.translationLanguage = options.translationLanguage;
2430
+ if (options?.maxResults) apiOptions.maxResults = options.maxResults;
2431
+ const response = await client.reviews.list(packageName, apiOptions);
2432
+ return {
2433
+ items: response.reviews || [],
2434
+ nextPageToken: response.tokenPagination?.nextPageToken
2435
+ };
2436
+ }, { limit: options?.limit });
2437
+ reviews = items;
2438
+ } else {
2439
+ const apiOptions = {};
2440
+ if (options?.translationLanguage) apiOptions.translationLanguage = options.translationLanguage;
2441
+ if (options?.maxResults) apiOptions.maxResults = options.maxResults;
2442
+ if (options?.nextPage) apiOptions.token = options.nextPage;
2443
+ const response = await client.reviews.list(packageName, apiOptions);
2444
+ reviews = response.reviews || [];
2445
+ }
2412
2446
  if (options?.stars !== void 0) {
2413
2447
  reviews = reviews.filter((r) => {
2414
2448
  const userComment = r.comments?.[0]?.userComment;
@@ -3286,6 +3320,8 @@ async function listVoidedPurchases(client, packageName, options) {
3286
3320
  const resp = await client.purchases.listVoided(packageName, {
3287
3321
  startTime: options?.startTime,
3288
3322
  endTime: options?.endTime,
3323
+ type: options?.type,
3324
+ includeQuantityBasedPartialRefund: options?.includeQuantityBasedPartialRefund,
3289
3325
  maxResults: options?.maxResults,
3290
3326
  token: pageToken
3291
3327
  });
@@ -4755,13 +4791,19 @@ function extractComponents(appElement, tagName) {
4755
4791
  }
4756
4792
 
4757
4793
  // src/preflight/aab-reader.ts
4758
- var MANIFEST_PATH = "base/manifest/AndroidManifest.xml";
4794
+ var AAB_MANIFEST_PATH = "base/manifest/AndroidManifest.xml";
4795
+ var APK_MANIFEST_PATH = "AndroidManifest.xml";
4796
+ function detectManifestPath(filePath) {
4797
+ return filePath.toLowerCase().endsWith(".apk") ? APK_MANIFEST_PATH : AAB_MANIFEST_PATH;
4798
+ }
4759
4799
  async function readAab(aabPath) {
4760
- const { zipfile, entries, manifestBuf } = await openAndScan(aabPath);
4800
+ const manifestPath = detectManifestPath(aabPath);
4801
+ const { zipfile, entries, manifestBuf } = await openAndScan(aabPath, manifestPath);
4761
4802
  zipfile.close();
4762
4803
  if (!manifestBuf) {
4804
+ const fileType = aabPath.toLowerCase().endsWith(".apk") ? "APK" : "AAB";
4763
4805
  throw new Error(
4764
- `AAB is missing ${MANIFEST_PATH}. This does not appear to be a valid Android App Bundle.`
4806
+ `${fileType} is missing ${manifestPath}. This does not appear to be a valid Android ${fileType === "APK" ? "application package" : "App Bundle"}.`
4765
4807
  );
4766
4808
  }
4767
4809
  let manifest;
@@ -4793,7 +4835,7 @@ function createFallbackManifest() {
4793
4835
  providers: []
4794
4836
  };
4795
4837
  }
4796
- function openAndScan(aabPath) {
4838
+ function openAndScan(aabPath, manifestPath = AAB_MANIFEST_PATH) {
4797
4839
  return new Promise((resolve2, reject) => {
4798
4840
  yauzlOpen(aabPath, { lazyEntries: true, autoClose: false }, (err, zipfile) => {
4799
4841
  if (err || !zipfile) {
@@ -4821,7 +4863,7 @@ function openAndScan(aabPath) {
4821
4863
  uncompressedSize: entry.uncompressedSize
4822
4864
  });
4823
4865
  }
4824
- if (path === MANIFEST_PATH) {
4866
+ if (path === manifestPath) {
4825
4867
  zipfile.openReadStream(entry, (streamErr, stream) => {
4826
4868
  if (streamErr || !stream) {
4827
4869
  fail(streamErr ?? new Error("Failed to read manifest entry"));
@@ -5413,7 +5455,7 @@ import { readFile as readFile11 } from "fs/promises";
5413
5455
 
5414
5456
  // src/preflight/scan-files.ts
5415
5457
  import { readdir as readdir6, stat as stat9 } from "fs/promises";
5416
- import { join as join9, extname as extname4 } from "path";
5458
+ import { join as join9, extname as extname5 } from "path";
5417
5459
  var DEFAULT_SKIP_DIRS = /* @__PURE__ */ new Set([
5418
5460
  ".git",
5419
5461
  "node_modules",
@@ -5443,7 +5485,7 @@ async function collectSourceFiles(dir, extensions, skipDirs = DEFAULT_SKIP_DIRS,
5443
5485
  const sub = await collectSourceFiles(fullPath, extensions, skipDirs, maxDepth - 1);
5444
5486
  files.push(...sub);
5445
5487
  } else if (s.isFile()) {
5446
- const ext = extname4(entry).toLowerCase();
5488
+ const ext = extname5(entry).toLowerCase();
5447
5489
  if (extensions.has(ext) || entry.endsWith(".gradle.kts")) {
5448
5490
  files.push(fullPath);
5449
5491
  }
@@ -6283,7 +6325,7 @@ async function sendWebhook(config, payload, target) {
6283
6325
  }
6284
6326
 
6285
6327
  // src/commands/internal-sharing.ts
6286
- import { extname as extname5 } from "path";
6328
+ import { extname as extname6 } from "path";
6287
6329
  async function uploadInternalSharing(client, packageName, filePath, fileType) {
6288
6330
  const resolvedType = fileType ?? detectFileType(filePath);
6289
6331
  const validation = await validateUploadFile(filePath);
@@ -6310,7 +6352,7 @@ ${validation.errors.join("\n")}`,
6310
6352
  };
6311
6353
  }
6312
6354
  function detectFileType(filePath) {
6313
- const ext = extname5(filePath).toLowerCase();
6355
+ const ext = extname6(filePath).toLowerCase();
6314
6356
  if (ext === ".aab") return "bundle";
6315
6357
  if (ext === ".apk") return "apk";
6316
6358
  throw new GpcError(
@@ -7000,7 +7042,12 @@ function formatStatusDiff(diff, since) {
7000
7042
  }
7001
7043
  async function runWatchLoop(opts) {
7002
7044
  if (opts.intervalSeconds < 10) {
7003
- throw new Error("--watch interval must be at least 10 seconds");
7045
+ throw new GpcError(
7046
+ "--watch interval must be at least 10 seconds",
7047
+ "STATUS_USAGE_ERROR",
7048
+ 2,
7049
+ "Use --watch 10 or higher."
7050
+ );
7004
7051
  }
7005
7052
  let running = true;
7006
7053
  const cleanup = () => {
@@ -7135,6 +7182,99 @@ function formatChangelogEntry(entry) {
7135
7182
  lines.push(`Full notes: ${entry.url}`);
7136
7183
  return lines.join("\n");
7137
7184
  }
7185
+
7186
+ // src/commands/rtdn.ts
7187
+ var SUBSCRIPTION_NOTIFICATION_TYPES = {
7188
+ 1: "SUBSCRIPTION_RECOVERED",
7189
+ 2: "SUBSCRIPTION_RENEWED",
7190
+ 3: "SUBSCRIPTION_CANCELED",
7191
+ 4: "SUBSCRIPTION_PURCHASED",
7192
+ 5: "SUBSCRIPTION_ON_HOLD",
7193
+ 6: "SUBSCRIPTION_IN_GRACE_PERIOD",
7194
+ 7: "SUBSCRIPTION_RESTARTED",
7195
+ 8: "SUBSCRIPTION_PRICE_CHANGE_CONFIRMED",
7196
+ 9: "SUBSCRIPTION_DEFERRED",
7197
+ 10: "SUBSCRIPTION_PAUSED",
7198
+ 11: "SUBSCRIPTION_PAUSE_SCHEDULE_CHANGED",
7199
+ 12: "SUBSCRIPTION_REVOKED",
7200
+ 13: "SUBSCRIPTION_EXPIRED",
7201
+ 20: "SUBSCRIPTION_PENDING_PURCHASE_CANCELED"
7202
+ };
7203
+ var OTP_NOTIFICATION_TYPES = {
7204
+ 1: "ONE_TIME_PRODUCT_PURCHASED",
7205
+ 2: "ONE_TIME_PRODUCT_CANCELED"
7206
+ };
7207
+ async function getRtdnStatus(client, packageName) {
7208
+ try {
7209
+ const edit = await client.edits.insert(packageName);
7210
+ try {
7211
+ const details = await client.details.get(packageName, edit.id);
7212
+ return {
7213
+ topicName: null,
7214
+ enabled: false
7215
+ };
7216
+ } finally {
7217
+ await client.edits.delete(packageName, edit.id).catch(() => {
7218
+ });
7219
+ }
7220
+ } catch {
7221
+ return { topicName: null, enabled: false };
7222
+ }
7223
+ }
7224
+ function decodeNotification(base64Payload) {
7225
+ try {
7226
+ const json = Buffer.from(base64Payload, "base64").toString("utf-8");
7227
+ return JSON.parse(json);
7228
+ } catch {
7229
+ throw new GpcError(
7230
+ "Failed to decode notification payload. Expected base64-encoded JSON.",
7231
+ "RTDN_DECODE_ERROR",
7232
+ 1,
7233
+ "Ensure the payload is a valid base64-encoded Pub/Sub message from Google Play."
7234
+ );
7235
+ }
7236
+ }
7237
+ function formatNotification(notification) {
7238
+ const base = {
7239
+ packageName: notification.packageName,
7240
+ eventTime: notification.eventTimeMillis ? new Date(Number(notification.eventTimeMillis)).toISOString() : "-",
7241
+ version: notification.version
7242
+ };
7243
+ if (notification.subscriptionNotification) {
7244
+ const n = notification.subscriptionNotification;
7245
+ return {
7246
+ ...base,
7247
+ type: "subscription",
7248
+ event: SUBSCRIPTION_NOTIFICATION_TYPES[n.notificationType] || `UNKNOWN(${n.notificationType})`,
7249
+ subscriptionId: n.subscriptionId,
7250
+ purchaseToken: n.purchaseToken.slice(0, 16) + "..."
7251
+ };
7252
+ }
7253
+ if (notification.oneTimeProductNotification) {
7254
+ const n = notification.oneTimeProductNotification;
7255
+ return {
7256
+ ...base,
7257
+ type: "one-time-product",
7258
+ event: OTP_NOTIFICATION_TYPES[n.notificationType] || `UNKNOWN(${n.notificationType})`,
7259
+ sku: n.sku,
7260
+ purchaseToken: n.purchaseToken.slice(0, 16) + "..."
7261
+ };
7262
+ }
7263
+ if (notification.voidedPurchaseNotification) {
7264
+ const n = notification.voidedPurchaseNotification;
7265
+ return {
7266
+ ...base,
7267
+ type: "voided-purchase",
7268
+ event: "VOIDED_PURCHASE",
7269
+ orderId: n.orderId,
7270
+ purchaseToken: n.purchaseToken.slice(0, 16) + "..."
7271
+ };
7272
+ }
7273
+ if (notification.testNotification) {
7274
+ return { ...base, type: "test", event: "TEST_NOTIFICATION" };
7275
+ }
7276
+ return { ...base, type: "unknown" };
7277
+ }
7138
7278
  export {
7139
7279
  ApiError,
7140
7280
  ConfigError,
@@ -7190,6 +7330,7 @@ export {
7190
7330
  deactivateBasePlan,
7191
7331
  deactivateOffer,
7192
7332
  deactivatePurchaseOption,
7333
+ decodeNotification,
7193
7334
  deferSubscriptionPurchase,
7194
7335
  deferSubscriptionV2,
7195
7336
  deleteBasePlan,
@@ -7222,6 +7363,7 @@ export {
7222
7363
  formatCustomPayload,
7223
7364
  formatDiscordPayload,
7224
7365
  formatJunit,
7366
+ formatNotification,
7225
7367
  formatOutput,
7226
7368
  formatSlackPayload,
7227
7369
  formatStatusDiff,
@@ -7249,6 +7391,7 @@ export {
7249
7391
  getQuotaUsage,
7250
7392
  getReleasesStatus,
7251
7393
  getReview,
7394
+ getRtdnStatus,
7252
7395
  getSubscription,
7253
7396
  getSubscriptionAnalytics,
7254
7397
  getSubscriptionPurchase,