@gpc-cli/core 0.10.1 → 0.10.2

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
@@ -1,7 +1,7 @@
1
1
  import { OutputFormat, ResolvedConfig, WebhookConfig } from '@gpc-cli/config';
2
2
  import { AuthClient } from '@gpc-cli/auth';
3
3
  import { GpcPlugin, PluginManifest, CommandEvent, CommandResult, PluginError, RequestEvent, ResponseEvent, PluginCommand } from '@gpc-cli/plugin-sdk';
4
- import { PlayApiClient, EditCommitOptions, Track, ExternallyHostedApk, ExternallyHostedApkResponse, DeobfuscationFileType, UploadProgressEvent, ResumableUploadOptions, Listing, ImageType, CountryAvailability, Image, AppDetails, Review, ReviewReplyResponse, Subscription, SubscriptionOffer, OffersListResponse, BasePlanMigratePricesRequest, InAppProduct, Order, SubscriptionDeferResponse, SubscriptionsV2DeferResponse, ProductPurchase, ProductPurchaseV2, SubscriptionPurchaseV2, VoidedPurchase, UsersApiClient, User, DeveloperLevelPermission, Grant, MetricRow, ReportingDimension, ReportingAggregation, VitalsMetricSet, ReportingApiClient, AnomalyDetectionResponse, MetricSetResponse, ErrorIssuesResponse, ConvertRegionPricesResponse, ReportType, StatsDimension, Testers, AppRecoveryTargeting, AppRecoveryAction, CreateAppRecoveryActionRequest, DataSafetyResponse, DataSafetyRequest, ExternalTransaction, ExternalTransactionRefund, DeviceTierConfig, OneTimeOffer, OneTimeProduct, OneTimeOffersListResponse, OneTimeProductsListResponse, GamesConfigClient, AchievementConfiguration, LeaderboardConfiguration, AchievementConfigurationListResponse, GamesApiClient, Achievement, LeaderboardConfigurationListResponse, Leaderboard, ImageConfiguration, EnterpriseApiClient, CustomApp, GeneratedApksPerSigningKey, Bundle } from '@gpc-cli/api';
4
+ import { PlayApiClient, EditCommitOptions, Track, ExternallyHostedApk, ExternallyHostedApkResponse, DeobfuscationFileType, UploadProgressEvent, ResumableUploadOptions, Listing, ImageType, CountryAvailability, Image, AppDetails, Review, ReviewReplyResponse, Subscription, SubscriptionOffer, OffersListResponse, BasePlanMigratePricesRequest, InAppProduct, ConsumptionUsageEvent, RefundPreference, Order, SubscriptionDeferResponse, SubscriptionsV2DeferResponse, ProductPurchase, ProductPurchaseV2, SubscriptionPurchaseV2, VoidedPurchase, UsersApiClient, User, DeveloperLevelPermission, Grant, MetricRow, ReportingDimension, ReportingAggregation, VitalsMetricSet, ReportingApiClient, AnomalyDetectionResponse, MetricSetResponse, ErrorIssuesResponse, ConvertRegionPricesResponse, ReportType, StatsDimension, Testers, AppRecoveryTargeting, AppRecoveryAction, CreateAppRecoveryActionRequest, DataSafetyResponse, DataSafetyRequest, ExternalTransaction, ExternalTransactionRefund, DeviceTierConfig, OneTimeOffer, OneTimeProduct, OneTimeOffersListResponse, OneTimeProductsListResponse, GamesConfigClient, AchievementConfiguration, LeaderboardConfiguration, AchievementConfigurationListResponse, GamesApiClient, Achievement, LeaderboardConfigurationListResponse, Leaderboard, ImageConfiguration, EnterpriseApiClient, CustomApp, GeneratedApksPerSigningKey, Bundle } from '@gpc-cli/api';
5
5
 
6
6
  declare class GpcError extends Error {
7
7
  readonly code: string;
@@ -367,7 +367,9 @@ declare function pushListings(client: PlayApiClient, packageName: string, dir: s
367
367
  commitOptions?: EditCommitOptions;
368
368
  }): Promise<PushResult | DryRunResult>;
369
369
  declare function listImages(client: PlayApiClient, packageName: string, language: string, imageType: ImageType): Promise<Image[]>;
370
- declare function uploadImage(client: PlayApiClient, packageName: string, language: string, imageType: ImageType, filePath: string, commitOptions?: EditCommitOptions): Promise<Image>;
370
+ declare function uploadImage(client: PlayApiClient, packageName: string, language: string, imageType: ImageType, filePath: string, commitOptions?: EditCommitOptions, uploadOptions?: {
371
+ aiGenerated?: boolean;
372
+ }): Promise<Image>;
371
373
  declare function deleteImage(client: PlayApiClient, packageName: string, language: string, imageType: ImageType, imageId: string, commitOptions?: EditCommitOptions): Promise<void>;
372
374
  declare function diffListingsCommand(client: PlayApiClient, packageName: string, dir: string): Promise<ListingDiff[]>;
373
375
  declare function getCountryAvailability(client: PlayApiClient, packageName: string, track: string): Promise<CountryAvailability>;
@@ -661,6 +663,32 @@ declare function batchGetOrders(client: PlayApiClient, packageName: string, orde
661
663
  declare function getProductPurchaseV2(client: PlayApiClient, packageName: string, token: string): Promise<ProductPurchaseV2>;
662
664
  declare function cancelSubscriptionV2(client: PlayApiClient, packageName: string, token: string, cancellationType?: string): Promise<void>;
663
665
  declare function deferSubscriptionV2(client: PlayApiClient, packageName: string, token: string, deferDuration: string, etag?: string): Promise<SubscriptionsV2DeferResponse>;
666
+ /** CLI-facing refund preference, mapped to the Google Play API enum. */
667
+ type RefundReviewPreference = "approve" | "decline" | "neutral";
668
+ interface ReviewOrderRefundOptions {
669
+ /** Token from the PendingRefundReviewNotification RTDN. */
670
+ pendingRefundToken: string;
671
+ preference: RefundReviewPreference;
672
+ /** Whether a free sample, trial, or functionality info was offered before purchase. */
673
+ sampleContentProvided: boolean;
674
+ consumptionPercentageMilliunits?: number;
675
+ consumptionUsageEvents?: ConsumptionUsageEvent[];
676
+ }
677
+ interface ReviewOrderRefundResult {
678
+ packageName: string;
679
+ orderId: string;
680
+ refundPreference: RefundPreference;
681
+ sampleContentProvided: boolean;
682
+ consumptionPercentageMilliunits?: number;
683
+ consumptionUsageEventCount: number;
684
+ submitted: boolean;
685
+ }
686
+ /**
687
+ * Respond to a chargeback review (PendingRefundReviewNotification) with a refund
688
+ * preference and optional purchase-usage evidence. Google Play expects a response
689
+ * within 24 hours of the notification.
690
+ */
691
+ declare function reviewOrderRefund(client: PlayApiClient, packageName: string, orderId: string, options: ReviewOrderRefundOptions): Promise<ReviewOrderRefundResult>;
664
692
 
665
693
  declare const PERMISSION_PROPAGATION_WARNING = "Note: Permission changes may take up to 48 hours to propagate.";
666
694
  interface ListUsersOptions {
@@ -1005,7 +1033,9 @@ declare function refundExternalTransaction(client: PlayApiClient, packageName: s
1005
1033
 
1006
1034
  declare function listDeviceTiers(client: PlayApiClient, packageName: string): Promise<DeviceTierConfig[]>;
1007
1035
  declare function getDeviceTier(client: PlayApiClient, packageName: string, configId: string): Promise<DeviceTierConfig>;
1008
- declare function createDeviceTier(client: PlayApiClient, packageName: string, config: DeviceTierConfig): Promise<DeviceTierConfig>;
1036
+ declare function createDeviceTier(client: PlayApiClient, packageName: string, config: DeviceTierConfig, options?: {
1037
+ allowUnknownDevices?: boolean;
1038
+ }): Promise<DeviceTierConfig>;
1009
1039
 
1010
1040
  declare function listOneTimeProducts(client: PlayApiClient, packageName: string): Promise<OneTimeProductsListResponse>;
1011
1041
  declare function getOneTimeProduct(client: PlayApiClient, packageName: string, productId: string): Promise<OneTimeProduct>;
@@ -1771,6 +1801,16 @@ interface DecodedNotification {
1771
1801
  productType: number;
1772
1802
  refundType?: number;
1773
1803
  };
1804
+ /** Chargeback review request — respond within 24h via `orders review-refund`. */
1805
+ pendingRefundReviewNotification?: {
1806
+ version: string;
1807
+ pendingRefundToken: string;
1808
+ orderId: string;
1809
+ /** Pending reviews currently only use CHARGEBACK (7). */
1810
+ refundReason?: number;
1811
+ obfuscatedAccountId?: string;
1812
+ obfuscatedProfileId?: string;
1813
+ };
1774
1814
  testNotification?: {
1775
1815
  version: string;
1776
1816
  };
@@ -1827,6 +1867,8 @@ interface ImageSyncOptions {
1827
1867
  delete?: boolean;
1828
1868
  dryRun?: boolean;
1829
1869
  commitOptions?: EditCommitOptions;
1870
+ /** Attest that the synced images were (or were not) generated by AI. */
1871
+ aiGenerated?: boolean;
1830
1872
  }
1831
1873
  interface ImageSyncDetail {
1832
1874
  language: string;
@@ -1935,4 +1977,4 @@ declare function readinessBadgeColor(grade: ReadinessGrade): string;
1935
1977
  declare function readinessBadgeUrl(score: ReadinessScore): string;
1936
1978
  declare function readinessBadgeMarkdown(score: ReadinessScore): string;
1937
1979
 
1938
- export { ApiError, type ApiSigningFingerprint, type AppInfo, type AppStatus, type ApplyReleaseNotesResult, type AuditEntry, type BatchSyncResult, type BundleAnalysis, type BundleComparison, type BundleEntry, type BundleSizeCheckResult, type BundleSizeConfig, type BundlesWaitOptions, type ChangelogEntry, type ChecklistInput, type ChecklistItem, type ChecklistResult, type CommandContext, type CommitCluster, type CommitResult, ConfigError, type CreateEnterpriseAppParams, DEFAULT_LIMITS, DEFAULT_MODELS, DEFAULT_PREFLIGHT_CONFIG, DEFAULT_WATCH_THRESHOLDS, type DecodedNotification, type DiffToken, type DiscoverPluginsOptions, type DiscoveredPlugin, type DryRunPublishResult, type DryRunResult, type DryRunUploadResult, type ErrorReason, type ExportImagesOptions, type ExportImagesSummary, type FastlaneDetection, type FastlaneLane, type FetchChangelogOptions, type FieldLintResult, type FileValidationResult, type FinancialReportDownload, type FindingSeverity, GOOGLE_PLAY_LANGUAGES, type GameConfigDiff, type GamesPushOptions, type GamesSyncResult, type GenerateOptions, type GeneratedChangelog, type GetAppStatusOptions, type GitNotesOptions, type GitReleaseNotes, type GitRunner, GpcError, type ImageSyncDetail, type ImageSyncOptions, type ImageSyncResult, type ImageValidationResult, type InitOptions, type InitResult, type InternalSharingUploadResult, type KeystoreFingerprint, type ListGamesConfigOptions, type ListIapOptions, type ListReportsResult, type ListSubscriptionsOptions, type ListUsersOptions, type ListVoidedOptions, type ListingDiff, type ListingFieldLimits, type ListingLintResult, type ListingsResult, type LoadedPlugin, type LocaleBundle, type LocaleEntry, type MigrationResult, NetworkError, type OneTimeProductDiff, type OutputMode, PERMISSION_PROPAGATION_WARNING, PLACEHOLDER_TEXT, PLAY_STORE_LIMIT, PROVIDER_WHITELIST, type ParsedCommit, type ParsedManifest, type ParsedMonth, type PlayStoreFormat, type PlayStoreRenderOptions, PluginManager, type PreflightConfig, type PreflightFinding, type PreflightOptions, type PreflightResult, type PreflightScanner, type Provider, type PublishOptions, type PublishResult, type PushResult, type QuotaUsage, RENDERERS, REVIEW_SORT_PRESETS, type RawCommit, type ReadinessBreakdownEntry, type ReadinessGrade, type ReadinessScore, type ReadinessSignal, type ReadinessSignalStatus, type ReleaseDiff, type ReleaseNotesValidation, type ReleaseStatusResult, type Renderer, type ReportObject, type ReportsAuth, type ResolveAiConfigOptions, type ResolveLocalesOptions, type ReviewAnalysis, type ReviewExportOptions, type ReviewSortPreset, type ReviewsFilterOptions, type RevocationRefundType, type RtdnStatus, SECTION_ORDER, SENSITIVE_ARG_KEYS, SENSITIVE_KEYS, SEVERITY_ORDER, type ScaffoldOptions, type ScaffoldResult, type SigningConsistencyResult, type SigningKeyComparison, type Spinner, type StatsReportDownload, type StatusDiff, type StatusRelease, type StatusReviews, type StatusVitalMetric, type SubscriptionAnalytics, type SubscriptionDiff, type SyncResult, type ThresholdResult, type TrainConfig, type TrainState, type TranslateBundleOptions, type TranslatedBundle, type TranslationFailure, type TranslationPath, type TranslationResult, type Translator, type TranslatorConfig, type UploadResult, VALID_WATCH_METRICS, type ValidateCheck, type ValidateOptions, type ValidateResult, type ValidatedPluginApproval, type VersionVitalsComparison, type VersionVitalsRow, type VitalsOverview, type VitalsQueryOptions, type VitalsTrendComparison, type WatchAction, type WatchCallbacks, type WatchConfig, type WatchEvent, type WatchMetric, type WatchOptions, type WatchRollout, type WatchSummary, type WatchVitalReading, type WatchVitalsOptions, type WebhookPayload, type ZipCsvEntry, abortTrain, acknowledgeProductPurchase, activateBasePlan, activateOffer, addRecoveryTargeting, addTesters, advanceTrain, analyzeBundle, analyzeRemoteListings, analyzeReviews, annotateListResult, applyReleaseNotes, assignRelease, batchGetOrders, batchSyncInAppProducts, buildChecklist, buildLocaleBundle, bundleToReleaseNotes, cancelRecoveryAction, cancelSubscriptionPurchase, cancelSubscriptionV2, checkBundleSize, checkSigningConsistency, checkThreshold, classifyError, clearAuditLog, commitWithRescue, compareBundles, compareFingerprints, compareVersionVitals, compareVitalsTrend, computeStatusDiff, consumeProductPurchase, convertRegionPrices, createAchievementConfig, createAuditEntry, createDeviceTier, createEnterpriseApp, createExternalTransaction, createGrant, createInAppProduct, createLeaderboardConfig, createOffer, createOneTimeOffer, createOneTimeProduct, createRecoveryAction, createSpinner, createSubscription, createTrack, createTranslator, deactivateBasePlan, deactivateOffer, decodeCsvText, decodeNotification, decodeStatsCsv, defaultGitRunner, deferSubscriptionPurchase, deferSubscriptionV2, deleteAchievementConfig, deleteBasePlan, deleteGrant, deleteImage, deleteInAppProduct, deleteLeaderboardConfig, deleteListing, deleteOffer, deleteOneTimeOffer, deleteOneTimeProduct, deleteSubscription, deployRecoveryAction, detectFastlane, detectOutputFormat, diffAchievementConfig, diffLeaderboardConfig, diffListings, diffListingsCommand, diffListingsEnhanced, diffOneTimeProduct, diffReleases, diffSubscription, discoverPluginEntries, discoverPlugins, dispatchWebhook, downloadFinancialReport, downloadGeneratedApk, downloadReportObject, downloadStatsReport, exportImages, exportReviews, extractCsvEntriesFromZip, fetchAggregateCost, fetchChangelog, fetchReleaseNotes, findBundle, formatChangelogEntry, formatCustomPayload, formatDiscordPayload, formatJunit, formatMoney, formatNotification, formatOutput, formatPathLabel, formatSlackPayload, formatStatusDiff, formatStatusSummary, formatStatusTable, formatWordDiff, generateChangelog, generateMigrationPlan, generateNotesFromGit, getAchievementConfig, getAllScannerNames, getApiSigningFingerprint, getAppInfo, getAppStatus, getCountryAvailability, getDeviceTier, getExternalTransaction, getInAppProduct, getKeystoreFingerprint, getLeaderboardConfig, getListings, getOffer, getOneTimeOffer, getOneTimeProduct, getOrderDetails, getProductPurchase, getProductPurchaseV2, getQuotaUsage, getReleasesStatus, getReview, getRtdnStatus, getSubscription, getSubscriptionAnalytics, getSubscriptionPurchase, getTrainStatus, getUser, getVitalsAnomalies, getVitalsAnr, getVitalsBattery, getVitalsCrashes, getVitalsErrorCount, getVitalsLmk, getVitalsMemory, getVitalsOverview, getVitalsRendering, getVitalsStartup, gradeFromPercent, gunzipIfNeeded, handleBreach, importDataSafety, importTestersFromCsv, initAudit, initProject, inviteUser, isFinancialReportType, isGzip, isStatsReportType, isValidBcp47, isValidReportType, isValidStatsDimension, isVersionedNotesDir, isZip, lintListing, lintListings, lintLocalListings, listAchievementConfigs, listAchievements, listAuditEvents, listBundles, listDeviceTiers, listGeneratedApks, listGrants, listImages, listInAppProducts, listLeaderboardConfigs, listLeaderboards, listOffers, listOneTimeOffers, listOneTimeProducts, listRecoveryActions, listReportObjects, listReports, listReviews, listSubscriptions, listTesters, listTracks, listUsers, listVoidedPurchases, loadPreflightConfig, loadStatusCache, maybePaginate, migratePrices, monthToken, moreResultsFooter, normalizeFingerprint, parseAppfile, parseCommit, parseFastfile, parseGrantArg, parseKeytoolOutput, parseMonth, parseRemoteUrl, pauseTrain, promoteRelease, publish, publishEnterpriseApp, pullAchievementConfigs, pullLeaderboardConfigs, pullListings, pushAchievementConfigs, pushLeaderboardConfigs, pushListings, readListingsFromDir, readReleaseNotesForVersion, readReleaseNotesFromDir, readinessBadgeColor, readinessBadgeMarkdown, readinessBadgeUrl, redactAuditArgs, redactSensitive, refundExternalTransaction, refundOrder, relativeTime, removeTesters, removeUser, renderChecklistMarkdown, renderJson, renderMarkdown, renderPlayStore, renderPlayStoreJson, renderPlayStoreMd, renderPlayStorePrompt, renderPrompt, replyToReview, reportAccessDeniedError, reportBucketNotFoundError, reportObjectNotFoundError, reportPrefix, resolveAiConfig, resolveLocales, resolveReportsBucket, retryOnUploadNotComplete, revokeSubscriptionPurchase, runPreflight, runWatch, runWatchLoop, safePath, safePathWithin, saveStatusCache, scaffoldPlugin, scoreReadiness, searchAuditEvents, searchVitalsErrors, sendNotification, sendWebhook, setAchievementIcon, setLeaderboardIcon, sha256File, sortResults, sortReviews, startTrain, statusHasBreach, syncImages, syncInAppProducts, topFiles, trackBreachState, translateBundle, updateAchievementConfig, updateAppDetails, updateDataSafety, updateGrant, updateInAppProduct, updateLeaderboardConfig, updateListing, updateOffer, updateOneTimeOffer, updateOneTimeProduct, updateRollout, updateSubscription, updateTrackConfig, updateUser, uploadExternallyHosted, uploadImage, uploadInternalSharing, uploadRelease, validateAndCommit, validateBundleForApply, validateImage, validateLanguageCode, validatePackageName, validatePluginForApproval, validatePreSubmission, validateReleaseNotes, validateSku, validateTrackName, validateUploadFile, validateVersionCode, waitForBundle, waitForBundleProcessing, watchVitalsWithAutoHalt, wordDiff, writeAuditLog, writeListingsToDir, writeMigrationOutput };
1980
+ export { ApiError, type ApiSigningFingerprint, type AppInfo, type AppStatus, type ApplyReleaseNotesResult, type AuditEntry, type BatchSyncResult, type BundleAnalysis, type BundleComparison, type BundleEntry, type BundleSizeCheckResult, type BundleSizeConfig, type BundlesWaitOptions, type ChangelogEntry, type ChecklistInput, type ChecklistItem, type ChecklistResult, type CommandContext, type CommitCluster, type CommitResult, ConfigError, type CreateEnterpriseAppParams, DEFAULT_LIMITS, DEFAULT_MODELS, DEFAULT_PREFLIGHT_CONFIG, DEFAULT_WATCH_THRESHOLDS, type DecodedNotification, type DiffToken, type DiscoverPluginsOptions, type DiscoveredPlugin, type DryRunPublishResult, type DryRunResult, type DryRunUploadResult, type ErrorReason, type ExportImagesOptions, type ExportImagesSummary, type FastlaneDetection, type FastlaneLane, type FetchChangelogOptions, type FieldLintResult, type FileValidationResult, type FinancialReportDownload, type FindingSeverity, GOOGLE_PLAY_LANGUAGES, type GameConfigDiff, type GamesPushOptions, type GamesSyncResult, type GenerateOptions, type GeneratedChangelog, type GetAppStatusOptions, type GitNotesOptions, type GitReleaseNotes, type GitRunner, GpcError, type ImageSyncDetail, type ImageSyncOptions, type ImageSyncResult, type ImageValidationResult, type InitOptions, type InitResult, type InternalSharingUploadResult, type KeystoreFingerprint, type ListGamesConfigOptions, type ListIapOptions, type ListReportsResult, type ListSubscriptionsOptions, type ListUsersOptions, type ListVoidedOptions, type ListingDiff, type ListingFieldLimits, type ListingLintResult, type ListingsResult, type LoadedPlugin, type LocaleBundle, type LocaleEntry, type MigrationResult, NetworkError, type OneTimeProductDiff, type OutputMode, PERMISSION_PROPAGATION_WARNING, PLACEHOLDER_TEXT, PLAY_STORE_LIMIT, PROVIDER_WHITELIST, type ParsedCommit, type ParsedManifest, type ParsedMonth, type PlayStoreFormat, type PlayStoreRenderOptions, PluginManager, type PreflightConfig, type PreflightFinding, type PreflightOptions, type PreflightResult, type PreflightScanner, type Provider, type PublishOptions, type PublishResult, type PushResult, type QuotaUsage, RENDERERS, REVIEW_SORT_PRESETS, type RawCommit, type ReadinessBreakdownEntry, type ReadinessGrade, type ReadinessScore, type ReadinessSignal, type ReadinessSignalStatus, type RefundReviewPreference, type ReleaseDiff, type ReleaseNotesValidation, type ReleaseStatusResult, type Renderer, type ReportObject, type ReportsAuth, type ResolveAiConfigOptions, type ResolveLocalesOptions, type ReviewAnalysis, type ReviewExportOptions, type ReviewOrderRefundOptions, type ReviewOrderRefundResult, type ReviewSortPreset, type ReviewsFilterOptions, type RevocationRefundType, type RtdnStatus, SECTION_ORDER, SENSITIVE_ARG_KEYS, SENSITIVE_KEYS, SEVERITY_ORDER, type ScaffoldOptions, type ScaffoldResult, type SigningConsistencyResult, type SigningKeyComparison, type Spinner, type StatsReportDownload, type StatusDiff, type StatusRelease, type StatusReviews, type StatusVitalMetric, type SubscriptionAnalytics, type SubscriptionDiff, type SyncResult, type ThresholdResult, type TrainConfig, type TrainState, type TranslateBundleOptions, type TranslatedBundle, type TranslationFailure, type TranslationPath, type TranslationResult, type Translator, type TranslatorConfig, type UploadResult, VALID_WATCH_METRICS, type ValidateCheck, type ValidateOptions, type ValidateResult, type ValidatedPluginApproval, type VersionVitalsComparison, type VersionVitalsRow, type VitalsOverview, type VitalsQueryOptions, type VitalsTrendComparison, type WatchAction, type WatchCallbacks, type WatchConfig, type WatchEvent, type WatchMetric, type WatchOptions, type WatchRollout, type WatchSummary, type WatchVitalReading, type WatchVitalsOptions, type WebhookPayload, type ZipCsvEntry, abortTrain, acknowledgeProductPurchase, activateBasePlan, activateOffer, addRecoveryTargeting, addTesters, advanceTrain, analyzeBundle, analyzeRemoteListings, analyzeReviews, annotateListResult, applyReleaseNotes, assignRelease, batchGetOrders, batchSyncInAppProducts, buildChecklist, buildLocaleBundle, bundleToReleaseNotes, cancelRecoveryAction, cancelSubscriptionPurchase, cancelSubscriptionV2, checkBundleSize, checkSigningConsistency, checkThreshold, classifyError, clearAuditLog, commitWithRescue, compareBundles, compareFingerprints, compareVersionVitals, compareVitalsTrend, computeStatusDiff, consumeProductPurchase, convertRegionPrices, createAchievementConfig, createAuditEntry, createDeviceTier, createEnterpriseApp, createExternalTransaction, createGrant, createInAppProduct, createLeaderboardConfig, createOffer, createOneTimeOffer, createOneTimeProduct, createRecoveryAction, createSpinner, createSubscription, createTrack, createTranslator, deactivateBasePlan, deactivateOffer, decodeCsvText, decodeNotification, decodeStatsCsv, defaultGitRunner, deferSubscriptionPurchase, deferSubscriptionV2, deleteAchievementConfig, deleteBasePlan, deleteGrant, deleteImage, deleteInAppProduct, deleteLeaderboardConfig, deleteListing, deleteOffer, deleteOneTimeOffer, deleteOneTimeProduct, deleteSubscription, deployRecoveryAction, detectFastlane, detectOutputFormat, diffAchievementConfig, diffLeaderboardConfig, diffListings, diffListingsCommand, diffListingsEnhanced, diffOneTimeProduct, diffReleases, diffSubscription, discoverPluginEntries, discoverPlugins, dispatchWebhook, downloadFinancialReport, downloadGeneratedApk, downloadReportObject, downloadStatsReport, exportImages, exportReviews, extractCsvEntriesFromZip, fetchAggregateCost, fetchChangelog, fetchReleaseNotes, findBundle, formatChangelogEntry, formatCustomPayload, formatDiscordPayload, formatJunit, formatMoney, formatNotification, formatOutput, formatPathLabel, formatSlackPayload, formatStatusDiff, formatStatusSummary, formatStatusTable, formatWordDiff, generateChangelog, generateMigrationPlan, generateNotesFromGit, getAchievementConfig, getAllScannerNames, getApiSigningFingerprint, getAppInfo, getAppStatus, getCountryAvailability, getDeviceTier, getExternalTransaction, getInAppProduct, getKeystoreFingerprint, getLeaderboardConfig, getListings, getOffer, getOneTimeOffer, getOneTimeProduct, getOrderDetails, getProductPurchase, getProductPurchaseV2, getQuotaUsage, getReleasesStatus, getReview, getRtdnStatus, getSubscription, getSubscriptionAnalytics, getSubscriptionPurchase, getTrainStatus, getUser, getVitalsAnomalies, getVitalsAnr, getVitalsBattery, getVitalsCrashes, getVitalsErrorCount, getVitalsLmk, getVitalsMemory, getVitalsOverview, getVitalsRendering, getVitalsStartup, gradeFromPercent, gunzipIfNeeded, handleBreach, importDataSafety, importTestersFromCsv, initAudit, initProject, inviteUser, isFinancialReportType, isGzip, isStatsReportType, isValidBcp47, isValidReportType, isValidStatsDimension, isVersionedNotesDir, isZip, lintListing, lintListings, lintLocalListings, listAchievementConfigs, listAchievements, listAuditEvents, listBundles, listDeviceTiers, listGeneratedApks, listGrants, listImages, listInAppProducts, listLeaderboardConfigs, listLeaderboards, listOffers, listOneTimeOffers, listOneTimeProducts, listRecoveryActions, listReportObjects, listReports, listReviews, listSubscriptions, listTesters, listTracks, listUsers, listVoidedPurchases, loadPreflightConfig, loadStatusCache, maybePaginate, migratePrices, monthToken, moreResultsFooter, normalizeFingerprint, parseAppfile, parseCommit, parseFastfile, parseGrantArg, parseKeytoolOutput, parseMonth, parseRemoteUrl, pauseTrain, promoteRelease, publish, publishEnterpriseApp, pullAchievementConfigs, pullLeaderboardConfigs, pullListings, pushAchievementConfigs, pushLeaderboardConfigs, pushListings, readListingsFromDir, readReleaseNotesForVersion, readReleaseNotesFromDir, readinessBadgeColor, readinessBadgeMarkdown, readinessBadgeUrl, redactAuditArgs, redactSensitive, refundExternalTransaction, refundOrder, relativeTime, removeTesters, removeUser, renderChecklistMarkdown, renderJson, renderMarkdown, renderPlayStore, renderPlayStoreJson, renderPlayStoreMd, renderPlayStorePrompt, renderPrompt, replyToReview, reportAccessDeniedError, reportBucketNotFoundError, reportObjectNotFoundError, reportPrefix, resolveAiConfig, resolveLocales, resolveReportsBucket, retryOnUploadNotComplete, reviewOrderRefund, revokeSubscriptionPurchase, runPreflight, runWatch, runWatchLoop, safePath, safePathWithin, saveStatusCache, scaffoldPlugin, scoreReadiness, searchAuditEvents, searchVitalsErrors, sendNotification, sendWebhook, setAchievementIcon, setLeaderboardIcon, sha256File, sortResults, sortReviews, startTrain, statusHasBreach, syncImages, syncInAppProducts, topFiles, trackBreachState, translateBundle, updateAchievementConfig, updateAppDetails, updateDataSafety, updateGrant, updateInAppProduct, updateLeaderboardConfig, updateListing, updateOffer, updateOneTimeOffer, updateOneTimeProduct, updateRollout, updateSubscription, updateTrackConfig, updateUser, uploadExternallyHosted, uploadImage, uploadInternalSharing, uploadRelease, validateAndCommit, validateBundleForApply, validateImage, validateLanguageCode, validatePackageName, validatePluginForApproval, validatePreSubmission, validateReleaseNotes, validateSku, validateTrackName, validateUploadFile, validateVersionCode, waitForBundle, waitForBundleProcessing, watchVitalsWithAutoHalt, wordDiff, writeAuditLog, writeListingsToDir, writeMigrationOutput };
package/dist/index.js CHANGED
@@ -1324,7 +1324,7 @@ async function listImages(client, packageName, language, imageType) {
1324
1324
  throw error;
1325
1325
  }
1326
1326
  }
1327
- async function uploadImage(client, packageName, language, imageType, filePath, commitOptions) {
1327
+ async function uploadImage(client, packageName, language, imageType, filePath, commitOptions, uploadOptions) {
1328
1328
  validateLanguage(language);
1329
1329
  const imageCheck = await validateImage(filePath, imageType);
1330
1330
  if (!imageCheck.valid) {
@@ -1340,7 +1340,14 @@ async function uploadImage(client, packageName, language, imageType, filePath, c
1340
1340
  }
1341
1341
  const edit = await client.edits.insert(packageName);
1342
1342
  try {
1343
- const image = await client.images.upload(packageName, edit.id, language, imageType, filePath);
1343
+ const image = await client.images.upload(
1344
+ packageName,
1345
+ edit.id,
1346
+ language,
1347
+ imageType,
1348
+ filePath,
1349
+ uploadOptions
1350
+ );
1344
1351
  await validateAndCommit(client, packageName, edit.id, commitOptions);
1345
1352
  return image;
1346
1353
  } catch (error) {
@@ -3979,6 +3986,87 @@ async function deferSubscriptionV2(client, packageName, token, deferDuration, et
3979
3986
  deferralContext: { etag: resolvedEtag, deferDuration }
3980
3987
  });
3981
3988
  }
3989
+ var REFUND_PREFERENCES = {
3990
+ approve: "APPROVE",
3991
+ decline: "DECLINE",
3992
+ neutral: "NEUTRAL"
3993
+ };
3994
+ var MAX_CONSUMPTION_USAGE_EVENTS = 1e3;
3995
+ var MAX_CONSUMPTION_MILLIUNITS = 1e5;
3996
+ var RFC3339 = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$/;
3997
+ function invalidReviewRefund(message, suggestion) {
3998
+ return new GpcError(message, "ORDER_REVIEW_REFUND_INVALID", 2, suggestion);
3999
+ }
4000
+ async function reviewOrderRefund(client, packageName, orderId, options) {
4001
+ validatePackageName(packageName);
4002
+ if (!orderId.trim()) {
4003
+ throw invalidReviewRefund(
4004
+ "orderId is required",
4005
+ "Pass the order ID from the pending refund review notification"
4006
+ );
4007
+ }
4008
+ const pendingRefundToken = options.pendingRefundToken.trim();
4009
+ if (!pendingRefundToken) {
4010
+ throw invalidReviewRefund(
4011
+ "pendingRefundToken is required and cannot be empty",
4012
+ "Pass --pending-refund-token with the token from the PendingRefundReviewNotification"
4013
+ );
4014
+ }
4015
+ const refundPreference = REFUND_PREFERENCES[options.preference];
4016
+ if (!refundPreference) {
4017
+ throw invalidReviewRefund(
4018
+ `Invalid refund preference "${options.preference}"`,
4019
+ "Use --preference with one of: approve, decline, neutral"
4020
+ );
4021
+ }
4022
+ const milliunits = options.consumptionPercentageMilliunits;
4023
+ if (milliunits !== void 0) {
4024
+ if (!Number.isInteger(milliunits) || milliunits < 0 || milliunits > MAX_CONSUMPTION_MILLIUNITS) {
4025
+ throw invalidReviewRefund(
4026
+ `consumptionPercentageMilliunits must be an integer between 0 and ${MAX_CONSUMPTION_MILLIUNITS}, got ${milliunits}`,
4027
+ "Pass --consumption-percent with a value between 0 and 100"
4028
+ );
4029
+ }
4030
+ }
4031
+ const events = options.consumptionUsageEvents ?? [];
4032
+ if (events.length > MAX_CONSUMPTION_USAGE_EVENTS) {
4033
+ throw invalidReviewRefund(
4034
+ `consumptionUsageEvents has ${events.length} entries; Google Play rejects lists over ${MAX_CONSUMPTION_USAGE_EVENTS}`,
4035
+ `Trim the usage-events file to at most ${MAX_CONSUMPTION_USAGE_EVENTS} events`
4036
+ );
4037
+ }
4038
+ events.forEach((event, index) => {
4039
+ const time = event.consumptionTime;
4040
+ if (time !== void 0 && (!RFC3339.test(time) || Number.isNaN(Date.parse(time)))) {
4041
+ throw invalidReviewRefund(
4042
+ `consumptionUsageEvents[${index}].consumptionTime "${time}" is not an RFC 3339 timestamp`,
4043
+ "Use a timestamp like 2026-08-30T10:15:00Z"
4044
+ );
4045
+ }
4046
+ if (event.location && !event.location.regionCode?.trim()) {
4047
+ throw invalidReviewRefund(
4048
+ `consumptionUsageEvents[${index}].location.regionCode is required when a location is supplied`,
4049
+ 'Set a CLDR region code such as "US" on each location'
4050
+ );
4051
+ }
4052
+ });
4053
+ await client.orders.reviewRefund(packageName, orderId, {
4054
+ pendingRefundToken,
4055
+ refundPreference,
4056
+ sampleContentProvided: options.sampleContentProvided,
4057
+ ...milliunits !== void 0 ? { consumptionPercentageMilliunits: milliunits } : {},
4058
+ ...events.length > 0 ? { consumptionUsageEvents: events } : {}
4059
+ });
4060
+ return {
4061
+ packageName,
4062
+ orderId,
4063
+ refundPreference,
4064
+ sampleContentProvided: options.sampleContentProvided,
4065
+ ...milliunits !== void 0 ? { consumptionPercentageMilliunits: milliunits } : {},
4066
+ consumptionUsageEventCount: events.length,
4067
+ submitted: true
4068
+ };
4069
+ }
3982
4070
 
3983
4071
  // src/commands/pricing.ts
3984
4072
  async function convertRegionPrices(client, packageName, currencyCode, amount) {
@@ -4796,7 +4884,7 @@ async function getDeviceTier(client, packageName, configId) {
4796
4884
  }
4797
4885
  return client.deviceTiers.get(packageName, configId);
4798
4886
  }
4799
- async function createDeviceTier(client, packageName, config) {
4887
+ async function createDeviceTier(client, packageName, config, options) {
4800
4888
  if (!packageName) {
4801
4889
  throw new GpcError(
4802
4890
  "Package name is required",
@@ -4813,7 +4901,7 @@ async function createDeviceTier(client, packageName, config) {
4813
4901
  "Provide a valid config with deviceGroups."
4814
4902
  );
4815
4903
  }
4816
- return client.deviceTiers.create(packageName, config);
4904
+ return client.deviceTiers.create(packageName, config, options);
4817
4905
  }
4818
4906
 
4819
4907
  // src/commands/one-time-products.ts
@@ -4857,7 +4945,14 @@ var OTP_ID_FIELDS = /* @__PURE__ */ new Set(["productId", "packageName"]);
4857
4945
  function deriveOtpUpdateMask(data) {
4858
4946
  return Object.keys(data).filter((k) => !OTP_ID_FIELDS.has(k)).join(",");
4859
4947
  }
4860
- var OTP_OFFER_ID_FIELDS = /* @__PURE__ */ new Set(["packageName", "productId", "purchaseOptionId", "offerId"]);
4948
+ var OTP_OFFER_ID_FIELDS = /* @__PURE__ */ new Set([
4949
+ "packageName",
4950
+ "productId",
4951
+ "purchaseOptionId",
4952
+ "offerId",
4953
+ "state",
4954
+ "regionsVersion"
4955
+ ]);
4861
4956
  function deriveOtpOfferUpdateMask(data) {
4862
4957
  return Object.keys(data).filter((k) => !OTP_OFFER_ID_FIELDS.has(k)).join(",");
4863
4958
  }
@@ -4930,7 +5025,7 @@ async function createOneTimeOffer(client, packageName, productId, data, purchase
4930
5025
  }
4931
5026
  async function updateOneTimeOffer(client, packageName, productId, offerId, data, updateMask, purchaseOptionId = "-", regionsVersion) {
4932
5027
  try {
4933
- const mask = updateMask || deriveOtpOfferUpdateMask(data);
5028
+ const mask = updateMask || deriveOtpOfferUpdateMask(data) || void 0;
4934
5029
  return await client.oneTimeProducts.updateOffer(
4935
5030
  packageName,
4936
5031
  productId,
@@ -6254,6 +6349,9 @@ var manifestScanner = {
6254
6349
  requires: ["manifest"],
6255
6350
  async scan(ctx) {
6256
6351
  const manifest = ctx.manifest;
6352
+ if (!manifest) {
6353
+ throw new Error('The "manifest" scanner requires manifest in the preflight context');
6354
+ }
6257
6355
  const findings = [];
6258
6356
  const minTargetSdk = ctx.config.targetSdkMinimum;
6259
6357
  if (manifest.targetSdk < minTargetSdk) {
@@ -6569,6 +6667,9 @@ var permissionsScanner = {
6569
6667
  requires: ["manifest"],
6570
6668
  async scan(ctx) {
6571
6669
  const manifest = ctx.manifest;
6670
+ if (!manifest) {
6671
+ throw new Error('The "permissions" scanner requires manifest in the preflight context');
6672
+ }
6572
6673
  const findings = [];
6573
6674
  const allowed = new Set(ctx.config.allowedPermissions);
6574
6675
  for (const perm of manifest.permissions) {
@@ -6712,13 +6813,16 @@ var nativeLibsScanner = {
6712
6813
  requires: ["zipEntries"],
6713
6814
  async scan(ctx) {
6714
6815
  const entries = ctx.zipEntries;
6816
+ if (!entries) {
6817
+ throw new Error('The "native-libs" scanner requires zipEntries in the preflight context');
6818
+ }
6715
6819
  const findings = [];
6716
6820
  const abisFound = /* @__PURE__ */ new Set();
6717
6821
  let totalNativeSize = 0;
6718
6822
  for (const entry of entries) {
6719
- const match = LIB_PATH_RE.exec(entry.path);
6720
- if (match) {
6721
- abisFound.add(match[1]);
6823
+ const abi = LIB_PATH_RE.exec(entry.path)?.[1];
6824
+ if (abi) {
6825
+ abisFound.add(abi);
6722
6826
  totalNativeSize += entry.uncompressedSize;
6723
6827
  }
6724
6828
  }
@@ -6831,6 +6935,9 @@ var metadataScanner = {
6831
6935
  requires: ["metadataDir"],
6832
6936
  async scan(ctx) {
6833
6937
  const dir = ctx.metadataDir;
6938
+ if (!dir) {
6939
+ throw new Error('The "metadata" scanner requires metadataDir in the preflight context');
6940
+ }
6834
6941
  const findings = [];
6835
6942
  let entries;
6836
6943
  try {
@@ -6936,7 +7043,7 @@ var metadataScanner = {
6936
7043
  });
6937
7044
  }
6938
7045
  }
6939
- const defaultLang = locales.includes("en-US") ? "en-US" : locales[0];
7046
+ const defaultLang = locales.includes("en-US") ? "en-US" : locales[0] ?? "en-US";
6940
7047
  const privacyPath = join10(dir, defaultLang, "privacy_policy_url.txt");
6941
7048
  try {
6942
7049
  const url = await readFile13(privacyPath, "utf-8");
@@ -7079,6 +7186,7 @@ var secretsScanner = {
7079
7186
  requires: ["sourceDir"],
7080
7187
  async scan(ctx) {
7081
7188
  const dir = ctx.sourceDir;
7189
+ if (!dir) throw new Error('The "secrets" scanner requires sourceDir in the preflight context');
7082
7190
  const findings = [];
7083
7191
  const files = await collectSourceFiles(dir, SCAN_EXTENSIONS);
7084
7192
  for (const filePath of files) {
@@ -7091,6 +7199,7 @@ var secretsScanner = {
7091
7199
  const lines = content.split("\n");
7092
7200
  for (let i = 0; i < lines.length; i++) {
7093
7201
  const line = lines[i];
7202
+ if (line === void 0) continue;
7094
7203
  for (const pattern of SECRET_PATTERNS) {
7095
7204
  if (pattern.pattern.test(line)) {
7096
7205
  const relativePath = filePath.startsWith(dir) ? filePath.slice(dir.length + 1) : filePath;
@@ -7167,6 +7276,7 @@ var billingScanner = {
7167
7276
  requires: ["sourceDir"],
7168
7277
  async scan(ctx) {
7169
7278
  const dir = ctx.sourceDir;
7279
+ if (!dir) throw new Error('The "billing" scanner requires sourceDir in the preflight context');
7170
7280
  const findings = [];
7171
7281
  const detectedSdks = /* @__PURE__ */ new Set();
7172
7282
  const files = await collectSourceFiles(dir, SCAN_EXTENSIONS2);
@@ -7233,6 +7343,7 @@ var privacyScanner = {
7233
7343
  requires: ["sourceDir"],
7234
7344
  async scan(ctx) {
7235
7345
  const dir = ctx.sourceDir;
7346
+ if (!dir) throw new Error('The "privacy" scanner requires sourceDir in the preflight context');
7236
7347
  const findings = [];
7237
7348
  const detectedSdks = /* @__PURE__ */ new Set();
7238
7349
  const files = await collectSourceFiles(dir, SCAN_EXTENSIONS3);
@@ -7314,6 +7425,9 @@ var policyScanner = {
7314
7425
  requires: ["manifest"],
7315
7426
  async scan(ctx) {
7316
7427
  const manifest = ctx.manifest;
7428
+ if (!manifest) {
7429
+ throw new Error('The "policy" scanner requires manifest in the preflight context');
7430
+ }
7317
7431
  const findings = [];
7318
7432
  const perms = new Set(manifest.permissions);
7319
7433
  if (manifest.targetSdk >= 28) {
@@ -7423,6 +7537,9 @@ var sizeScanner = {
7423
7537
  requires: ["zipEntries"],
7424
7538
  async scan(ctx) {
7425
7539
  const entries = ctx.zipEntries;
7540
+ if (!entries) {
7541
+ throw new Error('The "size" scanner requires zipEntries in the preflight context');
7542
+ }
7426
7543
  const findings = [];
7427
7544
  const maxMb = ctx.config.maxDownloadSizeMb;
7428
7545
  const totalCompressed = entries.reduce((sum, e) => sum + e.compressedSize, 0);
@@ -7571,12 +7688,12 @@ async function runPreflight(options) {
7571
7688
  }
7572
7689
  const settled = await Promise.allSettled(applicableScanners.map((scanner) => scanner.scan(ctx)));
7573
7690
  let findings = [...earlyFindings];
7574
- for (let i = 0; i < settled.length; i++) {
7691
+ for (const [i, scanner] of applicableScanners.entries()) {
7575
7692
  const result = settled[i];
7693
+ if (!result) continue;
7576
7694
  if (result.status === "fulfilled") {
7577
7695
  findings.push(...result.value);
7578
7696
  } else {
7579
- const scanner = applicableScanners[i];
7580
7697
  findings.push({
7581
7698
  scanner: scanner.name,
7582
7699
  ruleId: "scanner-error",
@@ -8167,7 +8284,7 @@ async function fetchChangelog(options) {
8167
8284
  return releases.filter((r) => r.tag_name.startsWith("v")).map((r) => ({
8168
8285
  version: r.tag_name,
8169
8286
  title: r.name || r.tag_name,
8170
- date: r.published_at ? r.published_at.split("T")[0] : "unknown",
8287
+ date: r.published_at ? r.published_at.split("T")[0] ?? "unknown" : "unknown",
8171
8288
  body: r.body || "No release notes.",
8172
8289
  url: r.html_url
8173
8290
  }));
@@ -8457,12 +8574,14 @@ var TYPE_PRIORITY = {
8457
8574
  function clusterCommits(commits) {
8458
8575
  const n = commits.length;
8459
8576
  const parent = Array.from({ length: n }, (_, i) => i);
8577
+ const parentOf = (i) => parent[i] ?? i;
8460
8578
  const find = (i) => {
8461
- while (parent[i] !== i) {
8462
- parent[i] = parent[parent[i]];
8463
- i = parent[i];
8579
+ let cur = i;
8580
+ while (parentOf(cur) !== cur) {
8581
+ parent[cur] = parentOf(parentOf(cur));
8582
+ cur = parentOf(cur);
8464
8583
  }
8465
- return i;
8584
+ return cur;
8466
8585
  };
8467
8586
  const union = (i, j) => {
8468
8587
  const ri = find(i);
@@ -8471,20 +8590,32 @@ function clusterCommits(commits) {
8471
8590
  };
8472
8591
  const tokens = commits.map((c) => tokenize2(c.subject));
8473
8592
  for (let i = 0; i < n; i++) {
8593
+ const ci = commits[i];
8594
+ const ti = tokens[i];
8595
+ if (ci === void 0 || ti === void 0) continue;
8474
8596
  for (let j = i + 1; j < n; j++) {
8475
- if (shouldCluster(commits[i], commits[j], tokens[i], tokens[j])) union(i, j);
8597
+ const cj = commits[j];
8598
+ const tj = tokens[j];
8599
+ if (cj === void 0 || tj === void 0) continue;
8600
+ if (shouldCluster(ci, cj, ti, tj)) union(i, j);
8476
8601
  }
8477
8602
  }
8478
8603
  const groups = /* @__PURE__ */ new Map();
8479
8604
  for (let i = 0; i < n; i++) {
8605
+ const commit = commits[i];
8606
+ if (commit === void 0) continue;
8480
8607
  const root = find(i);
8481
- if (!groups.has(root)) groups.set(root, []);
8482
- groups.get(root).push(commits[i]);
8608
+ let group = groups.get(root);
8609
+ if (!group) {
8610
+ group = [];
8611
+ groups.set(root, group);
8612
+ }
8613
+ group.push(commit);
8483
8614
  }
8484
8615
  const clusters = [];
8485
8616
  for (const [, members] of groups) {
8486
8617
  const weight = members.reduce((s, m) => s + m.weight, 0);
8487
- const primaryType = members.map((m) => m.type).sort((a, b) => (TYPE_PRIORITY[a] ?? 99) - (TYPE_PRIORITY[b] ?? 99))[0];
8618
+ const primaryType = members.map((m) => m.type).sort((a, b) => (TYPE_PRIORITY[a] ?? 99) - (TYPE_PRIORITY[b] ?? 99))[0] ?? "other";
8488
8619
  const label = clusterLabel(members);
8489
8620
  clusters.push({
8490
8621
  id: label.toLowerCase().replace(/\s+/g, "-"),
@@ -8527,7 +8658,7 @@ function clusterLabel(members) {
8527
8658
  bestTokenCount = c;
8528
8659
  }
8529
8660
  }
8530
- return bestToken ?? members[0].subject.slice(0, 30);
8661
+ return bestToken ?? members[0]?.subject.slice(0, 30) ?? "changes";
8531
8662
  }
8532
8663
  function scoreHeadlines(clusters) {
8533
8664
  return [...clusters].sort((a, b) => {
@@ -8596,8 +8727,8 @@ async function generateChangelog(opts = {}, runner = defaultGitRunner) {
8596
8727
  const headlineCandidates = scoreHeadlines(clusters);
8597
8728
  const grouped = {};
8598
8729
  for (const c of visible) {
8599
- if (!grouped[c.type]) grouped[c.type] = [];
8600
- grouped[c.type].push(c);
8730
+ const bucket = grouped[c.type] ??= [];
8731
+ bucket.push(c);
8601
8732
  }
8602
8733
  warnings.push(...lintJargon(visible));
8603
8734
  return {
@@ -9243,6 +9374,9 @@ var OTP_NOTIFICATION_TYPES = {
9243
9374
  1: "ONE_TIME_PRODUCT_PURCHASED",
9244
9375
  2: "ONE_TIME_PRODUCT_CANCELED"
9245
9376
  };
9377
+ var REFUND_REASONS = {
9378
+ 7: "CHARGEBACK"
9379
+ };
9246
9380
  async function getRtdnStatus(client, packageName) {
9247
9381
  try {
9248
9382
  const edit = await client.edits.insert(packageName);
@@ -9309,6 +9443,20 @@ function formatNotification(notification) {
9309
9443
  purchaseToken: n.purchaseToken.slice(0, 16) + "..."
9310
9444
  };
9311
9445
  }
9446
+ if (notification.pendingRefundReviewNotification) {
9447
+ const n = notification.pendingRefundReviewNotification;
9448
+ return {
9449
+ ...base,
9450
+ type: "pending-refund-review",
9451
+ event: "PENDING_REFUND_REVIEW",
9452
+ orderId: n.orderId,
9453
+ // An absent reason is plain "UNKNOWN"; only an unrecognised value is worth echoing.
9454
+ refundReason: n.refundReason == null ? "UNKNOWN" : REFUND_REASONS[n.refundReason] ?? `UNKNOWN(${n.refundReason})`,
9455
+ // A partial notification (hand-crafted, or a Google field we have not seen
9456
+ // yet) must not crash the decoder.
9457
+ pendingRefundToken: typeof n.pendingRefundToken === "string" ? n.pendingRefundToken.slice(0, 16) + "..." : "(missing)"
9458
+ };
9459
+ }
9312
9460
  if (notification.testNotification) {
9313
9461
  return { ...base, type: "test", event: "TEST_NOTIFICATION" };
9314
9462
  }
@@ -9538,7 +9686,14 @@ async function syncImages(client, packageName, dir, options) {
9538
9686
  "Check image dimensions, file size, and format."
9539
9687
  );
9540
9688
  }
9541
- await client.images.upload(packageName, edit.id, language, imageType, filePath);
9689
+ await client.images.upload(
9690
+ packageName,
9691
+ edit.id,
9692
+ language,
9693
+ imageType,
9694
+ filePath,
9695
+ options?.aiGenerated === void 0 ? void 0 : { aiGenerated: options.aiGenerated }
9696
+ );
9542
9697
  }
9543
9698
  uploaded++;
9544
9699
  details.push({ language, imageType, file, action: "upload", reason });
@@ -9556,7 +9711,7 @@ async function syncImages(client, packageName, dir, options) {
9556
9711
  }
9557
9712
  continue;
9558
9713
  }
9559
- const localSeq = localFiles.map((file) => localHashes.get(file));
9714
+ const localSeq = localFiles.map((file) => localHashes.get(file) ?? "");
9560
9715
  const remoteSeq = remoteImages.map((img) => img.sha256.toLowerCase());
9561
9716
  const inOrder = localSeq.length === remoteSeq.length && localSeq.every((hash, i) => hash === remoteSeq[i]);
9562
9717
  if (inOrder) {
@@ -9594,7 +9749,7 @@ async function syncImages(client, packageName, dir, options) {
9594
9749
  }
9595
9750
  for (const file of localFiles) {
9596
9751
  const hash = localHashes.get(file);
9597
- if (remoteSha256Set.has(hash)) {
9752
+ if (hash !== void 0 && remoteSha256Set.has(hash)) {
9598
9753
  skipped++;
9599
9754
  details.push({ language, imageType, file, action: "skip", reason: "sha256 match" });
9600
9755
  } else {
@@ -9657,7 +9812,8 @@ async function checkSigningConsistency(accessToken, packageName, apiHost = "andr
9657
9812
  }
9658
9813
  const bundlesData = await bundlesResp.json();
9659
9814
  const bundles = (bundlesData.bundles ?? []).sort((a, b) => b.versionCode - a.versionCode);
9660
- if (bundles.length === 0) {
9815
+ const [current, previous] = bundles;
9816
+ if (!current) {
9661
9817
  throw new GpcError(
9662
9818
  "No bundles found for this app",
9663
9819
  "NO_BUNDLES",
@@ -9665,9 +9821,9 @@ async function checkSigningConsistency(accessToken, packageName, apiHost = "andr
9665
9821
  "Upload at least one AAB with 'gpc publish' before checking signing consistency"
9666
9822
  );
9667
9823
  }
9668
- const currentVc = bundles[0].versionCode;
9824
+ const currentVc = current.versionCode;
9669
9825
  const currentFp = await fetchFingerprint(baseUrl, accessToken, currentVc);
9670
- if (bundles.length === 1) {
9826
+ if (!previous) {
9671
9827
  return {
9672
9828
  currentVersionCode: currentVc,
9673
9829
  currentFingerprint: currentFp,
@@ -9675,7 +9831,7 @@ async function checkSigningConsistency(accessToken, packageName, apiHost = "andr
9675
9831
  firstRelease: true
9676
9832
  };
9677
9833
  }
9678
- const previousVc = bundles[1].versionCode;
9834
+ const previousVc = previous.versionCode;
9679
9835
  const previousFp = await fetchFingerprint(baseUrl, accessToken, previousVc);
9680
9836
  const consistent = normalizeFingerprint(currentFp) === normalizeFingerprint(previousFp);
9681
9837
  return {
@@ -9725,6 +9881,8 @@ async function fetchFingerprint(baseUrl, accessToken, versionCode) {
9725
9881
  var PLAY_CONSOLE_SETTINGS = "https://play.google.com/console/developers/settings";
9726
9882
  var VERIFICATION_PAGE = "https://developer.android.com/developer-verification";
9727
9883
  var PLAY_APP_SIGNING = "https://support.google.com/googleplay/android-developer/answer/9842756";
9884
+ var QUALITY_REQUIREMENTS_ANNOUNCEMENT = "https://android-developers.googleblog.com/2026/08/app-quality-memory-optimization-secure-onboarding.html";
9885
+ var RESTORE_CREDENTIALS_DOCS = "https://developer.android.com/identity/sign-in/restore-credentials";
9728
9886
  function buildChecklist(input) {
9729
9887
  const items = [];
9730
9888
  const answers = input.interactiveAnswers ?? {};
@@ -9771,6 +9929,24 @@ function buildChecklist(input) {
9771
9929
  answers
9772
9930
  )
9773
9931
  );
9932
+ items.push(
9933
+ resolveManualStep(
9934
+ "memory-quality-thresholds",
9935
+ "Memory quality requirements reviewed",
9936
+ "Starting February 2027, apps and games must meet Google Play's dynamic memory, bitmap memory, and DEX code optimization thresholds (at least 25% coverage using R8 or another shrinking tool); apps that miss them may see reduced visibility and publishing capabilities. Review the dynamic memory metrics, out of memory crash filters, and DEX optimization insights in Play Console under Android vitals. Source: " + QUALITY_REQUIREMENTS_ANNOUNCEMENT,
9937
+ QUALITY_REQUIREMENTS_ANNOUNCEMENT,
9938
+ answers
9939
+ )
9940
+ );
9941
+ items.push(
9942
+ resolveManualStep(
9943
+ "restore-credentials",
9944
+ "Zero-Tap Sign-In (Restore Credentials API) implemented",
9945
+ "Starting April 2027, apps that support user sign-in must restore the signed-in state when a user moves to a new Android device, using the Android Restore Credentials API, to keep full publishing capabilities and optimal Play Store visibility. Games are currently exempt. Adopt the Restore Credentials API in your sign-in flow. Source: " + QUALITY_REQUIREMENTS_ANNOUNCEMENT,
9946
+ RESTORE_CREDENTIALS_DOCS,
9947
+ answers
9948
+ )
9949
+ );
9774
9950
  if (input.appAccessible !== void 0) {
9775
9951
  items.push({
9776
9952
  id: "app-accessible",
@@ -10174,6 +10350,7 @@ export {
10174
10350
  resolveLocales,
10175
10351
  resolveReportsBucket,
10176
10352
  retryOnUploadNotComplete,
10353
+ reviewOrderRefund,
10177
10354
  revokeSubscriptionPurchase,
10178
10355
  runPreflight,
10179
10356
  runWatch,