@gpc-cli/core 0.9.43 → 0.9.44
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/LICENSE +21 -0
- package/dist/index.d.ts +20 -16
- package/dist/index.js +307 -176
- package/dist/index.js.map +1 -1
- package/package.json +16 -16
package/dist/index.js
CHANGED
|
@@ -363,10 +363,7 @@ function formatJunit(data, commandName = "command") {
|
|
|
363
363
|
}
|
|
364
364
|
|
|
365
365
|
// src/plugins.ts
|
|
366
|
-
var FIRST_PARTY_PLUGINS = /* @__PURE__ */ new Set([
|
|
367
|
-
"@gpc-cli/plugin-ci",
|
|
368
|
-
"@gpc-cli/plugin-sdk"
|
|
369
|
-
]);
|
|
366
|
+
var FIRST_PARTY_PLUGINS = /* @__PURE__ */ new Set(["@gpc-cli/plugin-ci", "@gpc-cli/plugin-sdk"]);
|
|
370
367
|
var PluginManager = class {
|
|
371
368
|
plugins = [];
|
|
372
369
|
beforeHandlers = [];
|
|
@@ -755,7 +752,13 @@ ${validation.errors.join("\n")}`,
|
|
|
755
752
|
if (options.onUploadProgress) options.onUploadProgress(event);
|
|
756
753
|
}
|
|
757
754
|
};
|
|
758
|
-
const bundle = isApk ? await client.apks.upload(packageName, edit.id, filePath, uploadOpts) : await client.bundles.upload(
|
|
755
|
+
const bundle = isApk ? await client.apks.upload(packageName, edit.id, filePath, uploadOpts) : await client.bundles.upload(
|
|
756
|
+
packageName,
|
|
757
|
+
edit.id,
|
|
758
|
+
filePath,
|
|
759
|
+
uploadOpts,
|
|
760
|
+
options.deviceTierConfigId
|
|
761
|
+
);
|
|
759
762
|
if (options.mappingFile) {
|
|
760
763
|
await client.deobfuscation.upload(
|
|
761
764
|
packageName,
|
|
@@ -2452,16 +2455,20 @@ function analyzeReviews(reviews) {
|
|
|
2452
2455
|
async function listReviews(client, packageName, options) {
|
|
2453
2456
|
let reviews;
|
|
2454
2457
|
if (options?.all) {
|
|
2455
|
-
const { items } = await paginateAll(
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2458
|
+
const { items } = await paginateAll(
|
|
2459
|
+
async (pageToken) => {
|
|
2460
|
+
const apiOptions = { token: pageToken };
|
|
2461
|
+
if (options?.translationLanguage)
|
|
2462
|
+
apiOptions.translationLanguage = options.translationLanguage;
|
|
2463
|
+
if (options?.maxResults) apiOptions.maxResults = options.maxResults;
|
|
2464
|
+
const response = await client.reviews.list(packageName, apiOptions);
|
|
2465
|
+
return {
|
|
2466
|
+
items: response.reviews || [],
|
|
2467
|
+
nextPageToken: response.tokenPagination?.nextPageToken
|
|
2468
|
+
};
|
|
2469
|
+
},
|
|
2470
|
+
{ limit: options?.limit }
|
|
2471
|
+
);
|
|
2465
2472
|
reviews = items;
|
|
2466
2473
|
} else {
|
|
2467
2474
|
const apiOptions = {};
|
|
@@ -3336,10 +3343,6 @@ async function revokeSubscriptionPurchase(client, packageName, token) {
|
|
|
3336
3343
|
validatePackageName(packageName);
|
|
3337
3344
|
return client.purchases.revokeSubscriptionV2(packageName, token);
|
|
3338
3345
|
}
|
|
3339
|
-
async function refundSubscriptionV2(client, packageName, token) {
|
|
3340
|
-
validatePackageName(packageName);
|
|
3341
|
-
return client.purchases.refundSubscriptionV2(packageName, token);
|
|
3342
|
-
}
|
|
3343
3346
|
async function listVoidedPurchases(client, packageName, options) {
|
|
3344
3347
|
validatePackageName(packageName);
|
|
3345
3348
|
if (options?.limit || options?.nextPage) {
|
|
@@ -3375,10 +3378,20 @@ async function getOrderDetails(client, packageName, orderId) {
|
|
|
3375
3378
|
async function batchGetOrders(client, packageName, orderIds) {
|
|
3376
3379
|
validatePackageName(packageName);
|
|
3377
3380
|
if (orderIds.length === 0) {
|
|
3378
|
-
throw new GpcError(
|
|
3381
|
+
throw new GpcError(
|
|
3382
|
+
"No order IDs provided",
|
|
3383
|
+
"ORDERS_BATCH_EMPTY",
|
|
3384
|
+
2,
|
|
3385
|
+
"Pass at least one order ID with --ids"
|
|
3386
|
+
);
|
|
3379
3387
|
}
|
|
3380
3388
|
if (orderIds.length > 1e3) {
|
|
3381
|
-
throw new GpcError(
|
|
3389
|
+
throw new GpcError(
|
|
3390
|
+
`Too many order IDs (${orderIds.length}). Maximum is 1000.`,
|
|
3391
|
+
"ORDERS_BATCH_LIMIT",
|
|
3392
|
+
2,
|
|
3393
|
+
"Split into multiple requests of 1000 or fewer"
|
|
3394
|
+
);
|
|
3382
3395
|
}
|
|
3383
3396
|
return client.orders.batchGet(packageName, orderIds);
|
|
3384
3397
|
}
|
|
@@ -3504,8 +3517,22 @@ async function listUsers(client, developerId, options) {
|
|
|
3504
3517
|
const response = await client.list(developerId, options);
|
|
3505
3518
|
return { users: response.users || [], nextPageToken: response.nextPageToken };
|
|
3506
3519
|
}
|
|
3507
|
-
async function getUser(client, developerId,
|
|
3508
|
-
|
|
3520
|
+
async function getUser(client, developerId, email) {
|
|
3521
|
+
const lowerEmail = email.toLowerCase();
|
|
3522
|
+
let pageToken;
|
|
3523
|
+
do {
|
|
3524
|
+
const response = await client.list(developerId, pageToken ? { pageToken } : void 0);
|
|
3525
|
+
const users = response.users || [];
|
|
3526
|
+
const match = users.find((u) => u.email?.toLowerCase() === lowerEmail);
|
|
3527
|
+
if (match) return match;
|
|
3528
|
+
pageToken = response.nextPageToken;
|
|
3529
|
+
} while (pageToken);
|
|
3530
|
+
throw new GpcError(
|
|
3531
|
+
`User "${email}" not found in developer account.`,
|
|
3532
|
+
"USER_NOT_FOUND",
|
|
3533
|
+
4,
|
|
3534
|
+
"Check the email address and ensure the user has access to this developer account."
|
|
3535
|
+
);
|
|
3509
3536
|
}
|
|
3510
3537
|
async function inviteUser(client, developerId, email, permissions, grants) {
|
|
3511
3538
|
const user = { email };
|
|
@@ -3899,7 +3926,7 @@ var OTP_ID_FIELDS = /* @__PURE__ */ new Set(["productId", "packageName"]);
|
|
|
3899
3926
|
function deriveOtpUpdateMask(data) {
|
|
3900
3927
|
return Object.keys(data).filter((k) => !OTP_ID_FIELDS.has(k)).join(",");
|
|
3901
3928
|
}
|
|
3902
|
-
var OTP_OFFER_ID_FIELDS = /* @__PURE__ */ new Set(["productId", "offerId"]);
|
|
3929
|
+
var OTP_OFFER_ID_FIELDS = /* @__PURE__ */ new Set(["packageName", "productId", "purchaseOptionId", "offerId"]);
|
|
3903
3930
|
function deriveOtpOfferUpdateMask(data) {
|
|
3904
3931
|
return Object.keys(data).filter((k) => !OTP_OFFER_ID_FIELDS.has(k)).join(",");
|
|
3905
3932
|
}
|
|
@@ -3928,9 +3955,9 @@ async function deleteOneTimeProduct(client, packageName, productId) {
|
|
|
3928
3955
|
);
|
|
3929
3956
|
}
|
|
3930
3957
|
}
|
|
3931
|
-
async function listOneTimeOffers(client, packageName, productId) {
|
|
3958
|
+
async function listOneTimeOffers(client, packageName, productId, purchaseOptionId = "-") {
|
|
3932
3959
|
try {
|
|
3933
|
-
return await client.oneTimeProducts.listOffers(packageName, productId);
|
|
3960
|
+
return await client.oneTimeProducts.listOffers(packageName, productId, purchaseOptionId);
|
|
3934
3961
|
} catch (error) {
|
|
3935
3962
|
throw new GpcError(
|
|
3936
3963
|
`Failed to list offers for product "${productId}": ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -3940,9 +3967,9 @@ async function listOneTimeOffers(client, packageName, productId) {
|
|
|
3940
3967
|
);
|
|
3941
3968
|
}
|
|
3942
3969
|
}
|
|
3943
|
-
async function getOneTimeOffer(client, packageName, productId, offerId) {
|
|
3970
|
+
async function getOneTimeOffer(client, packageName, productId, offerId, purchaseOptionId = "-") {
|
|
3944
3971
|
try {
|
|
3945
|
-
return await client.oneTimeProducts.getOffer(packageName, productId, offerId);
|
|
3972
|
+
return await client.oneTimeProducts.getOffer(packageName, productId, purchaseOptionId, offerId);
|
|
3946
3973
|
} catch (error) {
|
|
3947
3974
|
throw new GpcError(
|
|
3948
3975
|
`Failed to get offer "${offerId}" for product "${productId}": ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -3952,9 +3979,9 @@ async function getOneTimeOffer(client, packageName, productId, offerId) {
|
|
|
3952
3979
|
);
|
|
3953
3980
|
}
|
|
3954
3981
|
}
|
|
3955
|
-
async function createOneTimeOffer(client, packageName, productId, data) {
|
|
3982
|
+
async function createOneTimeOffer(client, packageName, productId, data, purchaseOptionId = "-") {
|
|
3956
3983
|
try {
|
|
3957
|
-
return await client.oneTimeProducts.createOffer(packageName, productId, data);
|
|
3984
|
+
return await client.oneTimeProducts.createOffer(packageName, productId, purchaseOptionId, data);
|
|
3958
3985
|
} catch (error) {
|
|
3959
3986
|
throw new GpcError(
|
|
3960
3987
|
`Failed to create offer for product "${productId}": ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -3964,10 +3991,17 @@ async function createOneTimeOffer(client, packageName, productId, data) {
|
|
|
3964
3991
|
);
|
|
3965
3992
|
}
|
|
3966
3993
|
}
|
|
3967
|
-
async function updateOneTimeOffer(client, packageName, productId, offerId, data, updateMask) {
|
|
3994
|
+
async function updateOneTimeOffer(client, packageName, productId, offerId, data, updateMask, purchaseOptionId = "-") {
|
|
3968
3995
|
try {
|
|
3969
3996
|
const mask = updateMask || deriveOtpOfferUpdateMask(data);
|
|
3970
|
-
return await client.oneTimeProducts.updateOffer(
|
|
3997
|
+
return await client.oneTimeProducts.updateOffer(
|
|
3998
|
+
packageName,
|
|
3999
|
+
productId,
|
|
4000
|
+
purchaseOptionId,
|
|
4001
|
+
offerId,
|
|
4002
|
+
data,
|
|
4003
|
+
mask
|
|
4004
|
+
);
|
|
3971
4005
|
} catch (error) {
|
|
3972
4006
|
throw new GpcError(
|
|
3973
4007
|
`Failed to update offer "${offerId}" for product "${productId}": ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -3992,9 +4026,9 @@ async function diffOneTimeProduct(client, packageName, productId, localData) {
|
|
|
3992
4026
|
}
|
|
3993
4027
|
return diffs;
|
|
3994
4028
|
}
|
|
3995
|
-
async function deleteOneTimeOffer(client, packageName, productId, offerId) {
|
|
4029
|
+
async function deleteOneTimeOffer(client, packageName, productId, offerId, purchaseOptionId = "-") {
|
|
3996
4030
|
try {
|
|
3997
|
-
await client.oneTimeProducts.deleteOffer(packageName, productId, offerId);
|
|
4031
|
+
await client.oneTimeProducts.deleteOffer(packageName, productId, purchaseOptionId, offerId);
|
|
3998
4032
|
} catch (error) {
|
|
3999
4033
|
throw new GpcError(
|
|
4000
4034
|
`Failed to delete offer "${offerId}" for product "${productId}": ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -4225,7 +4259,13 @@ async function advanceTrain(apiClient, reportingClient, packageName) {
|
|
|
4225
4259
|
}
|
|
4226
4260
|
async function executeStage(apiClient, packageName, state, stageIndex) {
|
|
4227
4261
|
const stage = state.stages[stageIndex];
|
|
4228
|
-
if (!stage)
|
|
4262
|
+
if (!stage)
|
|
4263
|
+
throw new GpcError(
|
|
4264
|
+
`Stage ${stageIndex} not found`,
|
|
4265
|
+
"TRAIN_STAGE_NOT_FOUND",
|
|
4266
|
+
1,
|
|
4267
|
+
"Check your release train configuration."
|
|
4268
|
+
);
|
|
4229
4269
|
const rolloutFraction = stage.rollout / 100;
|
|
4230
4270
|
await updateRollout(apiClient, packageName, stage.track, "increase", rolloutFraction);
|
|
4231
4271
|
stage.executedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -4678,7 +4718,9 @@ var RESOURCE_IDS = {
|
|
|
4678
4718
|
16842754: "icon",
|
|
4679
4719
|
16842755: "name",
|
|
4680
4720
|
16842767: "versionCode",
|
|
4721
|
+
16843291: "versionCode",
|
|
4681
4722
|
16842768: "versionName",
|
|
4723
|
+
16843292: "versionName",
|
|
4682
4724
|
16843276: "minSdkVersion",
|
|
4683
4725
|
16843376: "targetSdkVersion",
|
|
4684
4726
|
16842782: "debuggable",
|
|
@@ -4694,27 +4736,39 @@ var RESOURCE_IDS = {
|
|
|
4694
4736
|
function buildXmlSchema() {
|
|
4695
4737
|
const root = new protobuf.Root();
|
|
4696
4738
|
const ns = root.define("aapt.pb");
|
|
4697
|
-
const
|
|
4698
|
-
const
|
|
4739
|
+
const NullType = new protobuf.Type("NullType");
|
|
4740
|
+
const EmptyType = new protobuf.Type("EmptyType");
|
|
4699
4741
|
const Primitive = new protobuf.Type("Primitive").add(
|
|
4700
|
-
new protobuf.OneOf("oneofValue").add(new protobuf.Field("intDecimalValue", 6, "int32")).add(new protobuf.Field("intHexadecimalValue", 7, "uint32")).add(new protobuf.Field("booleanValue", 8, "bool")).add(new protobuf.Field("
|
|
4701
|
-
);
|
|
4702
|
-
const Reference = new protobuf.Type("Reference").add(new protobuf.Field("id", 1, "uint32")).add(new protobuf.Field("name", 2, "string"));
|
|
4703
|
-
const Item = new protobuf.Type("Item").add(
|
|
4704
|
-
new protobuf.OneOf("value").add(new protobuf.Field("ref", 1, "Reference")).add(new protobuf.Field("str", 2, "String")).add(new protobuf.Field("prim", 4, "Primitive"))
|
|
4742
|
+
new protobuf.OneOf("oneofValue").add(new protobuf.Field("nullValue", 1, "NullType")).add(new protobuf.Field("emptyValue", 2, "EmptyType")).add(new protobuf.Field("floatValue", 3, "float")).add(new protobuf.Field("intDecimalValue", 6, "int32")).add(new protobuf.Field("intHexadecimalValue", 7, "uint32")).add(new protobuf.Field("booleanValue", 8, "bool")).add(new protobuf.Field("colorArgb8Value", 9, "uint32")).add(new protobuf.Field("colorRgb8Value", 10, "uint32")).add(new protobuf.Field("colorArgb4Value", 11, "uint32")).add(new protobuf.Field("colorRgb4Value", 12, "uint32")).add(new protobuf.Field("dimensionValue", 13, "uint32")).add(new protobuf.Field("fractionValue", 14, "uint32"))
|
|
4705
4743
|
);
|
|
4744
|
+
const Reference = new protobuf.Type("Reference").add(new protobuf.Field("type", 1, "uint32")).add(new protobuf.Field("id", 2, "uint32")).add(new protobuf.Field("name", 3, "string")).add(new protobuf.Field("isPrivate", 4, "bool")).add(new protobuf.Field("isDynamic", 5, "Boolean")).add(new protobuf.Field("typeFlags", 6, "uint32")).add(new protobuf.Field("allowRaw", 7, "bool"));
|
|
4706
4745
|
const StringMsg = new protobuf.Type("String").add(new protobuf.Field("value", 1, "string"));
|
|
4707
|
-
const
|
|
4708
|
-
const
|
|
4746
|
+
const RawString = new protobuf.Type("RawString").add(new protobuf.Field("value", 1, "string"));
|
|
4747
|
+
const StyledString = new protobuf.Type("StyledString").add(new protobuf.Field("value", 1, "string")).add(new protobuf.Field("span", 2, "Span", "repeated"));
|
|
4748
|
+
const Span = new protobuf.Type("Span").add(new protobuf.Field("tag", 1, "string")).add(new protobuf.Field("firstChar", 2, "uint32")).add(new protobuf.Field("lastChar", 3, "uint32"));
|
|
4749
|
+
const FileReference = new protobuf.Type("FileReference").add(new protobuf.Field("path", 1, "string")).add(new protobuf.Field("type", 2, "uint32"));
|
|
4750
|
+
const Id = new protobuf.Type("Id");
|
|
4751
|
+
const BooleanMsg = new protobuf.Type("Boolean").add(new protobuf.Field("value", 1, "bool"));
|
|
4752
|
+
const Item = new protobuf.Type("Item").add(
|
|
4753
|
+
new protobuf.OneOf("value").add(new protobuf.Field("ref", 1, "Reference")).add(new protobuf.Field("str", 2, "String")).add(new protobuf.Field("rawStr", 3, "RawString")).add(new protobuf.Field("styledStr", 4, "StyledString")).add(new protobuf.Field("file", 5, "FileReference")).add(new protobuf.Field("id", 6, "Id")).add(new protobuf.Field("prim", 7, "Primitive"))
|
|
4754
|
+
).add(new protobuf.Field("flagStatus", 8, "uint32")).add(new protobuf.Field("flagNegated", 9, "bool")).add(new protobuf.Field("flagName", 10, "string"));
|
|
4755
|
+
const XmlAttribute = new protobuf.Type("XmlAttribute").add(new protobuf.Field("namespaceUri", 1, "string")).add(new protobuf.Field("name", 2, "string")).add(new protobuf.Field("value", 3, "string")).add(new protobuf.Field("resourceId", 5, "uint32")).add(new protobuf.Field("compiledItem", 6, "Item"));
|
|
4756
|
+
const XmlNamespace = new protobuf.Type("XmlNamespace").add(new protobuf.Field("prefix", 1, "string")).add(new protobuf.Field("uri", 2, "string"));
|
|
4709
4757
|
const XmlElement = new protobuf.Type("XmlElement").add(new protobuf.Field("namespaceDeclaration", 1, "XmlNamespace", "repeated")).add(new protobuf.Field("namespaceUri", 2, "string")).add(new protobuf.Field("name", 3, "string")).add(new protobuf.Field("attribute", 4, "XmlAttribute", "repeated")).add(new protobuf.Field("child", 5, "XmlNode", "repeated"));
|
|
4710
4758
|
const XmlNode = new protobuf.Type("XmlNode").add(
|
|
4711
4759
|
new protobuf.OneOf("node").add(new protobuf.Field("element", 1, "XmlElement")).add(new protobuf.Field("text", 2, "string"))
|
|
4712
|
-
)
|
|
4713
|
-
ns.add(
|
|
4714
|
-
ns.add(
|
|
4760
|
+
);
|
|
4761
|
+
ns.add(NullType);
|
|
4762
|
+
ns.add(EmptyType);
|
|
4715
4763
|
ns.add(Primitive);
|
|
4764
|
+
ns.add(BooleanMsg);
|
|
4716
4765
|
ns.add(Reference);
|
|
4717
4766
|
ns.add(StringMsg);
|
|
4767
|
+
ns.add(RawString);
|
|
4768
|
+
ns.add(StyledString);
|
|
4769
|
+
ns.add(Span);
|
|
4770
|
+
ns.add(FileReference);
|
|
4771
|
+
ns.add(Id);
|
|
4718
4772
|
ns.add(Item);
|
|
4719
4773
|
ns.add(XmlAttribute);
|
|
4720
4774
|
ns.add(XmlNamespace);
|
|
@@ -4736,29 +4790,26 @@ function decodeManifest(buf) {
|
|
|
4736
4790
|
}
|
|
4737
4791
|
return extractManifestData(decoded.element);
|
|
4738
4792
|
}
|
|
4739
|
-
function getAttrValue(attrs, resId) {
|
|
4740
|
-
const attr = attrs.find((a) => a.resourceId === resId);
|
|
4741
|
-
if (!attr) return void 0;
|
|
4742
|
-
const ci = attr.compiledItem;
|
|
4743
|
-
if (ci?.str?.value !== void 0) return ci.str.value;
|
|
4744
|
-
if (ci?.ref?.name !== void 0) return ci.ref.name;
|
|
4745
|
-
return attr.value || void 0;
|
|
4746
|
-
}
|
|
4747
4793
|
function getAttrByName(attrs, name) {
|
|
4748
4794
|
const attr = attrs.find((a) => a.name === name || RESOURCE_IDS[a.resourceId] === name);
|
|
4749
4795
|
if (!attr) return void 0;
|
|
4750
4796
|
const ci = attr.compiledItem;
|
|
4751
4797
|
if (ci?.str?.value !== void 0) return ci.str.value;
|
|
4752
4798
|
if (ci?.ref?.name !== void 0) return ci.ref.name;
|
|
4753
|
-
|
|
4799
|
+
const prim = ci?.prim;
|
|
4800
|
+
if (attr.value) return attr.value;
|
|
4801
|
+
if (prim?.booleanValue === true) return "true";
|
|
4802
|
+
if (prim?.intDecimalValue) return String(prim.intDecimalValue);
|
|
4803
|
+
if (prim?.intHexadecimalValue) return String(prim.intHexadecimalValue);
|
|
4804
|
+
return void 0;
|
|
4754
4805
|
}
|
|
4755
|
-
function
|
|
4756
|
-
const val =
|
|
4806
|
+
function getBoolByName(attrs, name, defaultVal) {
|
|
4807
|
+
const val = getAttrByName(attrs, name);
|
|
4757
4808
|
if (val === void 0) return defaultVal;
|
|
4758
4809
|
return val === "true" || val === "1";
|
|
4759
4810
|
}
|
|
4760
|
-
function
|
|
4761
|
-
const val =
|
|
4811
|
+
function getIntByName(attrs, name, defaultVal) {
|
|
4812
|
+
const val = getAttrByName(attrs, name);
|
|
4762
4813
|
if (val === void 0) return defaultVal;
|
|
4763
4814
|
const n = parseInt(val, 10);
|
|
4764
4815
|
return isNaN(n) ? defaultVal : n;
|
|
@@ -4769,23 +4820,25 @@ function getChildren(elem, tagName) {
|
|
|
4769
4820
|
function extractManifestData(manifest) {
|
|
4770
4821
|
const attrs = manifest.attribute || [];
|
|
4771
4822
|
const packageName = getAttrByName(attrs, "package") || "";
|
|
4772
|
-
const
|
|
4773
|
-
const
|
|
4823
|
+
const versionCodeStr = getAttrByName(attrs, "versionCode");
|
|
4824
|
+
const versionCode = versionCodeStr ? parseInt(versionCodeStr, 10) || 0 : 0;
|
|
4825
|
+
const versionName = getAttrByName(attrs, "versionName") || "";
|
|
4774
4826
|
const usesSdkElements = getChildren(manifest, "uses-sdk");
|
|
4775
4827
|
const usesSdk = usesSdkElements[0];
|
|
4776
|
-
const minSdk = usesSdk ?
|
|
4777
|
-
const targetSdk = usesSdk ?
|
|
4778
|
-
const permissions = getChildren(manifest, "uses-permission").map((el) =>
|
|
4828
|
+
const minSdk = usesSdk ? getIntByName(usesSdk.attribute || [], "minSdkVersion", 1) : 1;
|
|
4829
|
+
const targetSdk = usesSdk ? getIntByName(usesSdk.attribute || [], "targetSdkVersion", minSdk) : minSdk;
|
|
4830
|
+
const permissions = getChildren(manifest, "uses-permission").map((el) => getAttrByName(el.attribute || [], "name")).filter((p) => p !== void 0);
|
|
4779
4831
|
const features = getChildren(manifest, "uses-feature").map((el) => ({
|
|
4780
|
-
name:
|
|
4781
|
-
required:
|
|
4832
|
+
name: getAttrByName(el.attribute || [], "name") || "",
|
|
4833
|
+
required: getBoolByName(el.attribute || [], "required", true)
|
|
4782
4834
|
}));
|
|
4783
4835
|
const appElements = getChildren(manifest, "application");
|
|
4784
4836
|
const app = appElements[0];
|
|
4785
|
-
const debuggable = app ?
|
|
4786
|
-
const testOnly =
|
|
4787
|
-
const usesCleartextTraffic = app ?
|
|
4788
|
-
const extractNativeLibs = app ?
|
|
4837
|
+
const debuggable = app ? getBoolByName(app.attribute || [], "debuggable", false) : false;
|
|
4838
|
+
const testOnly = getBoolByName(attrs, "testOnly", false);
|
|
4839
|
+
const usesCleartextTraffic = app ? getBoolByName(app.attribute || [], "usesCleartextTraffic", true) : true;
|
|
4840
|
+
const extractNativeLibs = app ? getBoolByName(app.attribute || [], "extractNativeLibs", true) : true;
|
|
4841
|
+
const pageSizeCompat = app ? getBoolByName(app.attribute || [], "pageSizeCompat", false) : false;
|
|
4789
4842
|
const activities = app ? extractComponents(app, "activity") : [];
|
|
4790
4843
|
const services = app ? extractComponents(app, "service") : [];
|
|
4791
4844
|
const receivers = app ? extractComponents(app, "receiver") : [];
|
|
@@ -4800,6 +4853,7 @@ function extractManifestData(manifest) {
|
|
|
4800
4853
|
testOnly,
|
|
4801
4854
|
usesCleartextTraffic,
|
|
4802
4855
|
extractNativeLibs,
|
|
4856
|
+
pageSizeCompat: pageSizeCompat || void 0,
|
|
4803
4857
|
permissions,
|
|
4804
4858
|
features,
|
|
4805
4859
|
activities,
|
|
@@ -4811,13 +4865,29 @@ function extractManifestData(manifest) {
|
|
|
4811
4865
|
function extractComponents(appElement, tagName) {
|
|
4812
4866
|
return getChildren(appElement, tagName).map((el) => {
|
|
4813
4867
|
const compAttrs = el.attribute || [];
|
|
4814
|
-
const exportedVal =
|
|
4815
|
-
const
|
|
4868
|
+
const exportedVal = getAttrByName(compAttrs, "exported");
|
|
4869
|
+
const intentFilters = getChildren(el, "intent-filter");
|
|
4870
|
+
const hasIntentFilter = intentFilters.length > 0;
|
|
4871
|
+
const intentActions = [];
|
|
4872
|
+
const intentCategories = [];
|
|
4873
|
+
for (const filter of intentFilters) {
|
|
4874
|
+
for (const action of getChildren(filter, "action")) {
|
|
4875
|
+
const name = getAttrByName(action.attribute || [], "name");
|
|
4876
|
+
if (name) intentActions.push(name);
|
|
4877
|
+
}
|
|
4878
|
+
for (const cat of getChildren(filter, "category")) {
|
|
4879
|
+
const name = getAttrByName(cat.attribute || [], "name");
|
|
4880
|
+
if (name) intentCategories.push(name);
|
|
4881
|
+
}
|
|
4882
|
+
}
|
|
4816
4883
|
return {
|
|
4817
|
-
name:
|
|
4884
|
+
name: getAttrByName(compAttrs, "name") || "",
|
|
4818
4885
|
exported: exportedVal === void 0 ? void 0 : exportedVal === "true" || exportedVal === "1",
|
|
4819
|
-
|
|
4820
|
-
|
|
4886
|
+
permission: getAttrByName(compAttrs, "permission") || void 0,
|
|
4887
|
+
foregroundServiceType: tagName === "service" ? getAttrByName(compAttrs, "foregroundServiceType") : void 0,
|
|
4888
|
+
hasIntentFilter,
|
|
4889
|
+
intentActions: intentActions.length > 0 ? intentActions : void 0,
|
|
4890
|
+
intentCategories: intentCategories.length > 0 ? intentCategories : void 0
|
|
4821
4891
|
};
|
|
4822
4892
|
});
|
|
4823
4893
|
}
|
|
@@ -4825,12 +4895,14 @@ function extractComponents(appElement, tagName) {
|
|
|
4825
4895
|
// src/preflight/aab-reader.ts
|
|
4826
4896
|
var AAB_MANIFEST_PATH = "base/manifest/AndroidManifest.xml";
|
|
4827
4897
|
var APK_MANIFEST_PATH = "AndroidManifest.xml";
|
|
4898
|
+
var SO_HEADER_BYTES = 256;
|
|
4899
|
+
var SO_PATH_RE = /\/lib\/[^/]+\/[^/]+\.so$/;
|
|
4828
4900
|
function detectManifestPath(filePath) {
|
|
4829
4901
|
return filePath.toLowerCase().endsWith(".apk") ? APK_MANIFEST_PATH : AAB_MANIFEST_PATH;
|
|
4830
4902
|
}
|
|
4831
4903
|
async function readAab(aabPath) {
|
|
4832
4904
|
const manifestPath = detectManifestPath(aabPath);
|
|
4833
|
-
const { zipfile, entries, manifestBuf } = await openAndScan(aabPath, manifestPath);
|
|
4905
|
+
const { zipfile, entries, manifestBuf, soHeaders } = await openAndScan(aabPath, manifestPath);
|
|
4834
4906
|
zipfile.close();
|
|
4835
4907
|
if (!manifestBuf) {
|
|
4836
4908
|
const fileType = aabPath.toLowerCase().endsWith(".apk") ? "APK" : "AAB";
|
|
@@ -4846,7 +4918,7 @@ async function readAab(aabPath) {
|
|
|
4846
4918
|
manifest = createFallbackManifest();
|
|
4847
4919
|
manifest._parseError = `Manifest could not be fully parsed: ${errMsg}. Manifest-dependent checks will be skipped.`;
|
|
4848
4920
|
}
|
|
4849
|
-
return { manifest, entries };
|
|
4921
|
+
return { manifest, entries, nativeLibHeaders: soHeaders };
|
|
4850
4922
|
}
|
|
4851
4923
|
function createFallbackManifest() {
|
|
4852
4924
|
return {
|
|
@@ -4875,8 +4947,11 @@ function openAndScan(aabPath, manifestPath = AAB_MANIFEST_PATH) {
|
|
|
4875
4947
|
return;
|
|
4876
4948
|
}
|
|
4877
4949
|
const entries = [];
|
|
4950
|
+
const soHeaders = /* @__PURE__ */ new Map();
|
|
4878
4951
|
let manifestBuf = null;
|
|
4879
4952
|
let rejected = false;
|
|
4953
|
+
let pendingStreams = 0;
|
|
4954
|
+
let entrysDone = false;
|
|
4880
4955
|
function fail(error) {
|
|
4881
4956
|
if (!rejected) {
|
|
4882
4957
|
rejected = true;
|
|
@@ -4884,6 +4959,46 @@ function openAndScan(aabPath, manifestPath = AAB_MANIFEST_PATH) {
|
|
|
4884
4959
|
reject(error);
|
|
4885
4960
|
}
|
|
4886
4961
|
}
|
|
4962
|
+
function maybeResolve() {
|
|
4963
|
+
if (!rejected && entrysDone && pendingStreams === 0) {
|
|
4964
|
+
resolve2({ zipfile, entries, manifestBuf, soHeaders });
|
|
4965
|
+
}
|
|
4966
|
+
}
|
|
4967
|
+
function readEntryStream(entry, onData, maxBytes) {
|
|
4968
|
+
pendingStreams++;
|
|
4969
|
+
zipfile.openReadStream(entry, (streamErr, stream) => {
|
|
4970
|
+
if (streamErr || !stream) {
|
|
4971
|
+
fail(streamErr ?? new Error(`Failed to read ${entry.fileName}`));
|
|
4972
|
+
return;
|
|
4973
|
+
}
|
|
4974
|
+
const chunks = [];
|
|
4975
|
+
let totalBytes = 0;
|
|
4976
|
+
let done = false;
|
|
4977
|
+
stream.on("data", (chunk) => {
|
|
4978
|
+
chunks.push(chunk);
|
|
4979
|
+
totalBytes += chunk.length;
|
|
4980
|
+
if (maxBytes && totalBytes >= maxBytes) {
|
|
4981
|
+
stream.destroy();
|
|
4982
|
+
}
|
|
4983
|
+
});
|
|
4984
|
+
stream.on("error", (e) => {
|
|
4985
|
+
if (maxBytes && totalBytes >= maxBytes) return;
|
|
4986
|
+
fail(e);
|
|
4987
|
+
});
|
|
4988
|
+
const finish = () => {
|
|
4989
|
+
if (done) return;
|
|
4990
|
+
done = true;
|
|
4991
|
+
const buf = Buffer.concat(chunks);
|
|
4992
|
+
onData(maxBytes ? buf.subarray(0, maxBytes) : buf);
|
|
4993
|
+
pendingStreams--;
|
|
4994
|
+
maybeResolve();
|
|
4995
|
+
};
|
|
4996
|
+
stream.on("end", finish);
|
|
4997
|
+
stream.on("close", () => {
|
|
4998
|
+
if (totalBytes > 0 && maxBytes) finish();
|
|
4999
|
+
});
|
|
5000
|
+
});
|
|
5001
|
+
}
|
|
4887
5002
|
zipfile.on("error", fail);
|
|
4888
5003
|
zipfile.on("entry", (entry) => {
|
|
4889
5004
|
if (rejected) return;
|
|
@@ -4896,27 +5011,23 @@ function openAndScan(aabPath, manifestPath = AAB_MANIFEST_PATH) {
|
|
|
4896
5011
|
});
|
|
4897
5012
|
}
|
|
4898
5013
|
if (path === manifestPath) {
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
fail(streamErr ?? new Error("Failed to read manifest entry"));
|
|
4902
|
-
return;
|
|
4903
|
-
}
|
|
4904
|
-
const chunks = [];
|
|
4905
|
-
stream.on("data", (chunk) => chunks.push(chunk));
|
|
4906
|
-
stream.on("error", (e) => fail(e));
|
|
4907
|
-
stream.on("end", () => {
|
|
4908
|
-
manifestBuf = Buffer.concat(chunks);
|
|
4909
|
-
zipfile.readEntry();
|
|
4910
|
-
});
|
|
5014
|
+
readEntryStream(entry, (buf) => {
|
|
5015
|
+
manifestBuf = buf;
|
|
4911
5016
|
});
|
|
4912
|
-
} else {
|
|
4913
|
-
|
|
5017
|
+
} else if (SO_PATH_RE.test(path)) {
|
|
5018
|
+
readEntryStream(
|
|
5019
|
+
entry,
|
|
5020
|
+
(buf) => {
|
|
5021
|
+
soHeaders.set(path, buf);
|
|
5022
|
+
},
|
|
5023
|
+
SO_HEADER_BYTES
|
|
5024
|
+
);
|
|
4914
5025
|
}
|
|
5026
|
+
zipfile.readEntry();
|
|
4915
5027
|
});
|
|
4916
5028
|
zipfile.on("end", () => {
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
}
|
|
5029
|
+
entrysDone = true;
|
|
5030
|
+
maybeResolve();
|
|
4920
5031
|
});
|
|
4921
5032
|
zipfile.readEntry();
|
|
4922
5033
|
});
|
|
@@ -4985,13 +5096,13 @@ var manifestScanner = {
|
|
|
4985
5096
|
});
|
|
4986
5097
|
}
|
|
4987
5098
|
if (manifest.targetSdk >= 31) {
|
|
4988
|
-
const
|
|
5099
|
+
const allComponents2 = [
|
|
4989
5100
|
...manifest.activities,
|
|
4990
5101
|
...manifest.services,
|
|
4991
5102
|
...manifest.receivers,
|
|
4992
5103
|
...manifest.providers
|
|
4993
5104
|
];
|
|
4994
|
-
for (const comp of
|
|
5105
|
+
for (const comp of allComponents2) {
|
|
4995
5106
|
if (comp.hasIntentFilter && comp.exported === void 0) {
|
|
4996
5107
|
findings.push({
|
|
4997
5108
|
scanner: "manifest",
|
|
@@ -5023,6 +5134,27 @@ var manifestScanner = {
|
|
|
5023
5134
|
}
|
|
5024
5135
|
}
|
|
5025
5136
|
}
|
|
5137
|
+
const allComponents = [
|
|
5138
|
+
...manifest.activities,
|
|
5139
|
+
...manifest.services,
|
|
5140
|
+
...manifest.receivers,
|
|
5141
|
+
...manifest.providers
|
|
5142
|
+
];
|
|
5143
|
+
for (const comp of allComponents) {
|
|
5144
|
+
if (comp.exported !== true) continue;
|
|
5145
|
+
if (comp.permission) continue;
|
|
5146
|
+
const isLauncher = comp.intentActions?.includes("android.intent.action.MAIN") && comp.intentCategories?.includes("android.intent.category.LAUNCHER");
|
|
5147
|
+
if (isLauncher) continue;
|
|
5148
|
+
findings.push({
|
|
5149
|
+
scanner: "manifest",
|
|
5150
|
+
ruleId: "exported-no-permission",
|
|
5151
|
+
severity: "warning",
|
|
5152
|
+
title: `Exported component "${comp.name}" has no permission`,
|
|
5153
|
+
message: `"${comp.name}" is exported without an android:permission attribute. Any app on the device can access this component.`,
|
|
5154
|
+
suggestion: `Add android:permission to restrict access, or set android:exported="false" if external access is not needed.`,
|
|
5155
|
+
policyUrl: "https://developer.android.com/privacy-and-security/security-tips#components"
|
|
5156
|
+
});
|
|
5157
|
+
}
|
|
5026
5158
|
if (manifest.minSdk < 21) {
|
|
5027
5159
|
findings.push({
|
|
5028
5160
|
scanner: "manifest",
|
|
@@ -5253,9 +5385,53 @@ var permissionsScanner = {
|
|
|
5253
5385
|
// src/preflight/scanners/native-libs-scanner.ts
|
|
5254
5386
|
var KNOWN_ABIS = ["arm64-v8a", "armeabi-v7a", "x86", "x86_64"];
|
|
5255
5387
|
var LIB_PATH_RE = /^(?:[^/]+\/)?lib\/([^/]+)\/[^/]+\.so$/;
|
|
5388
|
+
var ELF_MAGIC = 2135247942;
|
|
5389
|
+
var PT_LOAD = 1;
|
|
5390
|
+
var PAGE_16KB = 16384;
|
|
5391
|
+
function checkElfAlignment(buf) {
|
|
5392
|
+
if (buf.length < 52) return null;
|
|
5393
|
+
if (buf.readUInt32BE(0) !== ELF_MAGIC) return null;
|
|
5394
|
+
const is64 = buf[4] === 2;
|
|
5395
|
+
const isLE = buf[5] === 1;
|
|
5396
|
+
const read16 = isLE ? (o) => buf.readUInt16LE(o) : (o) => buf.readUInt16BE(o);
|
|
5397
|
+
const read32 = isLE ? (o) => buf.readUInt32LE(o) : (o) => buf.readUInt32BE(o);
|
|
5398
|
+
const read64 = isLE ? (o) => Number(buf.readBigUInt64LE(o)) : (o) => Number(buf.readBigUInt64BE(o));
|
|
5399
|
+
let phOff, phSize, phNum;
|
|
5400
|
+
if (is64) {
|
|
5401
|
+
if (buf.length < 64) return null;
|
|
5402
|
+
phOff = read64(32);
|
|
5403
|
+
phSize = read16(54);
|
|
5404
|
+
phNum = read16(56);
|
|
5405
|
+
} else {
|
|
5406
|
+
phOff = read32(28);
|
|
5407
|
+
phSize = read16(42);
|
|
5408
|
+
phNum = read16(44);
|
|
5409
|
+
}
|
|
5410
|
+
if (phNum === 0 || phSize === 0) return null;
|
|
5411
|
+
let minAlign = Infinity;
|
|
5412
|
+
let foundLoad = false;
|
|
5413
|
+
for (let i = 0; i < phNum; i++) {
|
|
5414
|
+
const off = phOff + i * phSize;
|
|
5415
|
+
if (off + phSize > buf.length) break;
|
|
5416
|
+
const pType = read32(off);
|
|
5417
|
+
if (pType !== PT_LOAD) continue;
|
|
5418
|
+
foundLoad = true;
|
|
5419
|
+
const pAlign = is64 ? read64(off + 48) : read32(off + 28);
|
|
5420
|
+
if (pAlign > 0 && pAlign < minAlign) {
|
|
5421
|
+
minAlign = pAlign;
|
|
5422
|
+
}
|
|
5423
|
+
}
|
|
5424
|
+
if (!foundLoad) return null;
|
|
5425
|
+
return { minAlign: minAlign === Infinity ? 0 : minAlign, is64 };
|
|
5426
|
+
}
|
|
5427
|
+
function alignmentLabel(align) {
|
|
5428
|
+
if (align <= 0) return "unknown";
|
|
5429
|
+
const log2 = Math.log2(align);
|
|
5430
|
+
return Number.isInteger(log2) ? `${align / 1024}KB (2^${log2})` : `${align} bytes`;
|
|
5431
|
+
}
|
|
5256
5432
|
var nativeLibsScanner = {
|
|
5257
5433
|
name: "native-libs",
|
|
5258
|
-
description: "Checks native library architectures
|
|
5434
|
+
description: "Checks native library architectures, 64-bit compliance, and 16KB alignment",
|
|
5259
5435
|
requires: ["zipEntries"],
|
|
5260
5436
|
async scan(ctx) {
|
|
5261
5437
|
const entries = ctx.zipEntries;
|
|
@@ -5298,6 +5474,7 @@ var nativeLibsScanner = {
|
|
|
5298
5474
|
policyUrl: "https://developer.android.com/google/play/requirements/64-bit"
|
|
5299
5475
|
});
|
|
5300
5476
|
}
|
|
5477
|
+
check16KBAlignment(ctx.nativeLibHeaders, ctx.manifest, findings);
|
|
5301
5478
|
const detectedAbis = KNOWN_ABIS.filter((abi) => abisFound.has(abi));
|
|
5302
5479
|
const unknownAbis = [...abisFound].filter(
|
|
5303
5480
|
(abi) => !KNOWN_ABIS.includes(abi)
|
|
@@ -5324,6 +5501,33 @@ var nativeLibsScanner = {
|
|
|
5324
5501
|
return findings;
|
|
5325
5502
|
}
|
|
5326
5503
|
};
|
|
5504
|
+
function check16KBAlignment(headers, manifest, findings) {
|
|
5505
|
+
if (!headers || headers.size === 0) return;
|
|
5506
|
+
const nonCompliant = [];
|
|
5507
|
+
for (const [path, buf] of headers) {
|
|
5508
|
+
const result = checkElfAlignment(buf);
|
|
5509
|
+
if (!result) continue;
|
|
5510
|
+
if (result.minAlign < PAGE_16KB) {
|
|
5511
|
+
nonCompliant.push({ path, align: result.minAlign });
|
|
5512
|
+
}
|
|
5513
|
+
}
|
|
5514
|
+
if (nonCompliant.length === 0) return;
|
|
5515
|
+
const hasPageSizeCompat = manifest?.pageSizeCompat === true;
|
|
5516
|
+
const fileList = nonCompliant.map((f) => {
|
|
5517
|
+
const name = f.path.split("/").pop() ?? f.path;
|
|
5518
|
+
return ` ${name}: aligned to ${alignmentLabel(f.align)}, requires 16KB (2^14)`;
|
|
5519
|
+
}).join("\n");
|
|
5520
|
+
findings.push({
|
|
5521
|
+
scanner: "native-libs",
|
|
5522
|
+
ruleId: "native-libs-16kb-alignment",
|
|
5523
|
+
severity: hasPageSizeCompat ? "warning" : "critical",
|
|
5524
|
+
title: `${nonCompliant.length} native ${nonCompliant.length === 1 ? "library" : "libraries"} not 16KB aligned`,
|
|
5525
|
+
message: `Google Play requires 16KB page size alignment for all native libraries (enforced since Nov 2025).
|
|
5526
|
+
${fileList}` + (hasPageSizeCompat ? "\nandroid:pageSizeCompat is set, so the app will work via compatibility mode, but users on 16KB devices will see a compatibility dialog." : ""),
|
|
5527
|
+
suggestion: "Recompile with: -Wl,-z,max-page-size=16384\nOr upgrade to NDK r28+ (16KB aligned by default).\nOr set cmake flag: -DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON (NDK r27).",
|
|
5528
|
+
policyUrl: "https://developer.android.com/guide/practices/page-sizes"
|
|
5529
|
+
});
|
|
5530
|
+
}
|
|
5327
5531
|
|
|
5328
5532
|
// src/preflight/scanners/metadata-scanner.ts
|
|
5329
5533
|
import { readdir as readdir5, stat as stat8, readFile as readFile10 } from "fs/promises";
|
|
@@ -6021,6 +6225,7 @@ async function runPreflight(options) {
|
|
|
6021
6225
|
const aab = await readAab(options.aabPath);
|
|
6022
6226
|
ctx.manifest = aab.manifest;
|
|
6023
6227
|
ctx.zipEntries = aab.entries;
|
|
6228
|
+
ctx.nativeLibHeaders = aab.nativeLibHeaders;
|
|
6024
6229
|
if (aab.manifest._parseError) {
|
|
6025
6230
|
earlyFindings.push({
|
|
6026
6231
|
scanner: "manifest-parser",
|
|
@@ -6431,68 +6636,6 @@ async function downloadGeneratedApk(client, packageName, versionCode, apkId, out
|
|
|
6431
6636
|
return { path: outputPath, sizeBytes: bytes.byteLength };
|
|
6432
6637
|
}
|
|
6433
6638
|
|
|
6434
|
-
// src/commands/purchase-options.ts
|
|
6435
|
-
async function listPurchaseOptions(client, packageName) {
|
|
6436
|
-
try {
|
|
6437
|
-
return await client.purchaseOptions.list(packageName);
|
|
6438
|
-
} catch (error) {
|
|
6439
|
-
throw new GpcError(
|
|
6440
|
-
`Failed to list purchase options: ${error instanceof Error ? error.message : String(error)}`,
|
|
6441
|
-
"PURCHASE_OPTIONS_LIST_FAILED",
|
|
6442
|
-
4,
|
|
6443
|
-
"Check your package name and API permissions."
|
|
6444
|
-
);
|
|
6445
|
-
}
|
|
6446
|
-
}
|
|
6447
|
-
async function getPurchaseOption(client, packageName, purchaseOptionId) {
|
|
6448
|
-
try {
|
|
6449
|
-
return await client.purchaseOptions.get(packageName, purchaseOptionId);
|
|
6450
|
-
} catch (error) {
|
|
6451
|
-
throw new GpcError(
|
|
6452
|
-
`Failed to get purchase option "${purchaseOptionId}": ${error instanceof Error ? error.message : String(error)}`,
|
|
6453
|
-
"PURCHASE_OPTION_GET_FAILED",
|
|
6454
|
-
4,
|
|
6455
|
-
"Check that the purchase option ID exists."
|
|
6456
|
-
);
|
|
6457
|
-
}
|
|
6458
|
-
}
|
|
6459
|
-
async function createPurchaseOption(client, packageName, data) {
|
|
6460
|
-
try {
|
|
6461
|
-
return await client.purchaseOptions.create(packageName, data);
|
|
6462
|
-
} catch (error) {
|
|
6463
|
-
throw new GpcError(
|
|
6464
|
-
`Failed to create purchase option: ${error instanceof Error ? error.message : String(error)}`,
|
|
6465
|
-
"PURCHASE_OPTION_CREATE_FAILED",
|
|
6466
|
-
4,
|
|
6467
|
-
"Check your purchase option data and API permissions."
|
|
6468
|
-
);
|
|
6469
|
-
}
|
|
6470
|
-
}
|
|
6471
|
-
async function activatePurchaseOption(client, packageName, purchaseOptionId) {
|
|
6472
|
-
try {
|
|
6473
|
-
return await client.purchaseOptions.activate(packageName, purchaseOptionId);
|
|
6474
|
-
} catch (error) {
|
|
6475
|
-
throw new GpcError(
|
|
6476
|
-
`Failed to activate purchase option "${purchaseOptionId}": ${error instanceof Error ? error.message : String(error)}`,
|
|
6477
|
-
"PURCHASE_OPTION_ACTIVATE_FAILED",
|
|
6478
|
-
4,
|
|
6479
|
-
"Check that the purchase option exists and is in a valid state for activation."
|
|
6480
|
-
);
|
|
6481
|
-
}
|
|
6482
|
-
}
|
|
6483
|
-
async function deactivatePurchaseOption(client, packageName, purchaseOptionId) {
|
|
6484
|
-
try {
|
|
6485
|
-
return await client.purchaseOptions.deactivate(packageName, purchaseOptionId);
|
|
6486
|
-
} catch (error) {
|
|
6487
|
-
throw new GpcError(
|
|
6488
|
-
`Failed to deactivate purchase option "${purchaseOptionId}": ${error instanceof Error ? error.message : String(error)}`,
|
|
6489
|
-
"PURCHASE_OPTION_DEACTIVATE_FAILED",
|
|
6490
|
-
4,
|
|
6491
|
-
"Check that the purchase option exists and is in a valid state for deactivation."
|
|
6492
|
-
);
|
|
6493
|
-
}
|
|
6494
|
-
}
|
|
6495
|
-
|
|
6496
6639
|
// src/commands/bundle-analysis.ts
|
|
6497
6640
|
import { readFile as readFile14, stat as stat10 } from "fs/promises";
|
|
6498
6641
|
var EOCD_SIGNATURE = 101010256;
|
|
@@ -7177,21 +7320,15 @@ async function fetchChangelog(options) {
|
|
|
7177
7320
|
signal: controller.signal
|
|
7178
7321
|
});
|
|
7179
7322
|
} catch {
|
|
7180
|
-
throw new Error(
|
|
7181
|
-
`Could not fetch changelog. View online: ${DOCS_CHANGELOG_URL}`
|
|
7182
|
-
);
|
|
7323
|
+
throw new Error(`Could not fetch changelog. View online: ${DOCS_CHANGELOG_URL}`);
|
|
7183
7324
|
} finally {
|
|
7184
7325
|
clearTimeout(timer);
|
|
7185
7326
|
}
|
|
7186
7327
|
if (!response.ok) {
|
|
7187
7328
|
if (response.status === 404 && options?.version) {
|
|
7188
|
-
throw new Error(
|
|
7189
|
-
`Version ${options.version} not found. Run: gpc changelog --limit 10`
|
|
7190
|
-
);
|
|
7329
|
+
throw new Error(`Version ${options.version} not found. Run: gpc changelog --limit 10`);
|
|
7191
7330
|
}
|
|
7192
|
-
throw new Error(
|
|
7193
|
-
`GitHub API returned ${response.status}. View online: ${DOCS_CHANGELOG_URL}`
|
|
7194
|
-
);
|
|
7331
|
+
throw new Error(`GitHub API returned ${response.status}. View online: ${DOCS_CHANGELOG_URL}`);
|
|
7195
7332
|
}
|
|
7196
7333
|
const data = await response.json();
|
|
7197
7334
|
const releases = Array.isArray(data) ? data : [data];
|
|
@@ -7240,7 +7377,7 @@ async function getRtdnStatus(client, packageName) {
|
|
|
7240
7377
|
try {
|
|
7241
7378
|
const edit = await client.edits.insert(packageName);
|
|
7242
7379
|
try {
|
|
7243
|
-
|
|
7380
|
+
await client.details.get(packageName, edit.id);
|
|
7244
7381
|
return {
|
|
7245
7382
|
topicName: null,
|
|
7246
7383
|
enabled: false
|
|
@@ -7324,7 +7461,6 @@ export {
|
|
|
7324
7461
|
acknowledgeProductPurchase,
|
|
7325
7462
|
activateBasePlan,
|
|
7326
7463
|
activateOffer,
|
|
7327
|
-
activatePurchaseOption,
|
|
7328
7464
|
addRecoveryTargeting,
|
|
7329
7465
|
addTesters,
|
|
7330
7466
|
advanceTrain,
|
|
@@ -7354,14 +7490,12 @@ export {
|
|
|
7354
7490
|
createOffer,
|
|
7355
7491
|
createOneTimeOffer,
|
|
7356
7492
|
createOneTimeProduct,
|
|
7357
|
-
createPurchaseOption,
|
|
7358
7493
|
createRecoveryAction,
|
|
7359
7494
|
createSpinner,
|
|
7360
7495
|
createSubscription,
|
|
7361
7496
|
createTrack,
|
|
7362
7497
|
deactivateBasePlan,
|
|
7363
7498
|
deactivateOffer,
|
|
7364
|
-
deactivatePurchaseOption,
|
|
7365
7499
|
decodeNotification,
|
|
7366
7500
|
deferSubscriptionPurchase,
|
|
7367
7501
|
deferSubscriptionV2,
|
|
@@ -7419,7 +7553,6 @@ export {
|
|
|
7419
7553
|
getOrderDetails,
|
|
7420
7554
|
getProductPurchase,
|
|
7421
7555
|
getProductPurchaseV2,
|
|
7422
|
-
getPurchaseOption,
|
|
7423
7556
|
getQuotaUsage,
|
|
7424
7557
|
getReleasesStatus,
|
|
7425
7558
|
getReview,
|
|
@@ -7464,7 +7597,6 @@ export {
|
|
|
7464
7597
|
listOffers,
|
|
7465
7598
|
listOneTimeOffers,
|
|
7466
7599
|
listOneTimeProducts,
|
|
7467
|
-
listPurchaseOptions,
|
|
7468
7600
|
listRecoveryActions,
|
|
7469
7601
|
listReports,
|
|
7470
7602
|
listReviews,
|
|
@@ -7492,7 +7624,6 @@ export {
|
|
|
7492
7624
|
redactSensitive,
|
|
7493
7625
|
refundExternalTransaction,
|
|
7494
7626
|
refundOrder,
|
|
7495
|
-
refundSubscriptionV2,
|
|
7496
7627
|
relativeTime,
|
|
7497
7628
|
removeTesters,
|
|
7498
7629
|
removeUser,
|