@gpc-cli/core 0.9.29 → 0.9.30

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
@@ -198,7 +198,10 @@ function formatTable(data) {
198
198
  const colCount = keys.length;
199
199
  const maxCellWidth = computeMaxCellWidth(colCount);
200
200
  const widths = keys.map(
201
- (key) => Math.max(key.length, ...rows.map((row) => truncateCell(cellValue(row[key]), maxCellWidth).length))
201
+ (key) => Math.max(
202
+ key.length,
203
+ ...rows.map((row) => truncateCell(cellValue(row[key]), maxCellWidth).length)
204
+ )
202
205
  );
203
206
  const isNumeric = keys.map(
204
207
  (key) => rows.every((row) => {
@@ -807,12 +810,13 @@ async function updateRollout(client, packageName, track, action, userFraction) {
807
810
  let newFraction;
808
811
  switch (action) {
809
812
  case "increase":
810
- if (!userFraction) throw new GpcError(
811
- "--to <percentage> is required for rollout increase",
812
- "ROLLOUT_MISSING_FRACTION",
813
- 2,
814
- "Specify the target rollout percentage with --to, e.g.: gpc rollout increase --to 0.5"
815
- );
813
+ if (!userFraction)
814
+ throw new GpcError(
815
+ "--to <percentage> is required for rollout increase",
816
+ "ROLLOUT_MISSING_FRACTION",
817
+ 2,
818
+ "Specify the target rollout percentage with --to, e.g.: gpc rollout increase --to 0.5"
819
+ );
816
820
  if (userFraction <= 0 || userFraction > 1) {
817
821
  throw new GpcError(
818
822
  "Rollout percentage must be between 0 and 1 (e.g., 0.1 for 10%)",
@@ -1769,9 +1773,7 @@ function generateMigrationPlan(detection) {
1769
1773
  }
1770
1774
  for (const lane of detection.lanes) {
1771
1775
  if (lane.gpcEquivalent) {
1772
- checklist.push(
1773
- `Replace Fastlane lane "${lane.name}" with: ${lane.gpcEquivalent} <your.aab>`
1774
- );
1776
+ checklist.push(`Replace Fastlane lane "${lane.name}" with: ${lane.gpcEquivalent} <your.aab>`);
1775
1777
  }
1776
1778
  if (lane.actions.includes("capture_android_screenshots")) {
1777
1779
  warnings.push(
@@ -1846,7 +1848,9 @@ async function writeMigrationOutput(result, dir) {
1846
1848
  lines.push("| `supply(skip_upload_aab: true)` | `gpc listings push` |");
1847
1849
  lines.push("| `capture_android_screenshots` | No equivalent \u2014 use separate tool |");
1848
1850
  lines.push("");
1849
- lines.push("See the full migration guide: https://yasserstudio.github.io/gpc/migration/from-fastlane");
1851
+ lines.push(
1852
+ "See the full migration guide: https://yasserstudio.github.io/gpc/migration/from-fastlane"
1853
+ );
1850
1854
  lines.push("");
1851
1855
  await writeFile2(migrationPath, lines.join("\n"), "utf-8");
1852
1856
  files.push(migrationPath);
@@ -2250,16 +2254,16 @@ function analyzeSentiment(text) {
2250
2254
  }
2251
2255
  function clusterTopics(texts) {
2252
2256
  const TOPIC_KEYWORDS = {
2253
- "performance": ["slow", "lag", "laggy", "freeze", "fast", "speed", "quick", "smooth"],
2254
- "crashes": ["crash", "crashes", "crash", "crashing", "force close", "stops", "stopped"],
2257
+ performance: ["slow", "lag", "laggy", "freeze", "fast", "speed", "quick", "smooth"],
2258
+ crashes: ["crash", "crashes", "crash", "crashing", "force close", "stops", "stopped"],
2255
2259
  "ui/ux": ["ui", "design", "interface", "layout", "button", "screen", "menu", "navigation"],
2256
- "battery": ["battery", "drain", "power", "charging", "drain"],
2257
- "updates": ["update", "updated", "version", "new version", "after update"],
2258
- "notifications": ["notification", "notifications", "alert", "alerts", "push"],
2260
+ battery: ["battery", "drain", "power", "charging", "drain"],
2261
+ updates: ["update", "updated", "version", "new version", "after update"],
2262
+ notifications: ["notification", "notifications", "alert", "alerts", "push"],
2259
2263
  "login/auth": ["login", "sign in", "logout", "password", "account", "auth"],
2260
2264
  "feature requests": ["please add", "would be nice", "missing", "need", "wish", "want"],
2261
- "bugs": ["bug", "bugs", "issue", "error", "problem", "glitch", "broken"],
2262
- "pricing": ["price", "pricing", "expensive", "cheap", "subscription", "pay", "cost", "free"]
2265
+ bugs: ["bug", "bugs", "issue", "error", "problem", "glitch", "broken"],
2266
+ pricing: ["price", "pricing", "expensive", "cheap", "subscription", "pay", "cost", "free"]
2263
2267
  };
2264
2268
  const clusterMap = /* @__PURE__ */ new Map();
2265
2269
  for (const text of texts) {
@@ -2499,7 +2503,8 @@ function autoFixProrationMode(data) {
2499
2503
  for (const bp of data.basePlans) {
2500
2504
  const mode = bp.autoRenewingBasePlanType?.prorationMode;
2501
2505
  if (mode && !mode.startsWith(PRORATION_MODE_PREFIX)) {
2502
- if (bp.autoRenewingBasePlanType) bp.autoRenewingBasePlanType.prorationMode = `${PRORATION_MODE_PREFIX}${mode}`;
2506
+ if (bp.autoRenewingBasePlanType)
2507
+ bp.autoRenewingBasePlanType.prorationMode = `${PRORATION_MODE_PREFIX}${mode}`;
2503
2508
  }
2504
2509
  if (bp.autoRenewingBasePlanType?.prorationMode) {
2505
2510
  const fullMode = bp.autoRenewingBasePlanType.prorationMode;
@@ -2637,7 +2642,13 @@ async function createOffer(client, packageName, productId, basePlanId, data) {
2637
2642
  validatePackageName(packageName);
2638
2643
  validateSku(productId);
2639
2644
  const sanitized = sanitizeOffer(data);
2640
- return client.subscriptions.createOffer(packageName, productId, basePlanId, sanitized, data.offerId);
2645
+ return client.subscriptions.createOffer(
2646
+ packageName,
2647
+ productId,
2648
+ basePlanId,
2649
+ sanitized,
2650
+ data.offerId
2651
+ );
2641
2652
  }
2642
2653
  var OFFER_ID_FIELDS = /* @__PURE__ */ new Set(["productId", "basePlanId", "offerId"]);
2643
2654
  function deriveOfferUpdateMask(data) {
@@ -2674,7 +2685,9 @@ async function diffSubscription(client, packageName, productId, localData) {
2674
2685
  const diffs = [];
2675
2686
  const fieldsToCompare = ["listings", "basePlans", "taxAndComplianceSettings"];
2676
2687
  for (const field of fieldsToCompare) {
2677
- const localVal = JSON.stringify(localData[field] ?? null);
2688
+ const localVal = JSON.stringify(
2689
+ localData[field] ?? null
2690
+ );
2678
2691
  const remoteVal = JSON.stringify(remote[field] ?? null);
2679
2692
  if (localVal !== remoteVal) {
2680
2693
  diffs.push({ field, local: localVal, remote: remoteVal });
@@ -2720,7 +2733,11 @@ async function getSubscriptionAnalytics(client, packageName) {
2720
2733
  }
2721
2734
  for (const bp of basePlans) {
2722
2735
  try {
2723
- const offersResp = await client.subscriptions.listOffers(packageName, sub.productId, bp.basePlanId);
2736
+ const offersResp = await client.subscriptions.listOffers(
2737
+ packageName,
2738
+ sub.productId,
2739
+ bp.basePlanId
2740
+ );
2724
2741
  const offers = offersResp.subscriptionOffers ?? [];
2725
2742
  subOfferCount += offers.length;
2726
2743
  totalOffers += offers.length;
@@ -2753,7 +2770,12 @@ var METRIC_SET_METRICS = {
2753
2770
  slowStartRateMetricSet: ["slowStartRate", "distinctUsers"],
2754
2771
  slowRenderingRateMetricSet: ["slowRenderingRate", "distinctUsers"],
2755
2772
  excessiveWakeupRateMetricSet: ["excessiveWakeupRate", "distinctUsers"],
2756
- stuckBackgroundWakelockRateMetricSet: ["stuckBackgroundWakelockRate", "distinctUsers"],
2773
+ // API requires the weighted variants — base `stuckBackgroundWakelockRate` is not a valid metric
2774
+ stuckBackgroundWakelockRateMetricSet: [
2775
+ "stuckBackgroundWakelockRate7dUserWeighted",
2776
+ "stuckBackgroundWakelockRate28dUserWeighted",
2777
+ "distinctUsers"
2778
+ ],
2757
2779
  errorCountMetricSet: ["errorReportCount", "distinctUsers"]
2758
2780
  };
2759
2781
  function buildQuery(metricSet, options) {
@@ -2797,9 +2819,7 @@ async function getVitalsOverview(reporting, packageName) {
2797
2819
  ["stuckBackgroundWakelockRateMetricSet", "stuckWakelockRate"]
2798
2820
  ];
2799
2821
  const results = await Promise.allSettled(
2800
- metricSets.map(
2801
- ([metric]) => reporting.queryMetricSet(packageName, metric, buildQuery(metric))
2802
- )
2822
+ metricSets.map(([metric]) => reporting.queryMetricSet(packageName, metric, buildQuery(metric)))
2803
2823
  );
2804
2824
  const overview = {};
2805
2825
  for (let i = 0; i < metricSets.length; i++) {
@@ -3374,9 +3394,15 @@ async function createGrant(client, developerId, email, packageName, permissions)
3374
3394
  });
3375
3395
  }
3376
3396
  async function updateGrant(client, developerId, email, packageName, permissions) {
3377
- return client.grants.patch(developerId, email, packageName, {
3378
- appLevelPermissions: permissions
3379
- }, "appLevelPermissions");
3397
+ return client.grants.patch(
3398
+ developerId,
3399
+ email,
3400
+ packageName,
3401
+ {
3402
+ appLevelPermissions: permissions
3403
+ },
3404
+ "appLevelPermissions"
3405
+ );
3380
3406
  }
3381
3407
  async function deleteGrant(client, developerId, email, packageName) {
3382
3408
  return client.grants.delete(developerId, email, packageName);
@@ -3789,7 +3815,9 @@ async function diffOneTimeProduct(client, packageName, productId, localData) {
3789
3815
  const diffs = [];
3790
3816
  const fieldsToCompare = ["listings", "purchaseType", "taxAndComplianceSettings"];
3791
3817
  for (const field of fieldsToCompare) {
3792
- const localVal = JSON.stringify(localData[field] ?? null);
3818
+ const localVal = JSON.stringify(
3819
+ localData[field] ?? null
3820
+ );
3793
3821
  const remoteVal = JSON.stringify(remote[field] ?? null);
3794
3822
  if (localVal !== remoteVal) {
3795
3823
  diffs.push({ field, local: localVal, remote: remoteVal });
@@ -4663,10 +4691,12 @@ var MODULE_SUBDIRS = /* @__PURE__ */ new Set(["dex", "manifest", "res", "assets"
4663
4691
  function detectCategory(path) {
4664
4692
  const lower = path.toLowerCase();
4665
4693
  if (lower.endsWith(".dex") || /\/dex\/[^/]+\.dex$/.test(lower)) return "dex";
4666
- if (lower === "resources.arsc" || lower.endsWith("/resources.arsc") || lower.endsWith("/resources.pb") || /^(([^/]+\/)?res\/)/.test(lower)) return "resources";
4694
+ if (lower === "resources.arsc" || lower.endsWith("/resources.arsc") || lower.endsWith("/resources.pb") || /^(([^/]+\/)?res\/)/.test(lower))
4695
+ return "resources";
4667
4696
  if (/^(([^/]+\/)?assets\/)/.test(lower)) return "assets";
4668
4697
  if (/^(([^/]+\/)?lib\/)/.test(lower)) return "native-libs";
4669
- if (lower === "androidmanifest.xml" || lower.endsWith("/androidmanifest.xml") || /^(([^/]+\/)?manifest\/)/.test(lower)) return "manifest";
4698
+ if (lower === "androidmanifest.xml" || lower.endsWith("/androidmanifest.xml") || /^(([^/]+\/)?manifest\/)/.test(lower))
4699
+ return "manifest";
4670
4700
  if (lower.startsWith("meta-inf/") || lower === "meta-inf") return "signing";
4671
4701
  return "other";
4672
4702
  }
@@ -4747,7 +4777,11 @@ async function analyzeBundle(filePath) {
4747
4777
  }));
4748
4778
  const moduleMap = /* @__PURE__ */ new Map();
4749
4779
  for (const entry of entries) {
4750
- const existing = moduleMap.get(entry.module) ?? { compressedSize: 0, uncompressedSize: 0, entries: 0 };
4780
+ const existing = moduleMap.get(entry.module) ?? {
4781
+ compressedSize: 0,
4782
+ uncompressedSize: 0,
4783
+ entries: 0
4784
+ };
4751
4785
  existing.compressedSize += entry.compressedSize;
4752
4786
  existing.uncompressedSize += entry.uncompressedSize;
4753
4787
  existing.entries += 1;
@@ -4755,7 +4789,11 @@ async function analyzeBundle(filePath) {
4755
4789
  }
4756
4790
  const categoryMap = /* @__PURE__ */ new Map();
4757
4791
  for (const entry of entries) {
4758
- const existing = categoryMap.get(entry.category) ?? { compressedSize: 0, uncompressedSize: 0, entries: 0 };
4792
+ const existing = categoryMap.get(entry.category) ?? {
4793
+ compressedSize: 0,
4794
+ uncompressedSize: 0,
4795
+ entries: 0
4796
+ };
4759
4797
  existing.compressedSize += entry.compressedSize;
4760
4798
  existing.uncompressedSize += entry.uncompressedSize;
4761
4799
  existing.entries += 1;
@@ -4842,7 +4880,7 @@ async function checkBundleSize(analysis, configPath = ".bundlesize.json") {
4842
4880
 
4843
4881
  // src/commands/status.ts
4844
4882
  import { mkdir as mkdir6, readFile as readFile11, writeFile as writeFile8 } from "fs/promises";
4845
- import { execSync } from "child_process";
4883
+ import { execFile as execFile2 } from "child_process";
4846
4884
  import { join as join8 } from "path";
4847
4885
  import { getCacheDir as getCacheDir2 } from "@gpc-cli/config";
4848
4886
  var DEFAULT_TTL_SECONDS = 3600;
@@ -4981,7 +5019,14 @@ async function getAppStatus(client, reporting, packageName, options = {}) {
4981
5019
  slowStartRate: options.vitalThresholds?.slowStartRate ?? DEFAULT_THRESHOLDS.slowStartRate,
4982
5020
  slowRenderingRate: options.vitalThresholds?.slowRenderingRate ?? DEFAULT_THRESHOLDS.slowRenderingRate
4983
5021
  };
4984
- const [releasesResult, crashesResult, anrResult, slowStartResult, slowRenderResult, reviewsResult] = await Promise.allSettled([
5022
+ const [
5023
+ releasesResult,
5024
+ crashesResult,
5025
+ anrResult,
5026
+ slowStartResult,
5027
+ slowRenderResult,
5028
+ reviewsResult
5029
+ ] = await Promise.allSettled([
4985
5030
  sections.has("releases") ? getReleasesStatus(client, packageName) : Promise.resolve([]),
4986
5031
  sections.has("vitals") ? queryVitalWithTrend(reporting, packageName, "crashRateMetricSet", days) : Promise.resolve(SKIPPED_VITAL),
4987
5032
  sections.has("vitals") ? queryVitalWithTrend(reporting, packageName, "anrRateMetricSet", days) : Promise.resolve(SKIPPED_VITAL),
@@ -5010,7 +5055,12 @@ async function getAppStatus(client, reporting, packageName, options = {}) {
5010
5055
  releases,
5011
5056
  vitals: {
5012
5057
  windowDays: days,
5013
- crashes: toVitalMetric(crashes.current, thresholds.crashRate, crashes.previous, crashes.trend),
5058
+ crashes: toVitalMetric(
5059
+ crashes.current,
5060
+ thresholds.crashRate,
5061
+ crashes.previous,
5062
+ crashes.trend
5063
+ ),
5014
5064
  anr: toVitalMetric(anr.current, thresholds.anrRate, anr.previous, anr.trend),
5015
5065
  slowStarts: toVitalMetric(
5016
5066
  slowStart.current,
@@ -5264,20 +5314,20 @@ function sendNotification(title, body) {
5264
5314
  try {
5265
5315
  const p = process.platform;
5266
5316
  if (p === "darwin") {
5267
- execSync(
5268
- `osascript -e 'display notification ${JSON.stringify(body)} with title ${JSON.stringify(title)}'`,
5269
- { stdio: "ignore" }
5270
- );
5317
+ execFile2("osascript", [
5318
+ "-e",
5319
+ `display notification ${JSON.stringify(body)} with title ${JSON.stringify(title)}`
5320
+ ]);
5271
5321
  } else if (p === "linux") {
5272
- execSync(`notify-send ${JSON.stringify(title)} ${JSON.stringify(body)}`, {
5273
- stdio: "ignore"
5274
- });
5322
+ execFile2("notify-send", [title, body]);
5275
5323
  } else if (p === "win32") {
5276
- const escaped = (s) => s.replace(/'/g, "''");
5277
- execSync(
5278
- `powershell -Command "Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.MessageBox]::Show('${escaped(body)}', '${escaped(title)}')"`,
5279
- { stdio: "ignore" }
5280
- );
5324
+ const psEscape = (s) => s.replace(/'/g, "''").replace(/[\r\n]/g, " ");
5325
+ execFile2("powershell", [
5326
+ "-NoProfile",
5327
+ "-NonInteractive",
5328
+ "-Command",
5329
+ `Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.MessageBox]::Show('${psEscape(body)}', '${psEscape(title)}')`
5330
+ ]);
5281
5331
  }
5282
5332
  } catch {
5283
5333
  }