@gpc-cli/core 0.9.38 → 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 +51 -1
- package/dist/index.js +175 -27
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
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;
|
|
@@ -1171,6 +1175,7 @@ interface WatchOptions {
|
|
|
1171
1175
|
declare function loadStatusCache(packageName: string, ttlSeconds?: number): Promise<AppStatus | null>;
|
|
1172
1176
|
declare function saveStatusCache(packageName: string, data: AppStatus, ttlSeconds?: number): Promise<void>;
|
|
1173
1177
|
declare function getAppStatus(client: PlayApiClient, reporting: ReportingApiClient, packageName: string, options?: GetAppStatusOptions): Promise<AppStatus>;
|
|
1178
|
+
declare function relativeTime(isoString: string): string;
|
|
1174
1179
|
declare function formatStatusTable(status: AppStatus): string;
|
|
1175
1180
|
declare function formatStatusSummary(status: AppStatus): string;
|
|
1176
1181
|
declare function computeStatusDiff(prev: AppStatus, curr: AppStatus): StatusDiff;
|
|
@@ -1203,4 +1208,49 @@ declare function fetchChangelog(options?: FetchChangelogOptions): Promise<Change
|
|
|
1203
1208
|
*/
|
|
1204
1209
|
declare function formatChangelogEntry(entry: ChangelogEntry): string;
|
|
1205
1210
|
|
|
1206
|
-
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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 (
|
|
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
|
-
|
|
2408
|
-
if (options?.
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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 ===
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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(
|
|
@@ -6938,9 +6980,15 @@ function formatStatusSummary(status) {
|
|
|
6938
6980
|
}
|
|
6939
6981
|
return parts.join(" \xB7 ") + (statusHasBreach(status) ? " [ALERT]" : "");
|
|
6940
6982
|
}
|
|
6983
|
+
function latestProductionVersion(releases) {
|
|
6984
|
+
const prod = releases.find((r) => r.track === "production");
|
|
6985
|
+
if (prod) return prod.versionCode;
|
|
6986
|
+
const nonDraft = releases.find((r) => r.status !== "draft");
|
|
6987
|
+
return nonDraft?.versionCode ?? releases[0]?.versionCode ?? null;
|
|
6988
|
+
}
|
|
6941
6989
|
function computeStatusDiff(prev, curr) {
|
|
6942
|
-
const prevVersion = prev.releases
|
|
6943
|
-
const currVersion = curr.releases
|
|
6990
|
+
const prevVersion = latestProductionVersion(prev.releases);
|
|
6991
|
+
const currVersion = latestProductionVersion(curr.releases);
|
|
6944
6992
|
const prevCrash = prev.vitals.crashes.value ?? null;
|
|
6945
6993
|
const currCrash = curr.vitals.crashes.value ?? null;
|
|
6946
6994
|
const prevAnr = prev.vitals.anr.value ?? null;
|
|
@@ -6994,13 +7042,16 @@ function formatStatusDiff(diff, since) {
|
|
|
6994
7042
|
}
|
|
6995
7043
|
async function runWatchLoop(opts) {
|
|
6996
7044
|
if (opts.intervalSeconds < 10) {
|
|
6997
|
-
throw new
|
|
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
|
+
);
|
|
6998
7051
|
}
|
|
6999
7052
|
let running = true;
|
|
7000
7053
|
const cleanup = () => {
|
|
7001
7054
|
running = false;
|
|
7002
|
-
process.stdout.write("\n");
|
|
7003
|
-
process.exit(0);
|
|
7004
7055
|
};
|
|
7005
7056
|
process.on("SIGINT", cleanup);
|
|
7006
7057
|
process.on("SIGTERM", cleanup);
|
|
@@ -7131,6 +7182,99 @@ function formatChangelogEntry(entry) {
|
|
|
7131
7182
|
lines.push(`Full notes: ${entry.url}`);
|
|
7132
7183
|
return lines.join("\n");
|
|
7133
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
|
+
}
|
|
7134
7278
|
export {
|
|
7135
7279
|
ApiError,
|
|
7136
7280
|
ConfigError,
|
|
@@ -7186,6 +7330,7 @@ export {
|
|
|
7186
7330
|
deactivateBasePlan,
|
|
7187
7331
|
deactivateOffer,
|
|
7188
7332
|
deactivatePurchaseOption,
|
|
7333
|
+
decodeNotification,
|
|
7189
7334
|
deferSubscriptionPurchase,
|
|
7190
7335
|
deferSubscriptionV2,
|
|
7191
7336
|
deleteBasePlan,
|
|
@@ -7218,6 +7363,7 @@ export {
|
|
|
7218
7363
|
formatCustomPayload,
|
|
7219
7364
|
formatDiscordPayload,
|
|
7220
7365
|
formatJunit,
|
|
7366
|
+
formatNotification,
|
|
7221
7367
|
formatOutput,
|
|
7222
7368
|
formatSlackPayload,
|
|
7223
7369
|
formatStatusDiff,
|
|
@@ -7245,6 +7391,7 @@ export {
|
|
|
7245
7391
|
getQuotaUsage,
|
|
7246
7392
|
getReleasesStatus,
|
|
7247
7393
|
getReview,
|
|
7394
|
+
getRtdnStatus,
|
|
7248
7395
|
getSubscription,
|
|
7249
7396
|
getSubscriptionAnalytics,
|
|
7250
7397
|
getSubscriptionPurchase,
|
|
@@ -7314,6 +7461,7 @@ export {
|
|
|
7314
7461
|
refundExternalTransaction,
|
|
7315
7462
|
refundOrder,
|
|
7316
7463
|
refundSubscriptionV2,
|
|
7464
|
+
relativeTime,
|
|
7317
7465
|
removeTesters,
|
|
7318
7466
|
removeUser,
|
|
7319
7467
|
replyToReview,
|