@gpc-cli/core 0.10.1 → 0.10.3
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/{chunk-7EVJN7FZ.js → chunk-LFU537UA.js} +10 -1
- package/dist/{chunk-7EVJN7FZ.js.map → chunk-LFU537UA.js.map} +1 -1
- package/dist/index.d.ts +50 -4
- package/dist/index.js +240 -33
- package/dist/index.js.map +1 -1
- package/dist/{releases-GKSVPYT7.js → releases-B6ROIEZA.js} +2 -2
- package/package.json +3 -3
- /package/dist/{releases-GKSVPYT7.js.map → releases-B6ROIEZA.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
validateUploadFile,
|
|
29
29
|
waitForBundle,
|
|
30
30
|
waitForBundleProcessing
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-LFU537UA.js";
|
|
32
32
|
|
|
33
33
|
// src/output.ts
|
|
34
34
|
import process2 from "process";
|
|
@@ -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(
|
|
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) {
|
|
@@ -2671,6 +2678,22 @@ var METRIC_SET_METRICS = {
|
|
|
2671
2678
|
"userPerceivedLmkRate28dUserWeighted",
|
|
2672
2679
|
"distinctUsers"
|
|
2673
2680
|
],
|
|
2681
|
+
anonRssAndSwapMemoryUsageMetricSet: [
|
|
2682
|
+
"anonRssAndSwapMemoryUsageP50",
|
|
2683
|
+
"anonRssAndSwapMemoryUsageP75",
|
|
2684
|
+
"anonRssAndSwapMemoryUsageP90",
|
|
2685
|
+
"anonRssAndSwapMemoryUsageP95",
|
|
2686
|
+
"anonRssAndSwapMemoryUsageP99",
|
|
2687
|
+
"distinctUsers"
|
|
2688
|
+
],
|
|
2689
|
+
bitmapMemoryUsageMetricSet: [
|
|
2690
|
+
"bitmapMemoryUsageP50",
|
|
2691
|
+
"bitmapMemoryUsageP75",
|
|
2692
|
+
"bitmapMemoryUsageP90",
|
|
2693
|
+
"bitmapMemoryUsageP95",
|
|
2694
|
+
"bitmapMemoryUsageP99",
|
|
2695
|
+
"distinctUsers"
|
|
2696
|
+
],
|
|
2674
2697
|
errorCountMetricSet: ["errorReportCount", "distinctUsers"]
|
|
2675
2698
|
};
|
|
2676
2699
|
async function getFreshnessEndDate(reporting, packageName, metricSet, aggregation = "DAILY") {
|
|
@@ -2785,6 +2808,18 @@ async function getVitalsLmk(reporting, packageName, options) {
|
|
|
2785
2808
|
aggregation: "DAILY"
|
|
2786
2809
|
});
|
|
2787
2810
|
}
|
|
2811
|
+
async function getVitalsMemoryRss(reporting, packageName, options) {
|
|
2812
|
+
return queryMetric(reporting, packageName, "anonRssAndSwapMemoryUsageMetricSet", {
|
|
2813
|
+
...options,
|
|
2814
|
+
aggregation: "DAILY"
|
|
2815
|
+
});
|
|
2816
|
+
}
|
|
2817
|
+
async function getVitalsMemoryBitmap(reporting, packageName, options) {
|
|
2818
|
+
return queryMetric(reporting, packageName, "bitmapMemoryUsageMetricSet", {
|
|
2819
|
+
...options,
|
|
2820
|
+
aggregation: "DAILY"
|
|
2821
|
+
});
|
|
2822
|
+
}
|
|
2788
2823
|
async function getVitalsErrorCount(reporting, packageName, options) {
|
|
2789
2824
|
const opts = options?.dimension ? options : { ...options, dimension: "reportType" };
|
|
2790
2825
|
return queryMetric(reporting, packageName, "errorCountMetricSet", opts);
|
|
@@ -3580,7 +3615,7 @@ async function handleBreach(event, config, client) {
|
|
|
3580
3615
|
}
|
|
3581
3616
|
case "halt": {
|
|
3582
3617
|
try {
|
|
3583
|
-
const { updateRollout: updateRollout2 } = await import("./releases-
|
|
3618
|
+
const { updateRollout: updateRollout2 } = await import("./releases-B6ROIEZA.js");
|
|
3584
3619
|
await updateRollout2(client, config.packageName, config.track, "halt");
|
|
3585
3620
|
halted = true;
|
|
3586
3621
|
} catch {
|
|
@@ -3979,6 +4014,87 @@ async function deferSubscriptionV2(client, packageName, token, deferDuration, et
|
|
|
3979
4014
|
deferralContext: { etag: resolvedEtag, deferDuration }
|
|
3980
4015
|
});
|
|
3981
4016
|
}
|
|
4017
|
+
var REFUND_PREFERENCES = {
|
|
4018
|
+
approve: "APPROVE",
|
|
4019
|
+
decline: "DECLINE",
|
|
4020
|
+
neutral: "NEUTRAL"
|
|
4021
|
+
};
|
|
4022
|
+
var MAX_CONSUMPTION_USAGE_EVENTS = 1e3;
|
|
4023
|
+
var MAX_CONSUMPTION_MILLIUNITS = 1e5;
|
|
4024
|
+
var RFC3339 = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$/;
|
|
4025
|
+
function invalidReviewRefund(message, suggestion) {
|
|
4026
|
+
return new GpcError(message, "ORDER_REVIEW_REFUND_INVALID", 2, suggestion);
|
|
4027
|
+
}
|
|
4028
|
+
async function reviewOrderRefund(client, packageName, orderId, options) {
|
|
4029
|
+
validatePackageName(packageName);
|
|
4030
|
+
if (!orderId.trim()) {
|
|
4031
|
+
throw invalidReviewRefund(
|
|
4032
|
+
"orderId is required",
|
|
4033
|
+
"Pass the order ID from the pending refund review notification"
|
|
4034
|
+
);
|
|
4035
|
+
}
|
|
4036
|
+
const pendingRefundToken = options.pendingRefundToken.trim();
|
|
4037
|
+
if (!pendingRefundToken) {
|
|
4038
|
+
throw invalidReviewRefund(
|
|
4039
|
+
"pendingRefundToken is required and cannot be empty",
|
|
4040
|
+
"Pass --pending-refund-token with the token from the PendingRefundReviewNotification"
|
|
4041
|
+
);
|
|
4042
|
+
}
|
|
4043
|
+
const refundPreference = REFUND_PREFERENCES[options.preference];
|
|
4044
|
+
if (!refundPreference) {
|
|
4045
|
+
throw invalidReviewRefund(
|
|
4046
|
+
`Invalid refund preference "${options.preference}"`,
|
|
4047
|
+
"Use --preference with one of: approve, decline, neutral"
|
|
4048
|
+
);
|
|
4049
|
+
}
|
|
4050
|
+
const milliunits = options.consumptionPercentageMilliunits;
|
|
4051
|
+
if (milliunits !== void 0) {
|
|
4052
|
+
if (!Number.isInteger(milliunits) || milliunits < 0 || milliunits > MAX_CONSUMPTION_MILLIUNITS) {
|
|
4053
|
+
throw invalidReviewRefund(
|
|
4054
|
+
`consumptionPercentageMilliunits must be an integer between 0 and ${MAX_CONSUMPTION_MILLIUNITS}, got ${milliunits}`,
|
|
4055
|
+
"Pass --consumption-percent with a value between 0 and 100"
|
|
4056
|
+
);
|
|
4057
|
+
}
|
|
4058
|
+
}
|
|
4059
|
+
const events = options.consumptionUsageEvents ?? [];
|
|
4060
|
+
if (events.length > MAX_CONSUMPTION_USAGE_EVENTS) {
|
|
4061
|
+
throw invalidReviewRefund(
|
|
4062
|
+
`consumptionUsageEvents has ${events.length} entries; Google Play rejects lists over ${MAX_CONSUMPTION_USAGE_EVENTS}`,
|
|
4063
|
+
`Trim the usage-events file to at most ${MAX_CONSUMPTION_USAGE_EVENTS} events`
|
|
4064
|
+
);
|
|
4065
|
+
}
|
|
4066
|
+
events.forEach((event, index) => {
|
|
4067
|
+
const time = event.consumptionTime;
|
|
4068
|
+
if (time !== void 0 && (!RFC3339.test(time) || Number.isNaN(Date.parse(time)))) {
|
|
4069
|
+
throw invalidReviewRefund(
|
|
4070
|
+
`consumptionUsageEvents[${index}].consumptionTime "${time}" is not an RFC 3339 timestamp`,
|
|
4071
|
+
"Use a timestamp like 2026-08-30T10:15:00Z"
|
|
4072
|
+
);
|
|
4073
|
+
}
|
|
4074
|
+
if (event.location && !event.location.regionCode?.trim()) {
|
|
4075
|
+
throw invalidReviewRefund(
|
|
4076
|
+
`consumptionUsageEvents[${index}].location.regionCode is required when a location is supplied`,
|
|
4077
|
+
'Set a CLDR region code such as "US" on each location'
|
|
4078
|
+
);
|
|
4079
|
+
}
|
|
4080
|
+
});
|
|
4081
|
+
await client.orders.reviewRefund(packageName, orderId, {
|
|
4082
|
+
pendingRefundToken,
|
|
4083
|
+
refundPreference,
|
|
4084
|
+
sampleContentProvided: options.sampleContentProvided,
|
|
4085
|
+
...milliunits !== void 0 ? { consumptionPercentageMilliunits: milliunits } : {},
|
|
4086
|
+
...events.length > 0 ? { consumptionUsageEvents: events } : {}
|
|
4087
|
+
});
|
|
4088
|
+
return {
|
|
4089
|
+
packageName,
|
|
4090
|
+
orderId,
|
|
4091
|
+
refundPreference,
|
|
4092
|
+
sampleContentProvided: options.sampleContentProvided,
|
|
4093
|
+
...milliunits !== void 0 ? { consumptionPercentageMilliunits: milliunits } : {},
|
|
4094
|
+
consumptionUsageEventCount: events.length,
|
|
4095
|
+
submitted: true
|
|
4096
|
+
};
|
|
4097
|
+
}
|
|
3982
4098
|
|
|
3983
4099
|
// src/commands/pricing.ts
|
|
3984
4100
|
async function convertRegionPrices(client, packageName, currencyCode, amount) {
|
|
@@ -4796,7 +4912,7 @@ async function getDeviceTier(client, packageName, configId) {
|
|
|
4796
4912
|
}
|
|
4797
4913
|
return client.deviceTiers.get(packageName, configId);
|
|
4798
4914
|
}
|
|
4799
|
-
async function createDeviceTier(client, packageName, config) {
|
|
4915
|
+
async function createDeviceTier(client, packageName, config, options) {
|
|
4800
4916
|
if (!packageName) {
|
|
4801
4917
|
throw new GpcError(
|
|
4802
4918
|
"Package name is required",
|
|
@@ -4813,7 +4929,7 @@ async function createDeviceTier(client, packageName, config) {
|
|
|
4813
4929
|
"Provide a valid config with deviceGroups."
|
|
4814
4930
|
);
|
|
4815
4931
|
}
|
|
4816
|
-
return client.deviceTiers.create(packageName, config);
|
|
4932
|
+
return client.deviceTiers.create(packageName, config, options);
|
|
4817
4933
|
}
|
|
4818
4934
|
|
|
4819
4935
|
// src/commands/one-time-products.ts
|
|
@@ -4857,7 +4973,14 @@ var OTP_ID_FIELDS = /* @__PURE__ */ new Set(["productId", "packageName"]);
|
|
|
4857
4973
|
function deriveOtpUpdateMask(data) {
|
|
4858
4974
|
return Object.keys(data).filter((k) => !OTP_ID_FIELDS.has(k)).join(",");
|
|
4859
4975
|
}
|
|
4860
|
-
var OTP_OFFER_ID_FIELDS = /* @__PURE__ */ new Set([
|
|
4976
|
+
var OTP_OFFER_ID_FIELDS = /* @__PURE__ */ new Set([
|
|
4977
|
+
"packageName",
|
|
4978
|
+
"productId",
|
|
4979
|
+
"purchaseOptionId",
|
|
4980
|
+
"offerId",
|
|
4981
|
+
"state",
|
|
4982
|
+
"regionsVersion"
|
|
4983
|
+
]);
|
|
4861
4984
|
function deriveOtpOfferUpdateMask(data) {
|
|
4862
4985
|
return Object.keys(data).filter((k) => !OTP_OFFER_ID_FIELDS.has(k)).join(",");
|
|
4863
4986
|
}
|
|
@@ -4930,7 +5053,7 @@ async function createOneTimeOffer(client, packageName, productId, data, purchase
|
|
|
4930
5053
|
}
|
|
4931
5054
|
async function updateOneTimeOffer(client, packageName, productId, offerId, data, updateMask, purchaseOptionId = "-", regionsVersion) {
|
|
4932
5055
|
try {
|
|
4933
|
-
const mask = updateMask || deriveOtpOfferUpdateMask(data);
|
|
5056
|
+
const mask = updateMask || deriveOtpOfferUpdateMask(data) || void 0;
|
|
4934
5057
|
return await client.oneTimeProducts.updateOffer(
|
|
4935
5058
|
packageName,
|
|
4936
5059
|
productId,
|
|
@@ -6254,6 +6377,9 @@ var manifestScanner = {
|
|
|
6254
6377
|
requires: ["manifest"],
|
|
6255
6378
|
async scan(ctx) {
|
|
6256
6379
|
const manifest = ctx.manifest;
|
|
6380
|
+
if (!manifest) {
|
|
6381
|
+
throw new Error('The "manifest" scanner requires manifest in the preflight context');
|
|
6382
|
+
}
|
|
6257
6383
|
const findings = [];
|
|
6258
6384
|
const minTargetSdk = ctx.config.targetSdkMinimum;
|
|
6259
6385
|
if (manifest.targetSdk < minTargetSdk) {
|
|
@@ -6569,6 +6695,9 @@ var permissionsScanner = {
|
|
|
6569
6695
|
requires: ["manifest"],
|
|
6570
6696
|
async scan(ctx) {
|
|
6571
6697
|
const manifest = ctx.manifest;
|
|
6698
|
+
if (!manifest) {
|
|
6699
|
+
throw new Error('The "permissions" scanner requires manifest in the preflight context');
|
|
6700
|
+
}
|
|
6572
6701
|
const findings = [];
|
|
6573
6702
|
const allowed = new Set(ctx.config.allowedPermissions);
|
|
6574
6703
|
for (const perm of manifest.permissions) {
|
|
@@ -6712,13 +6841,16 @@ var nativeLibsScanner = {
|
|
|
6712
6841
|
requires: ["zipEntries"],
|
|
6713
6842
|
async scan(ctx) {
|
|
6714
6843
|
const entries = ctx.zipEntries;
|
|
6844
|
+
if (!entries) {
|
|
6845
|
+
throw new Error('The "native-libs" scanner requires zipEntries in the preflight context');
|
|
6846
|
+
}
|
|
6715
6847
|
const findings = [];
|
|
6716
6848
|
const abisFound = /* @__PURE__ */ new Set();
|
|
6717
6849
|
let totalNativeSize = 0;
|
|
6718
6850
|
for (const entry of entries) {
|
|
6719
|
-
const
|
|
6720
|
-
if (
|
|
6721
|
-
abisFound.add(
|
|
6851
|
+
const abi = LIB_PATH_RE.exec(entry.path)?.[1];
|
|
6852
|
+
if (abi) {
|
|
6853
|
+
abisFound.add(abi);
|
|
6722
6854
|
totalNativeSize += entry.uncompressedSize;
|
|
6723
6855
|
}
|
|
6724
6856
|
}
|
|
@@ -6831,6 +6963,9 @@ var metadataScanner = {
|
|
|
6831
6963
|
requires: ["metadataDir"],
|
|
6832
6964
|
async scan(ctx) {
|
|
6833
6965
|
const dir = ctx.metadataDir;
|
|
6966
|
+
if (!dir) {
|
|
6967
|
+
throw new Error('The "metadata" scanner requires metadataDir in the preflight context');
|
|
6968
|
+
}
|
|
6834
6969
|
const findings = [];
|
|
6835
6970
|
let entries;
|
|
6836
6971
|
try {
|
|
@@ -6936,7 +7071,7 @@ var metadataScanner = {
|
|
|
6936
7071
|
});
|
|
6937
7072
|
}
|
|
6938
7073
|
}
|
|
6939
|
-
const defaultLang = locales.includes("en-US") ? "en-US" : locales[0];
|
|
7074
|
+
const defaultLang = locales.includes("en-US") ? "en-US" : locales[0] ?? "en-US";
|
|
6940
7075
|
const privacyPath = join10(dir, defaultLang, "privacy_policy_url.txt");
|
|
6941
7076
|
try {
|
|
6942
7077
|
const url = await readFile13(privacyPath, "utf-8");
|
|
@@ -7079,6 +7214,7 @@ var secretsScanner = {
|
|
|
7079
7214
|
requires: ["sourceDir"],
|
|
7080
7215
|
async scan(ctx) {
|
|
7081
7216
|
const dir = ctx.sourceDir;
|
|
7217
|
+
if (!dir) throw new Error('The "secrets" scanner requires sourceDir in the preflight context');
|
|
7082
7218
|
const findings = [];
|
|
7083
7219
|
const files = await collectSourceFiles(dir, SCAN_EXTENSIONS);
|
|
7084
7220
|
for (const filePath of files) {
|
|
@@ -7091,6 +7227,7 @@ var secretsScanner = {
|
|
|
7091
7227
|
const lines = content.split("\n");
|
|
7092
7228
|
for (let i = 0; i < lines.length; i++) {
|
|
7093
7229
|
const line = lines[i];
|
|
7230
|
+
if (line === void 0) continue;
|
|
7094
7231
|
for (const pattern of SECRET_PATTERNS) {
|
|
7095
7232
|
if (pattern.pattern.test(line)) {
|
|
7096
7233
|
const relativePath = filePath.startsWith(dir) ? filePath.slice(dir.length + 1) : filePath;
|
|
@@ -7167,6 +7304,7 @@ var billingScanner = {
|
|
|
7167
7304
|
requires: ["sourceDir"],
|
|
7168
7305
|
async scan(ctx) {
|
|
7169
7306
|
const dir = ctx.sourceDir;
|
|
7307
|
+
if (!dir) throw new Error('The "billing" scanner requires sourceDir in the preflight context');
|
|
7170
7308
|
const findings = [];
|
|
7171
7309
|
const detectedSdks = /* @__PURE__ */ new Set();
|
|
7172
7310
|
const files = await collectSourceFiles(dir, SCAN_EXTENSIONS2);
|
|
@@ -7233,6 +7371,7 @@ var privacyScanner = {
|
|
|
7233
7371
|
requires: ["sourceDir"],
|
|
7234
7372
|
async scan(ctx) {
|
|
7235
7373
|
const dir = ctx.sourceDir;
|
|
7374
|
+
if (!dir) throw new Error('The "privacy" scanner requires sourceDir in the preflight context');
|
|
7236
7375
|
const findings = [];
|
|
7237
7376
|
const detectedSdks = /* @__PURE__ */ new Set();
|
|
7238
7377
|
const files = await collectSourceFiles(dir, SCAN_EXTENSIONS3);
|
|
@@ -7314,6 +7453,9 @@ var policyScanner = {
|
|
|
7314
7453
|
requires: ["manifest"],
|
|
7315
7454
|
async scan(ctx) {
|
|
7316
7455
|
const manifest = ctx.manifest;
|
|
7456
|
+
if (!manifest) {
|
|
7457
|
+
throw new Error('The "policy" scanner requires manifest in the preflight context');
|
|
7458
|
+
}
|
|
7317
7459
|
const findings = [];
|
|
7318
7460
|
const perms = new Set(manifest.permissions);
|
|
7319
7461
|
if (manifest.targetSdk >= 28) {
|
|
@@ -7423,6 +7565,9 @@ var sizeScanner = {
|
|
|
7423
7565
|
requires: ["zipEntries"],
|
|
7424
7566
|
async scan(ctx) {
|
|
7425
7567
|
const entries = ctx.zipEntries;
|
|
7568
|
+
if (!entries) {
|
|
7569
|
+
throw new Error('The "size" scanner requires zipEntries in the preflight context');
|
|
7570
|
+
}
|
|
7426
7571
|
const findings = [];
|
|
7427
7572
|
const maxMb = ctx.config.maxDownloadSizeMb;
|
|
7428
7573
|
const totalCompressed = entries.reduce((sum, e) => sum + e.compressedSize, 0);
|
|
@@ -7571,12 +7716,12 @@ async function runPreflight(options) {
|
|
|
7571
7716
|
}
|
|
7572
7717
|
const settled = await Promise.allSettled(applicableScanners.map((scanner) => scanner.scan(ctx)));
|
|
7573
7718
|
let findings = [...earlyFindings];
|
|
7574
|
-
for (
|
|
7719
|
+
for (const [i, scanner] of applicableScanners.entries()) {
|
|
7575
7720
|
const result = settled[i];
|
|
7721
|
+
if (!result) continue;
|
|
7576
7722
|
if (result.status === "fulfilled") {
|
|
7577
7723
|
findings.push(...result.value);
|
|
7578
7724
|
} else {
|
|
7579
|
-
const scanner = applicableScanners[i];
|
|
7580
7725
|
findings.push({
|
|
7581
7726
|
scanner: scanner.name,
|
|
7582
7727
|
ruleId: "scanner-error",
|
|
@@ -8167,7 +8312,7 @@ async function fetchChangelog(options) {
|
|
|
8167
8312
|
return releases.filter((r) => r.tag_name.startsWith("v")).map((r) => ({
|
|
8168
8313
|
version: r.tag_name,
|
|
8169
8314
|
title: r.name || r.tag_name,
|
|
8170
|
-
date: r.published_at ? r.published_at.split("T")[0] : "unknown",
|
|
8315
|
+
date: r.published_at ? r.published_at.split("T")[0] ?? "unknown" : "unknown",
|
|
8171
8316
|
body: r.body || "No release notes.",
|
|
8172
8317
|
url: r.html_url
|
|
8173
8318
|
}));
|
|
@@ -8457,12 +8602,14 @@ var TYPE_PRIORITY = {
|
|
|
8457
8602
|
function clusterCommits(commits) {
|
|
8458
8603
|
const n = commits.length;
|
|
8459
8604
|
const parent = Array.from({ length: n }, (_, i) => i);
|
|
8605
|
+
const parentOf = (i) => parent[i] ?? i;
|
|
8460
8606
|
const find = (i) => {
|
|
8461
|
-
|
|
8462
|
-
|
|
8463
|
-
|
|
8607
|
+
let cur = i;
|
|
8608
|
+
while (parentOf(cur) !== cur) {
|
|
8609
|
+
parent[cur] = parentOf(parentOf(cur));
|
|
8610
|
+
cur = parentOf(cur);
|
|
8464
8611
|
}
|
|
8465
|
-
return
|
|
8612
|
+
return cur;
|
|
8466
8613
|
};
|
|
8467
8614
|
const union = (i, j) => {
|
|
8468
8615
|
const ri = find(i);
|
|
@@ -8471,20 +8618,32 @@ function clusterCommits(commits) {
|
|
|
8471
8618
|
};
|
|
8472
8619
|
const tokens = commits.map((c) => tokenize2(c.subject));
|
|
8473
8620
|
for (let i = 0; i < n; i++) {
|
|
8621
|
+
const ci = commits[i];
|
|
8622
|
+
const ti = tokens[i];
|
|
8623
|
+
if (ci === void 0 || ti === void 0) continue;
|
|
8474
8624
|
for (let j = i + 1; j < n; j++) {
|
|
8475
|
-
|
|
8625
|
+
const cj = commits[j];
|
|
8626
|
+
const tj = tokens[j];
|
|
8627
|
+
if (cj === void 0 || tj === void 0) continue;
|
|
8628
|
+
if (shouldCluster(ci, cj, ti, tj)) union(i, j);
|
|
8476
8629
|
}
|
|
8477
8630
|
}
|
|
8478
8631
|
const groups = /* @__PURE__ */ new Map();
|
|
8479
8632
|
for (let i = 0; i < n; i++) {
|
|
8633
|
+
const commit = commits[i];
|
|
8634
|
+
if (commit === void 0) continue;
|
|
8480
8635
|
const root = find(i);
|
|
8481
|
-
|
|
8482
|
-
|
|
8636
|
+
let group = groups.get(root);
|
|
8637
|
+
if (!group) {
|
|
8638
|
+
group = [];
|
|
8639
|
+
groups.set(root, group);
|
|
8640
|
+
}
|
|
8641
|
+
group.push(commit);
|
|
8483
8642
|
}
|
|
8484
8643
|
const clusters = [];
|
|
8485
8644
|
for (const [, members] of groups) {
|
|
8486
8645
|
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];
|
|
8646
|
+
const primaryType = members.map((m) => m.type).sort((a, b) => (TYPE_PRIORITY[a] ?? 99) - (TYPE_PRIORITY[b] ?? 99))[0] ?? "other";
|
|
8488
8647
|
const label = clusterLabel(members);
|
|
8489
8648
|
clusters.push({
|
|
8490
8649
|
id: label.toLowerCase().replace(/\s+/g, "-"),
|
|
@@ -8527,7 +8686,7 @@ function clusterLabel(members) {
|
|
|
8527
8686
|
bestTokenCount = c;
|
|
8528
8687
|
}
|
|
8529
8688
|
}
|
|
8530
|
-
return bestToken ?? members[0]
|
|
8689
|
+
return bestToken ?? members[0]?.subject.slice(0, 30) ?? "changes";
|
|
8531
8690
|
}
|
|
8532
8691
|
function scoreHeadlines(clusters) {
|
|
8533
8692
|
return [...clusters].sort((a, b) => {
|
|
@@ -8596,8 +8755,8 @@ async function generateChangelog(opts = {}, runner = defaultGitRunner) {
|
|
|
8596
8755
|
const headlineCandidates = scoreHeadlines(clusters);
|
|
8597
8756
|
const grouped = {};
|
|
8598
8757
|
for (const c of visible) {
|
|
8599
|
-
|
|
8600
|
-
|
|
8758
|
+
const bucket = grouped[c.type] ??= [];
|
|
8759
|
+
bucket.push(c);
|
|
8601
8760
|
}
|
|
8602
8761
|
warnings.push(...lintJargon(visible));
|
|
8603
8762
|
return {
|
|
@@ -9243,6 +9402,9 @@ var OTP_NOTIFICATION_TYPES = {
|
|
|
9243
9402
|
1: "ONE_TIME_PRODUCT_PURCHASED",
|
|
9244
9403
|
2: "ONE_TIME_PRODUCT_CANCELED"
|
|
9245
9404
|
};
|
|
9405
|
+
var REFUND_REASONS = {
|
|
9406
|
+
7: "CHARGEBACK"
|
|
9407
|
+
};
|
|
9246
9408
|
async function getRtdnStatus(client, packageName) {
|
|
9247
9409
|
try {
|
|
9248
9410
|
const edit = await client.edits.insert(packageName);
|
|
@@ -9309,6 +9471,20 @@ function formatNotification(notification) {
|
|
|
9309
9471
|
purchaseToken: n.purchaseToken.slice(0, 16) + "..."
|
|
9310
9472
|
};
|
|
9311
9473
|
}
|
|
9474
|
+
if (notification.pendingRefundReviewNotification) {
|
|
9475
|
+
const n = notification.pendingRefundReviewNotification;
|
|
9476
|
+
return {
|
|
9477
|
+
...base,
|
|
9478
|
+
type: "pending-refund-review",
|
|
9479
|
+
event: "PENDING_REFUND_REVIEW",
|
|
9480
|
+
orderId: n.orderId,
|
|
9481
|
+
// An absent reason is plain "UNKNOWN"; only an unrecognised value is worth echoing.
|
|
9482
|
+
refundReason: n.refundReason == null ? "UNKNOWN" : REFUND_REASONS[n.refundReason] ?? `UNKNOWN(${n.refundReason})`,
|
|
9483
|
+
// A partial notification (hand-crafted, or a Google field we have not seen
|
|
9484
|
+
// yet) must not crash the decoder.
|
|
9485
|
+
pendingRefundToken: typeof n.pendingRefundToken === "string" ? n.pendingRefundToken.slice(0, 16) + "..." : "(missing)"
|
|
9486
|
+
};
|
|
9487
|
+
}
|
|
9312
9488
|
if (notification.testNotification) {
|
|
9313
9489
|
return { ...base, type: "test", event: "TEST_NOTIFICATION" };
|
|
9314
9490
|
}
|
|
@@ -9538,7 +9714,14 @@ async function syncImages(client, packageName, dir, options) {
|
|
|
9538
9714
|
"Check image dimensions, file size, and format."
|
|
9539
9715
|
);
|
|
9540
9716
|
}
|
|
9541
|
-
await client.images.upload(
|
|
9717
|
+
await client.images.upload(
|
|
9718
|
+
packageName,
|
|
9719
|
+
edit.id,
|
|
9720
|
+
language,
|
|
9721
|
+
imageType,
|
|
9722
|
+
filePath,
|
|
9723
|
+
options?.aiGenerated === void 0 ? void 0 : { aiGenerated: options.aiGenerated }
|
|
9724
|
+
);
|
|
9542
9725
|
}
|
|
9543
9726
|
uploaded++;
|
|
9544
9727
|
details.push({ language, imageType, file, action: "upload", reason });
|
|
@@ -9556,7 +9739,7 @@ async function syncImages(client, packageName, dir, options) {
|
|
|
9556
9739
|
}
|
|
9557
9740
|
continue;
|
|
9558
9741
|
}
|
|
9559
|
-
const localSeq = localFiles.map((file) => localHashes.get(file));
|
|
9742
|
+
const localSeq = localFiles.map((file) => localHashes.get(file) ?? "");
|
|
9560
9743
|
const remoteSeq = remoteImages.map((img) => img.sha256.toLowerCase());
|
|
9561
9744
|
const inOrder = localSeq.length === remoteSeq.length && localSeq.every((hash, i) => hash === remoteSeq[i]);
|
|
9562
9745
|
if (inOrder) {
|
|
@@ -9594,7 +9777,7 @@ async function syncImages(client, packageName, dir, options) {
|
|
|
9594
9777
|
}
|
|
9595
9778
|
for (const file of localFiles) {
|
|
9596
9779
|
const hash = localHashes.get(file);
|
|
9597
|
-
if (remoteSha256Set.has(hash)) {
|
|
9780
|
+
if (hash !== void 0 && remoteSha256Set.has(hash)) {
|
|
9598
9781
|
skipped++;
|
|
9599
9782
|
details.push({ language, imageType, file, action: "skip", reason: "sha256 match" });
|
|
9600
9783
|
} else {
|
|
@@ -9657,7 +9840,8 @@ async function checkSigningConsistency(accessToken, packageName, apiHost = "andr
|
|
|
9657
9840
|
}
|
|
9658
9841
|
const bundlesData = await bundlesResp.json();
|
|
9659
9842
|
const bundles = (bundlesData.bundles ?? []).sort((a, b) => b.versionCode - a.versionCode);
|
|
9660
|
-
|
|
9843
|
+
const [current, previous] = bundles;
|
|
9844
|
+
if (!current) {
|
|
9661
9845
|
throw new GpcError(
|
|
9662
9846
|
"No bundles found for this app",
|
|
9663
9847
|
"NO_BUNDLES",
|
|
@@ -9665,9 +9849,9 @@ async function checkSigningConsistency(accessToken, packageName, apiHost = "andr
|
|
|
9665
9849
|
"Upload at least one AAB with 'gpc publish' before checking signing consistency"
|
|
9666
9850
|
);
|
|
9667
9851
|
}
|
|
9668
|
-
const currentVc =
|
|
9852
|
+
const currentVc = current.versionCode;
|
|
9669
9853
|
const currentFp = await fetchFingerprint(baseUrl, accessToken, currentVc);
|
|
9670
|
-
if (
|
|
9854
|
+
if (!previous) {
|
|
9671
9855
|
return {
|
|
9672
9856
|
currentVersionCode: currentVc,
|
|
9673
9857
|
currentFingerprint: currentFp,
|
|
@@ -9675,7 +9859,7 @@ async function checkSigningConsistency(accessToken, packageName, apiHost = "andr
|
|
|
9675
9859
|
firstRelease: true
|
|
9676
9860
|
};
|
|
9677
9861
|
}
|
|
9678
|
-
const previousVc =
|
|
9862
|
+
const previousVc = previous.versionCode;
|
|
9679
9863
|
const previousFp = await fetchFingerprint(baseUrl, accessToken, previousVc);
|
|
9680
9864
|
const consistent = normalizeFingerprint(currentFp) === normalizeFingerprint(previousFp);
|
|
9681
9865
|
return {
|
|
@@ -9725,6 +9909,8 @@ async function fetchFingerprint(baseUrl, accessToken, versionCode) {
|
|
|
9725
9909
|
var PLAY_CONSOLE_SETTINGS = "https://play.google.com/console/developers/settings";
|
|
9726
9910
|
var VERIFICATION_PAGE = "https://developer.android.com/developer-verification";
|
|
9727
9911
|
var PLAY_APP_SIGNING = "https://support.google.com/googleplay/android-developer/answer/9842756";
|
|
9912
|
+
var QUALITY_REQUIREMENTS_ANNOUNCEMENT = "https://android-developers.googleblog.com/2026/08/app-quality-memory-optimization-secure-onboarding.html";
|
|
9913
|
+
var RESTORE_CREDENTIALS_DOCS = "https://developer.android.com/identity/sign-in/restore-credentials";
|
|
9728
9914
|
function buildChecklist(input) {
|
|
9729
9915
|
const items = [];
|
|
9730
9916
|
const answers = input.interactiveAnswers ?? {};
|
|
@@ -9771,6 +9957,24 @@ function buildChecklist(input) {
|
|
|
9771
9957
|
answers
|
|
9772
9958
|
)
|
|
9773
9959
|
);
|
|
9960
|
+
items.push(
|
|
9961
|
+
resolveManualStep(
|
|
9962
|
+
"memory-quality-thresholds",
|
|
9963
|
+
"Memory quality requirements reviewed",
|
|
9964
|
+
"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,
|
|
9965
|
+
QUALITY_REQUIREMENTS_ANNOUNCEMENT,
|
|
9966
|
+
answers
|
|
9967
|
+
)
|
|
9968
|
+
);
|
|
9969
|
+
items.push(
|
|
9970
|
+
resolveManualStep(
|
|
9971
|
+
"restore-credentials",
|
|
9972
|
+
"Zero-Tap Sign-In (Restore Credentials API) implemented",
|
|
9973
|
+
"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,
|
|
9974
|
+
RESTORE_CREDENTIALS_DOCS,
|
|
9975
|
+
answers
|
|
9976
|
+
)
|
|
9977
|
+
);
|
|
9774
9978
|
if (input.appAccessible !== void 0) {
|
|
9775
9979
|
items.push({
|
|
9776
9980
|
id: "app-accessible",
|
|
@@ -10075,6 +10279,8 @@ export {
|
|
|
10075
10279
|
getVitalsErrorCount,
|
|
10076
10280
|
getVitalsLmk,
|
|
10077
10281
|
getVitalsMemory,
|
|
10282
|
+
getVitalsMemoryBitmap,
|
|
10283
|
+
getVitalsMemoryRss,
|
|
10078
10284
|
getVitalsOverview,
|
|
10079
10285
|
getVitalsRendering,
|
|
10080
10286
|
getVitalsStartup,
|
|
@@ -10174,6 +10380,7 @@ export {
|
|
|
10174
10380
|
resolveLocales,
|
|
10175
10381
|
resolveReportsBucket,
|
|
10176
10382
|
retryOnUploadNotComplete,
|
|
10383
|
+
reviewOrderRefund,
|
|
10177
10384
|
revokeSubscriptionPurchase,
|
|
10178
10385
|
runPreflight,
|
|
10179
10386
|
runWatch,
|