@gpc-cli/core 0.9.42 → 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/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(packageName, edit.id, filePath, uploadOpts, options.deviceTierConfigId);
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,
@@ -773,7 +776,9 @@ ${validation.errors.join("\n")}`,
773
776
  ...options.releaseName && { name: options.releaseName }
774
777
  };
775
778
  await client.tracks.update(packageName, edit.id, options.track, release);
776
- await client.edits.validate(packageName, edit.id);
779
+ if (!options.commitOptions?.changesNotSentForReview) {
780
+ await client.edits.validate(packageName, edit.id);
781
+ }
777
782
  await client.edits.commit(packageName, edit.id, options.commitOptions);
778
783
  return {
779
784
  versionCode: bundle.versionCode,
@@ -839,7 +844,9 @@ async function promoteRelease(client, packageName, fromTrack, toTrack, options)
839
844
  releaseNotes: options?.releaseNotes || currentRelease.releaseNotes || []
840
845
  };
841
846
  await client.tracks.update(packageName, edit.id, toTrack, release);
842
- await client.edits.validate(packageName, edit.id);
847
+ if (!options?.commitOptions?.changesNotSentForReview) {
848
+ await client.edits.validate(packageName, edit.id);
849
+ }
843
850
  await client.edits.commit(packageName, edit.id, options?.commitOptions);
844
851
  return {
845
852
  track: toTrack,
@@ -906,7 +913,9 @@ async function updateRollout(client, packageName, track, action, userFraction, c
906
913
  releaseNotes: currentRelease.releaseNotes || []
907
914
  };
908
915
  await client.tracks.update(packageName, edit.id, track, release);
909
- await client.edits.validate(packageName, edit.id);
916
+ if (!commitOptions?.changesNotSentForReview) {
917
+ await client.edits.validate(packageName, edit.id);
918
+ }
910
919
  await client.edits.commit(packageName, edit.id, commitOptions);
911
920
  return {
912
921
  track,
@@ -944,7 +953,9 @@ async function createTrack(client, packageName, trackName, commitOptions) {
944
953
  const edit = await client.edits.insert(packageName);
945
954
  try {
946
955
  const track = await client.tracks.create(packageName, edit.id, trackName);
947
- await client.edits.validate(packageName, edit.id);
956
+ if (!commitOptions?.changesNotSentForReview) {
957
+ await client.edits.validate(packageName, edit.id);
958
+ }
948
959
  await client.edits.commit(packageName, edit.id, commitOptions);
949
960
  return track;
950
961
  } catch (error) {
@@ -978,7 +989,9 @@ async function updateTrackConfig(client, packageName, trackName, config, commitO
978
989
  release.name = config["name"];
979
990
  }
980
991
  const track = await client.tracks.update(packageName, edit.id, trackName, release);
981
- await client.edits.validate(packageName, edit.id);
992
+ if (!commitOptions?.changesNotSentForReview) {
993
+ await client.edits.validate(packageName, edit.id);
994
+ }
982
995
  await client.edits.commit(packageName, edit.id, commitOptions);
983
996
  return track;
984
997
  } catch (error) {
@@ -1052,7 +1065,9 @@ async function uploadExternallyHosted(client, packageName, data, commitOptions)
1052
1065
  const edit = await client.edits.insert(packageName);
1053
1066
  try {
1054
1067
  const result = await client.apks.addExternallyHosted(packageName, edit.id, data);
1055
- await client.edits.validate(packageName, edit.id);
1068
+ if (!commitOptions?.changesNotSentForReview) {
1069
+ await client.edits.validate(packageName, edit.id);
1070
+ }
1056
1071
  await client.edits.commit(packageName, edit.id, commitOptions);
1057
1072
  return result;
1058
1073
  } catch (error) {
@@ -1403,7 +1418,9 @@ async function updateListing(client, packageName, language, data, commitOptions)
1403
1418
  const edit = await client.edits.insert(packageName);
1404
1419
  try {
1405
1420
  const listing = await client.listings.patch(packageName, edit.id, language, data);
1406
- await client.edits.validate(packageName, edit.id);
1421
+ if (!commitOptions?.changesNotSentForReview) {
1422
+ await client.edits.validate(packageName, edit.id);
1423
+ }
1407
1424
  await client.edits.commit(packageName, edit.id, commitOptions);
1408
1425
  return listing;
1409
1426
  } catch (error) {
@@ -1417,7 +1434,9 @@ async function deleteListing(client, packageName, language, commitOptions) {
1417
1434
  const edit = await client.edits.insert(packageName);
1418
1435
  try {
1419
1436
  await client.listings.delete(packageName, edit.id, language);
1420
- await client.edits.validate(packageName, edit.id);
1437
+ if (!commitOptions?.changesNotSentForReview) {
1438
+ await client.edits.validate(packageName, edit.id);
1439
+ }
1421
1440
  await client.edits.commit(packageName, edit.id, commitOptions);
1422
1441
  } catch (error) {
1423
1442
  await client.edits.delete(packageName, edit.id).catch(() => {
@@ -1541,7 +1560,9 @@ ${details}`,
1541
1560
  const { language, ...data } = listing;
1542
1561
  await client.listings.update(packageName, edit.id, language, data);
1543
1562
  }
1544
- await client.edits.validate(packageName, edit.id);
1563
+ if (!options?.commitOptions?.changesNotSentForReview) {
1564
+ await client.edits.validate(packageName, edit.id);
1565
+ }
1545
1566
  await client.edits.commit(packageName, edit.id, options?.commitOptions);
1546
1567
  return {
1547
1568
  updated: localListings.length,
@@ -1583,7 +1604,9 @@ async function uploadImage(client, packageName, language, imageType, filePath, c
1583
1604
  const edit = await client.edits.insert(packageName);
1584
1605
  try {
1585
1606
  const image = await client.images.upload(packageName, edit.id, language, imageType, filePath);
1586
- await client.edits.validate(packageName, edit.id);
1607
+ if (!commitOptions?.changesNotSentForReview) {
1608
+ await client.edits.validate(packageName, edit.id);
1609
+ }
1587
1610
  await client.edits.commit(packageName, edit.id, commitOptions);
1588
1611
  return image;
1589
1612
  } catch (error) {
@@ -1597,7 +1620,9 @@ async function deleteImage(client, packageName, language, imageType, imageId, co
1597
1620
  const edit = await client.edits.insert(packageName);
1598
1621
  try {
1599
1622
  await client.images.delete(packageName, edit.id, language, imageType, imageId);
1600
- await client.edits.validate(packageName, edit.id);
1623
+ if (!commitOptions?.changesNotSentForReview) {
1624
+ await client.edits.validate(packageName, edit.id);
1625
+ }
1601
1626
  await client.edits.commit(packageName, edit.id, commitOptions);
1602
1627
  } catch (error) {
1603
1628
  await client.edits.delete(packageName, edit.id).catch(() => {
@@ -1711,7 +1736,9 @@ async function updateAppDetails(client, packageName, details, commitOptions) {
1711
1736
  const edit = await client.edits.insert(packageName);
1712
1737
  try {
1713
1738
  const result = await client.details.patch(packageName, edit.id, details);
1714
- await client.edits.validate(packageName, edit.id);
1739
+ if (!commitOptions?.changesNotSentForReview) {
1740
+ await client.edits.validate(packageName, edit.id);
1741
+ }
1715
1742
  await client.edits.commit(packageName, edit.id, commitOptions);
1716
1743
  return result;
1717
1744
  } catch (error) {
@@ -2428,16 +2455,20 @@ function analyzeReviews(reviews) {
2428
2455
  async function listReviews(client, packageName, options) {
2429
2456
  let reviews;
2430
2457
  if (options?.all) {
2431
- const { items } = await paginateAll(async (pageToken) => {
2432
- const apiOptions = { token: pageToken };
2433
- if (options?.translationLanguage) apiOptions.translationLanguage = options.translationLanguage;
2434
- if (options?.maxResults) apiOptions.maxResults = options.maxResults;
2435
- const response = await client.reviews.list(packageName, apiOptions);
2436
- return {
2437
- items: response.reviews || [],
2438
- nextPageToken: response.tokenPagination?.nextPageToken
2439
- };
2440
- }, { limit: options?.limit });
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
+ );
2441
2472
  reviews = items;
2442
2473
  } else {
2443
2474
  const apiOptions = {};
@@ -3312,10 +3343,6 @@ async function revokeSubscriptionPurchase(client, packageName, token) {
3312
3343
  validatePackageName(packageName);
3313
3344
  return client.purchases.revokeSubscriptionV2(packageName, token);
3314
3345
  }
3315
- async function refundSubscriptionV2(client, packageName, token) {
3316
- validatePackageName(packageName);
3317
- return client.purchases.refundSubscriptionV2(packageName, token);
3318
- }
3319
3346
  async function listVoidedPurchases(client, packageName, options) {
3320
3347
  validatePackageName(packageName);
3321
3348
  if (options?.limit || options?.nextPage) {
@@ -3351,10 +3378,20 @@ async function getOrderDetails(client, packageName, orderId) {
3351
3378
  async function batchGetOrders(client, packageName, orderIds) {
3352
3379
  validatePackageName(packageName);
3353
3380
  if (orderIds.length === 0) {
3354
- throw new GpcError("No order IDs provided", "ORDERS_BATCH_EMPTY", 2, "Pass at least one order ID with --ids");
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
+ );
3355
3387
  }
3356
3388
  if (orderIds.length > 1e3) {
3357
- throw new GpcError(`Too many order IDs (${orderIds.length}). Maximum is 1000.`, "ORDERS_BATCH_LIMIT", 2, "Split into multiple requests of 1000 or fewer");
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
+ );
3358
3395
  }
3359
3396
  return client.orders.batchGet(packageName, orderIds);
3360
3397
  }
@@ -3480,8 +3517,22 @@ async function listUsers(client, developerId, options) {
3480
3517
  const response = await client.list(developerId, options);
3481
3518
  return { users: response.users || [], nextPageToken: response.nextPageToken };
3482
3519
  }
3483
- async function getUser(client, developerId, userId) {
3484
- return client.get(developerId, userId);
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
+ );
3485
3536
  }
3486
3537
  async function inviteUser(client, developerId, email, permissions, grants) {
3487
3538
  const user = { email };
@@ -3569,7 +3620,9 @@ async function addTesters(client, packageName, track, groupEmails, commitOptions
3569
3620
  const updated = await client.testers.update(packageName, edit.id, track, {
3570
3621
  googleGroups: [...existing]
3571
3622
  });
3572
- await client.edits.validate(packageName, edit.id);
3623
+ if (!commitOptions?.changesNotSentForReview) {
3624
+ await client.edits.validate(packageName, edit.id);
3625
+ }
3573
3626
  await client.edits.commit(packageName, edit.id, commitOptions);
3574
3627
  return updated;
3575
3628
  } catch (error) {
@@ -3587,7 +3640,9 @@ async function removeTesters(client, packageName, track, groupEmails, commitOpti
3587
3640
  const updated = await client.testers.update(packageName, edit.id, track, {
3588
3641
  googleGroups: filtered
3589
3642
  });
3590
- await client.edits.validate(packageName, edit.id);
3643
+ if (!commitOptions?.changesNotSentForReview) {
3644
+ await client.edits.validate(packageName, edit.id);
3645
+ }
3591
3646
  await client.edits.commit(packageName, edit.id, commitOptions);
3592
3647
  return updated;
3593
3648
  } catch (error) {
@@ -3596,7 +3651,7 @@ async function removeTesters(client, packageName, track, groupEmails, commitOpti
3596
3651
  throw error;
3597
3652
  }
3598
3653
  }
3599
- async function importTestersFromCsv(client, packageName, track, csvPath) {
3654
+ async function importTestersFromCsv(client, packageName, track, csvPath, commitOptions) {
3600
3655
  const content = await readFile5(csvPath, "utf-8");
3601
3656
  const emails = content.split(/[,\n\r]+/).map((e) => e.trim()).filter((e) => e.length > 0 && e.includes("@"));
3602
3657
  if (emails.length === 0) {
@@ -3607,7 +3662,7 @@ async function importTestersFromCsv(client, packageName, track, csvPath) {
3607
3662
  "The CSV file must contain email addresses separated by commas or newlines. Each email must contain an @ symbol."
3608
3663
  );
3609
3664
  }
3610
- const testers = await addTesters(client, packageName, track, emails);
3665
+ const testers = await addTesters(client, packageName, track, emails, commitOptions);
3611
3666
  return { added: emails.length, testers };
3612
3667
  }
3613
3668
 
@@ -3871,7 +3926,7 @@ var OTP_ID_FIELDS = /* @__PURE__ */ new Set(["productId", "packageName"]);
3871
3926
  function deriveOtpUpdateMask(data) {
3872
3927
  return Object.keys(data).filter((k) => !OTP_ID_FIELDS.has(k)).join(",");
3873
3928
  }
3874
- var OTP_OFFER_ID_FIELDS = /* @__PURE__ */ new Set(["productId", "offerId"]);
3929
+ var OTP_OFFER_ID_FIELDS = /* @__PURE__ */ new Set(["packageName", "productId", "purchaseOptionId", "offerId"]);
3875
3930
  function deriveOtpOfferUpdateMask(data) {
3876
3931
  return Object.keys(data).filter((k) => !OTP_OFFER_ID_FIELDS.has(k)).join(",");
3877
3932
  }
@@ -3900,9 +3955,9 @@ async function deleteOneTimeProduct(client, packageName, productId) {
3900
3955
  );
3901
3956
  }
3902
3957
  }
3903
- async function listOneTimeOffers(client, packageName, productId) {
3958
+ async function listOneTimeOffers(client, packageName, productId, purchaseOptionId = "-") {
3904
3959
  try {
3905
- return await client.oneTimeProducts.listOffers(packageName, productId);
3960
+ return await client.oneTimeProducts.listOffers(packageName, productId, purchaseOptionId);
3906
3961
  } catch (error) {
3907
3962
  throw new GpcError(
3908
3963
  `Failed to list offers for product "${productId}": ${error instanceof Error ? error.message : String(error)}`,
@@ -3912,9 +3967,9 @@ async function listOneTimeOffers(client, packageName, productId) {
3912
3967
  );
3913
3968
  }
3914
3969
  }
3915
- async function getOneTimeOffer(client, packageName, productId, offerId) {
3970
+ async function getOneTimeOffer(client, packageName, productId, offerId, purchaseOptionId = "-") {
3916
3971
  try {
3917
- return await client.oneTimeProducts.getOffer(packageName, productId, offerId);
3972
+ return await client.oneTimeProducts.getOffer(packageName, productId, purchaseOptionId, offerId);
3918
3973
  } catch (error) {
3919
3974
  throw new GpcError(
3920
3975
  `Failed to get offer "${offerId}" for product "${productId}": ${error instanceof Error ? error.message : String(error)}`,
@@ -3924,9 +3979,9 @@ async function getOneTimeOffer(client, packageName, productId, offerId) {
3924
3979
  );
3925
3980
  }
3926
3981
  }
3927
- async function createOneTimeOffer(client, packageName, productId, data) {
3982
+ async function createOneTimeOffer(client, packageName, productId, data, purchaseOptionId = "-") {
3928
3983
  try {
3929
- return await client.oneTimeProducts.createOffer(packageName, productId, data);
3984
+ return await client.oneTimeProducts.createOffer(packageName, productId, purchaseOptionId, data);
3930
3985
  } catch (error) {
3931
3986
  throw new GpcError(
3932
3987
  `Failed to create offer for product "${productId}": ${error instanceof Error ? error.message : String(error)}`,
@@ -3936,10 +3991,17 @@ async function createOneTimeOffer(client, packageName, productId, data) {
3936
3991
  );
3937
3992
  }
3938
3993
  }
3939
- async function updateOneTimeOffer(client, packageName, productId, offerId, data, updateMask) {
3994
+ async function updateOneTimeOffer(client, packageName, productId, offerId, data, updateMask, purchaseOptionId = "-") {
3940
3995
  try {
3941
3996
  const mask = updateMask || deriveOtpOfferUpdateMask(data);
3942
- return await client.oneTimeProducts.updateOffer(packageName, productId, offerId, data, mask);
3997
+ return await client.oneTimeProducts.updateOffer(
3998
+ packageName,
3999
+ productId,
4000
+ purchaseOptionId,
4001
+ offerId,
4002
+ data,
4003
+ mask
4004
+ );
3943
4005
  } catch (error) {
3944
4006
  throw new GpcError(
3945
4007
  `Failed to update offer "${offerId}" for product "${productId}": ${error instanceof Error ? error.message : String(error)}`,
@@ -3964,9 +4026,9 @@ async function diffOneTimeProduct(client, packageName, productId, localData) {
3964
4026
  }
3965
4027
  return diffs;
3966
4028
  }
3967
- async function deleteOneTimeOffer(client, packageName, productId, offerId) {
4029
+ async function deleteOneTimeOffer(client, packageName, productId, offerId, purchaseOptionId = "-") {
3968
4030
  try {
3969
- await client.oneTimeProducts.deleteOffer(packageName, productId, offerId);
4031
+ await client.oneTimeProducts.deleteOffer(packageName, productId, purchaseOptionId, offerId);
3970
4032
  } catch (error) {
3971
4033
  throw new GpcError(
3972
4034
  `Failed to delete offer "${offerId}" for product "${productId}": ${error instanceof Error ? error.message : String(error)}`,
@@ -4197,7 +4259,13 @@ async function advanceTrain(apiClient, reportingClient, packageName) {
4197
4259
  }
4198
4260
  async function executeStage(apiClient, packageName, state, stageIndex) {
4199
4261
  const stage = state.stages[stageIndex];
4200
- if (!stage) throw new GpcError(`Stage ${stageIndex} not found`, "TRAIN_STAGE_NOT_FOUND", 1, "Check your release train configuration.");
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
+ );
4201
4269
  const rolloutFraction = stage.rollout / 100;
4202
4270
  await updateRollout(apiClient, packageName, stage.track, "increase", rolloutFraction);
4203
4271
  stage.executedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -4650,7 +4718,9 @@ var RESOURCE_IDS = {
4650
4718
  16842754: "icon",
4651
4719
  16842755: "name",
4652
4720
  16842767: "versionCode",
4721
+ 16843291: "versionCode",
4653
4722
  16842768: "versionName",
4723
+ 16843292: "versionName",
4654
4724
  16843276: "minSdkVersion",
4655
4725
  16843376: "targetSdkVersion",
4656
4726
  16842782: "debuggable",
@@ -4666,27 +4736,39 @@ var RESOURCE_IDS = {
4666
4736
  function buildXmlSchema() {
4667
4737
  const root = new protobuf.Root();
4668
4738
  const ns = root.define("aapt.pb");
4669
- const Source = new protobuf.Type("Source").add(new protobuf.Field("pathIdx", 1, "uint32")).add(new protobuf.Field("position", 2, "Position"));
4670
- const Position = new protobuf.Type("Position").add(new protobuf.Field("lineNumber", 1, "uint32")).add(new protobuf.Field("columnNumber", 2, "uint32"));
4739
+ const NullType = new protobuf.Type("NullType");
4740
+ const EmptyType = new protobuf.Type("EmptyType");
4671
4741
  const Primitive = new protobuf.Type("Primitive").add(
4672
- 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("floatValue", 11, "float"))
4673
- );
4674
- const Reference = new protobuf.Type("Reference").add(new protobuf.Field("id", 1, "uint32")).add(new protobuf.Field("name", 2, "string"));
4675
- const Item = new protobuf.Type("Item").add(
4676
- 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"))
4677
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"));
4678
4745
  const StringMsg = new protobuf.Type("String").add(new protobuf.Field("value", 1, "string"));
4679
- 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("source", 4, "Source")).add(new protobuf.Field("resourceId", 5, "uint32")).add(new protobuf.Field("compiledItem", 6, "Item"));
4680
- const XmlNamespace = new protobuf.Type("XmlNamespace").add(new protobuf.Field("prefix", 1, "string")).add(new protobuf.Field("uri", 2, "string")).add(new protobuf.Field("source", 3, "Source"));
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"));
4681
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"));
4682
4758
  const XmlNode = new protobuf.Type("XmlNode").add(
4683
4759
  new protobuf.OneOf("node").add(new protobuf.Field("element", 1, "XmlElement")).add(new protobuf.Field("text", 2, "string"))
4684
- ).add(new protobuf.Field("source", 3, "Source"));
4685
- ns.add(Position);
4686
- ns.add(Source);
4760
+ );
4761
+ ns.add(NullType);
4762
+ ns.add(EmptyType);
4687
4763
  ns.add(Primitive);
4764
+ ns.add(BooleanMsg);
4688
4765
  ns.add(Reference);
4689
4766
  ns.add(StringMsg);
4767
+ ns.add(RawString);
4768
+ ns.add(StyledString);
4769
+ ns.add(Span);
4770
+ ns.add(FileReference);
4771
+ ns.add(Id);
4690
4772
  ns.add(Item);
4691
4773
  ns.add(XmlAttribute);
4692
4774
  ns.add(XmlNamespace);
@@ -4708,29 +4790,26 @@ function decodeManifest(buf) {
4708
4790
  }
4709
4791
  return extractManifestData(decoded.element);
4710
4792
  }
4711
- function getAttrValue(attrs, resId) {
4712
- const attr = attrs.find((a) => a.resourceId === resId);
4713
- if (!attr) return void 0;
4714
- const ci = attr.compiledItem;
4715
- if (ci?.str?.value !== void 0) return ci.str.value;
4716
- if (ci?.ref?.name !== void 0) return ci.ref.name;
4717
- return attr.value || void 0;
4718
- }
4719
4793
  function getAttrByName(attrs, name) {
4720
4794
  const attr = attrs.find((a) => a.name === name || RESOURCE_IDS[a.resourceId] === name);
4721
4795
  if (!attr) return void 0;
4722
4796
  const ci = attr.compiledItem;
4723
4797
  if (ci?.str?.value !== void 0) return ci.str.value;
4724
4798
  if (ci?.ref?.name !== void 0) return ci.ref.name;
4725
- return attr.value || void 0;
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;
4726
4805
  }
4727
- function getBoolAttr(attrs, resId, defaultVal) {
4728
- const val = getAttrValue(attrs, resId);
4806
+ function getBoolByName(attrs, name, defaultVal) {
4807
+ const val = getAttrByName(attrs, name);
4729
4808
  if (val === void 0) return defaultVal;
4730
4809
  return val === "true" || val === "1";
4731
4810
  }
4732
- function getIntAttr(attrs, resId, defaultVal) {
4733
- const val = getAttrValue(attrs, resId);
4811
+ function getIntByName(attrs, name, defaultVal) {
4812
+ const val = getAttrByName(attrs, name);
4734
4813
  if (val === void 0) return defaultVal;
4735
4814
  const n = parseInt(val, 10);
4736
4815
  return isNaN(n) ? defaultVal : n;
@@ -4741,23 +4820,25 @@ function getChildren(elem, tagName) {
4741
4820
  function extractManifestData(manifest) {
4742
4821
  const attrs = manifest.attribute || [];
4743
4822
  const packageName = getAttrByName(attrs, "package") || "";
4744
- const versionCode = getIntAttr(attrs, 16842767, 0);
4745
- const versionName = getAttrValue(attrs, 16842768) || "";
4823
+ const versionCodeStr = getAttrByName(attrs, "versionCode");
4824
+ const versionCode = versionCodeStr ? parseInt(versionCodeStr, 10) || 0 : 0;
4825
+ const versionName = getAttrByName(attrs, "versionName") || "";
4746
4826
  const usesSdkElements = getChildren(manifest, "uses-sdk");
4747
4827
  const usesSdk = usesSdkElements[0];
4748
- const minSdk = usesSdk ? getIntAttr(usesSdk.attribute || [], 16843276, 1) : 1;
4749
- const targetSdk = usesSdk ? getIntAttr(usesSdk.attribute || [], 16843376, minSdk) : minSdk;
4750
- const permissions = getChildren(manifest, "uses-permission").map((el) => getAttrValue(el.attribute || [], 16842755)).filter((p) => p !== void 0);
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);
4751
4831
  const features = getChildren(manifest, "uses-feature").map((el) => ({
4752
- name: getAttrValue(el.attribute || [], 16842755) || "",
4753
- required: getBoolAttr(el.attribute || [], 16843402, true)
4832
+ name: getAttrByName(el.attribute || [], "name") || "",
4833
+ required: getBoolByName(el.attribute || [], "required", true)
4754
4834
  }));
4755
4835
  const appElements = getChildren(manifest, "application");
4756
4836
  const app = appElements[0];
4757
- const debuggable = app ? getBoolAttr(app.attribute || [], 16842782, false) : false;
4758
- const testOnly = getBoolAttr(attrs, 16843378, false);
4759
- const usesCleartextTraffic = app ? getBoolAttr(app.attribute || [], 16843760, true) : true;
4760
- const extractNativeLibs = app ? getBoolAttr(app.attribute || [], 16844010, true) : true;
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;
4761
4842
  const activities = app ? extractComponents(app, "activity") : [];
4762
4843
  const services = app ? extractComponents(app, "service") : [];
4763
4844
  const receivers = app ? extractComponents(app, "receiver") : [];
@@ -4772,6 +4853,7 @@ function extractManifestData(manifest) {
4772
4853
  testOnly,
4773
4854
  usesCleartextTraffic,
4774
4855
  extractNativeLibs,
4856
+ pageSizeCompat: pageSizeCompat || void 0,
4775
4857
  permissions,
4776
4858
  features,
4777
4859
  activities,
@@ -4783,13 +4865,29 @@ function extractManifestData(manifest) {
4783
4865
  function extractComponents(appElement, tagName) {
4784
4866
  return getChildren(appElement, tagName).map((el) => {
4785
4867
  const compAttrs = el.attribute || [];
4786
- const exportedVal = getAttrValue(compAttrs, 16842795);
4787
- const hasIntentFilter = getChildren(el, "intent-filter").length > 0;
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
+ }
4788
4883
  return {
4789
- name: getAttrValue(compAttrs, 16842755) || "",
4884
+ name: getAttrByName(compAttrs, "name") || "",
4790
4885
  exported: exportedVal === void 0 ? void 0 : exportedVal === "true" || exportedVal === "1",
4791
- foregroundServiceType: tagName === "service" ? getAttrValue(compAttrs, 16843985) : void 0,
4792
- hasIntentFilter
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
4793
4891
  };
4794
4892
  });
4795
4893
  }
@@ -4797,12 +4895,14 @@ function extractComponents(appElement, tagName) {
4797
4895
  // src/preflight/aab-reader.ts
4798
4896
  var AAB_MANIFEST_PATH = "base/manifest/AndroidManifest.xml";
4799
4897
  var APK_MANIFEST_PATH = "AndroidManifest.xml";
4898
+ var SO_HEADER_BYTES = 256;
4899
+ var SO_PATH_RE = /\/lib\/[^/]+\/[^/]+\.so$/;
4800
4900
  function detectManifestPath(filePath) {
4801
4901
  return filePath.toLowerCase().endsWith(".apk") ? APK_MANIFEST_PATH : AAB_MANIFEST_PATH;
4802
4902
  }
4803
4903
  async function readAab(aabPath) {
4804
4904
  const manifestPath = detectManifestPath(aabPath);
4805
- const { zipfile, entries, manifestBuf } = await openAndScan(aabPath, manifestPath);
4905
+ const { zipfile, entries, manifestBuf, soHeaders } = await openAndScan(aabPath, manifestPath);
4806
4906
  zipfile.close();
4807
4907
  if (!manifestBuf) {
4808
4908
  const fileType = aabPath.toLowerCase().endsWith(".apk") ? "APK" : "AAB";
@@ -4818,7 +4918,7 @@ async function readAab(aabPath) {
4818
4918
  manifest = createFallbackManifest();
4819
4919
  manifest._parseError = `Manifest could not be fully parsed: ${errMsg}. Manifest-dependent checks will be skipped.`;
4820
4920
  }
4821
- return { manifest, entries };
4921
+ return { manifest, entries, nativeLibHeaders: soHeaders };
4822
4922
  }
4823
4923
  function createFallbackManifest() {
4824
4924
  return {
@@ -4847,8 +4947,11 @@ function openAndScan(aabPath, manifestPath = AAB_MANIFEST_PATH) {
4847
4947
  return;
4848
4948
  }
4849
4949
  const entries = [];
4950
+ const soHeaders = /* @__PURE__ */ new Map();
4850
4951
  let manifestBuf = null;
4851
4952
  let rejected = false;
4953
+ let pendingStreams = 0;
4954
+ let entrysDone = false;
4852
4955
  function fail(error) {
4853
4956
  if (!rejected) {
4854
4957
  rejected = true;
@@ -4856,6 +4959,46 @@ function openAndScan(aabPath, manifestPath = AAB_MANIFEST_PATH) {
4856
4959
  reject(error);
4857
4960
  }
4858
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
+ }
4859
5002
  zipfile.on("error", fail);
4860
5003
  zipfile.on("entry", (entry) => {
4861
5004
  if (rejected) return;
@@ -4868,27 +5011,23 @@ function openAndScan(aabPath, manifestPath = AAB_MANIFEST_PATH) {
4868
5011
  });
4869
5012
  }
4870
5013
  if (path === manifestPath) {
4871
- zipfile.openReadStream(entry, (streamErr, stream) => {
4872
- if (streamErr || !stream) {
4873
- fail(streamErr ?? new Error("Failed to read manifest entry"));
4874
- return;
4875
- }
4876
- const chunks = [];
4877
- stream.on("data", (chunk) => chunks.push(chunk));
4878
- stream.on("error", (e) => fail(e));
4879
- stream.on("end", () => {
4880
- manifestBuf = Buffer.concat(chunks);
4881
- zipfile.readEntry();
4882
- });
5014
+ readEntryStream(entry, (buf) => {
5015
+ manifestBuf = buf;
4883
5016
  });
4884
- } else {
4885
- zipfile.readEntry();
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
+ );
4886
5025
  }
5026
+ zipfile.readEntry();
4887
5027
  });
4888
5028
  zipfile.on("end", () => {
4889
- if (!rejected) {
4890
- resolve2({ zipfile, entries, manifestBuf });
4891
- }
5029
+ entrysDone = true;
5030
+ maybeResolve();
4892
5031
  });
4893
5032
  zipfile.readEntry();
4894
5033
  });
@@ -4957,13 +5096,13 @@ var manifestScanner = {
4957
5096
  });
4958
5097
  }
4959
5098
  if (manifest.targetSdk >= 31) {
4960
- const allComponents = [
5099
+ const allComponents2 = [
4961
5100
  ...manifest.activities,
4962
5101
  ...manifest.services,
4963
5102
  ...manifest.receivers,
4964
5103
  ...manifest.providers
4965
5104
  ];
4966
- for (const comp of allComponents) {
5105
+ for (const comp of allComponents2) {
4967
5106
  if (comp.hasIntentFilter && comp.exported === void 0) {
4968
5107
  findings.push({
4969
5108
  scanner: "manifest",
@@ -4995,6 +5134,27 @@ var manifestScanner = {
4995
5134
  }
4996
5135
  }
4997
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
+ }
4998
5158
  if (manifest.minSdk < 21) {
4999
5159
  findings.push({
5000
5160
  scanner: "manifest",
@@ -5225,9 +5385,53 @@ var permissionsScanner = {
5225
5385
  // src/preflight/scanners/native-libs-scanner.ts
5226
5386
  var KNOWN_ABIS = ["arm64-v8a", "armeabi-v7a", "x86", "x86_64"];
5227
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
+ }
5228
5432
  var nativeLibsScanner = {
5229
5433
  name: "native-libs",
5230
- description: "Checks native library architectures for 64-bit compliance",
5434
+ description: "Checks native library architectures, 64-bit compliance, and 16KB alignment",
5231
5435
  requires: ["zipEntries"],
5232
5436
  async scan(ctx) {
5233
5437
  const entries = ctx.zipEntries;
@@ -5270,6 +5474,7 @@ var nativeLibsScanner = {
5270
5474
  policyUrl: "https://developer.android.com/google/play/requirements/64-bit"
5271
5475
  });
5272
5476
  }
5477
+ check16KBAlignment(ctx.nativeLibHeaders, ctx.manifest, findings);
5273
5478
  const detectedAbis = KNOWN_ABIS.filter((abi) => abisFound.has(abi));
5274
5479
  const unknownAbis = [...abisFound].filter(
5275
5480
  (abi) => !KNOWN_ABIS.includes(abi)
@@ -5296,6 +5501,33 @@ var nativeLibsScanner = {
5296
5501
  return findings;
5297
5502
  }
5298
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
+ }
5299
5531
 
5300
5532
  // src/preflight/scanners/metadata-scanner.ts
5301
5533
  import { readdir as readdir5, stat as stat8, readFile as readFile10 } from "fs/promises";
@@ -5993,6 +6225,7 @@ async function runPreflight(options) {
5993
6225
  const aab = await readAab(options.aabPath);
5994
6226
  ctx.manifest = aab.manifest;
5995
6227
  ctx.zipEntries = aab.entries;
6228
+ ctx.nativeLibHeaders = aab.nativeLibHeaders;
5996
6229
  if (aab.manifest._parseError) {
5997
6230
  earlyFindings.push({
5998
6231
  scanner: "manifest-parser",
@@ -6403,68 +6636,6 @@ async function downloadGeneratedApk(client, packageName, versionCode, apkId, out
6403
6636
  return { path: outputPath, sizeBytes: bytes.byteLength };
6404
6637
  }
6405
6638
 
6406
- // src/commands/purchase-options.ts
6407
- async function listPurchaseOptions(client, packageName) {
6408
- try {
6409
- return await client.purchaseOptions.list(packageName);
6410
- } catch (error) {
6411
- throw new GpcError(
6412
- `Failed to list purchase options: ${error instanceof Error ? error.message : String(error)}`,
6413
- "PURCHASE_OPTIONS_LIST_FAILED",
6414
- 4,
6415
- "Check your package name and API permissions."
6416
- );
6417
- }
6418
- }
6419
- async function getPurchaseOption(client, packageName, purchaseOptionId) {
6420
- try {
6421
- return await client.purchaseOptions.get(packageName, purchaseOptionId);
6422
- } catch (error) {
6423
- throw new GpcError(
6424
- `Failed to get purchase option "${purchaseOptionId}": ${error instanceof Error ? error.message : String(error)}`,
6425
- "PURCHASE_OPTION_GET_FAILED",
6426
- 4,
6427
- "Check that the purchase option ID exists."
6428
- );
6429
- }
6430
- }
6431
- async function createPurchaseOption(client, packageName, data) {
6432
- try {
6433
- return await client.purchaseOptions.create(packageName, data);
6434
- } catch (error) {
6435
- throw new GpcError(
6436
- `Failed to create purchase option: ${error instanceof Error ? error.message : String(error)}`,
6437
- "PURCHASE_OPTION_CREATE_FAILED",
6438
- 4,
6439
- "Check your purchase option data and API permissions."
6440
- );
6441
- }
6442
- }
6443
- async function activatePurchaseOption(client, packageName, purchaseOptionId) {
6444
- try {
6445
- return await client.purchaseOptions.activate(packageName, purchaseOptionId);
6446
- } catch (error) {
6447
- throw new GpcError(
6448
- `Failed to activate purchase option "${purchaseOptionId}": ${error instanceof Error ? error.message : String(error)}`,
6449
- "PURCHASE_OPTION_ACTIVATE_FAILED",
6450
- 4,
6451
- "Check that the purchase option exists and is in a valid state for activation."
6452
- );
6453
- }
6454
- }
6455
- async function deactivatePurchaseOption(client, packageName, purchaseOptionId) {
6456
- try {
6457
- return await client.purchaseOptions.deactivate(packageName, purchaseOptionId);
6458
- } catch (error) {
6459
- throw new GpcError(
6460
- `Failed to deactivate purchase option "${purchaseOptionId}": ${error instanceof Error ? error.message : String(error)}`,
6461
- "PURCHASE_OPTION_DEACTIVATE_FAILED",
6462
- 4,
6463
- "Check that the purchase option exists and is in a valid state for deactivation."
6464
- );
6465
- }
6466
- }
6467
-
6468
6639
  // src/commands/bundle-analysis.ts
6469
6640
  import { readFile as readFile14, stat as stat10 } from "fs/promises";
6470
6641
  var EOCD_SIGNATURE = 101010256;
@@ -7149,21 +7320,15 @@ async function fetchChangelog(options) {
7149
7320
  signal: controller.signal
7150
7321
  });
7151
7322
  } catch {
7152
- throw new Error(
7153
- `Could not fetch changelog. View online: ${DOCS_CHANGELOG_URL}`
7154
- );
7323
+ throw new Error(`Could not fetch changelog. View online: ${DOCS_CHANGELOG_URL}`);
7155
7324
  } finally {
7156
7325
  clearTimeout(timer);
7157
7326
  }
7158
7327
  if (!response.ok) {
7159
7328
  if (response.status === 404 && options?.version) {
7160
- throw new Error(
7161
- `Version ${options.version} not found. Run: gpc changelog --limit 10`
7162
- );
7329
+ throw new Error(`Version ${options.version} not found. Run: gpc changelog --limit 10`);
7163
7330
  }
7164
- throw new Error(
7165
- `GitHub API returned ${response.status}. View online: ${DOCS_CHANGELOG_URL}`
7166
- );
7331
+ throw new Error(`GitHub API returned ${response.status}. View online: ${DOCS_CHANGELOG_URL}`);
7167
7332
  }
7168
7333
  const data = await response.json();
7169
7334
  const releases = Array.isArray(data) ? data : [data];
@@ -7212,7 +7377,7 @@ async function getRtdnStatus(client, packageName) {
7212
7377
  try {
7213
7378
  const edit = await client.edits.insert(packageName);
7214
7379
  try {
7215
- const details = await client.details.get(packageName, edit.id);
7380
+ await client.details.get(packageName, edit.id);
7216
7381
  return {
7217
7382
  topicName: null,
7218
7383
  enabled: false
@@ -7296,7 +7461,6 @@ export {
7296
7461
  acknowledgeProductPurchase,
7297
7462
  activateBasePlan,
7298
7463
  activateOffer,
7299
- activatePurchaseOption,
7300
7464
  addRecoveryTargeting,
7301
7465
  addTesters,
7302
7466
  advanceTrain,
@@ -7326,14 +7490,12 @@ export {
7326
7490
  createOffer,
7327
7491
  createOneTimeOffer,
7328
7492
  createOneTimeProduct,
7329
- createPurchaseOption,
7330
7493
  createRecoveryAction,
7331
7494
  createSpinner,
7332
7495
  createSubscription,
7333
7496
  createTrack,
7334
7497
  deactivateBasePlan,
7335
7498
  deactivateOffer,
7336
- deactivatePurchaseOption,
7337
7499
  decodeNotification,
7338
7500
  deferSubscriptionPurchase,
7339
7501
  deferSubscriptionV2,
@@ -7391,7 +7553,6 @@ export {
7391
7553
  getOrderDetails,
7392
7554
  getProductPurchase,
7393
7555
  getProductPurchaseV2,
7394
- getPurchaseOption,
7395
7556
  getQuotaUsage,
7396
7557
  getReleasesStatus,
7397
7558
  getReview,
@@ -7436,7 +7597,6 @@ export {
7436
7597
  listOffers,
7437
7598
  listOneTimeOffers,
7438
7599
  listOneTimeProducts,
7439
- listPurchaseOptions,
7440
7600
  listRecoveryActions,
7441
7601
  listReports,
7442
7602
  listReviews,
@@ -7464,7 +7624,6 @@ export {
7464
7624
  redactSensitive,
7465
7625
  refundExternalTransaction,
7466
7626
  refundOrder,
7467
- refundSubscriptionV2,
7468
7627
  relativeTime,
7469
7628
  removeTesters,
7470
7629
  removeUser,