@gpc-cli/core 0.9.20 → 0.9.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -676,4 +676,58 @@ declare function createPurchaseOption(client: PlayApiClient, packageName: string
676
676
  declare function activatePurchaseOption(client: PlayApiClient, packageName: string, purchaseOptionId: string): Promise<PurchaseOption>;
677
677
  declare function deactivatePurchaseOption(client: PlayApiClient, packageName: string, purchaseOptionId: string): Promise<PurchaseOption>;
678
678
 
679
- export { ApiError, type AppInfo, type AuditEntry, type BatchSyncResult, type CommandContext, ConfigError, type DiscoverPluginsOptions, type DryRunPublishResult, type DryRunResult, type DryRunUploadResult, type ExportImagesOptions, type ExportImagesSummary, type FastlaneDetection, type FastlaneLane, type FileValidationResult, GOOGLE_PLAY_LANGUAGES, type GitNotesOptions, type GitReleaseNotes, GpcError, type ImageValidationResult, type InternalSharingUploadResult, type ListIapOptions, type ListSubscriptionsOptions, type ListUsersOptions, type ListVoidedOptions, type ListingDiff, type ListingsResult, type LoadedPlugin, type MigrationResult, NetworkError, type OneTimeProductDiff, PERMISSION_PROPAGATION_WARNING, type ParsedMonth, PluginManager, type PublishOptions, type PublishResult, type PushResult, type ReleaseDiff, type ReleaseNotesValidation, type ReleaseStatusResult, type ReviewExportOptions, type ReviewsFilterOptions, SENSITIVE_ARG_KEYS, SENSITIVE_KEYS, type ScaffoldOptions, type ScaffoldResult, type Spinner, type SubscriptionDiff, type SyncResult, type ThresholdResult, type UploadResult, type ValidateCheck, type ValidateOptions, type ValidateResult, type VitalsOverview, type VitalsQueryOptions, type VitalsTrendComparison, type WebhookPayload, acknowledgeProductPurchase, activateBasePlan, activateOffer, activatePurchaseOption, addRecoveryTargeting, addTesters, batchSyncInAppProducts, cancelRecoveryAction, cancelSubscriptionPurchase, checkThreshold, clearAuditLog, compareVitalsTrend, consumeProductPurchase, convertRegionPrices, createAuditEntry, createDeviceTier, createExternalTransaction, createInAppProduct, createOffer, createOneTimeOffer, createOneTimeProduct, createPurchaseOption, createRecoveryAction, createSpinner, createSubscription, createTrack, deactivateBasePlan, deactivateOffer, deactivatePurchaseOption, deferSubscriptionPurchase, deleteBasePlan, deleteImage, deleteInAppProduct, deleteListing, deleteOffer, deleteOneTimeOffer, deleteOneTimeProduct, deleteSubscription, deployRecoveryAction, detectFastlane, detectOutputFormat, diffListings, diffListingsCommand, diffOneTimeProduct, diffReleases, diffSubscription, discoverPlugins, downloadGeneratedApk, downloadReport, exportDataSafety, exportImages, exportReviews, formatCustomPayload, formatDiscordPayload, formatJunit, formatOutput, formatSlackPayload, generateMigrationPlan, generateNotesFromGit, getAppInfo, getCountryAvailability, getDataSafety, getDeviceTier, getExternalTransaction, getInAppProduct, getListings, getOffer, getOneTimeOffer, getOneTimeProduct, getProductPurchase, getPurchaseOption, getReleasesStatus, getReview, getSubscription, getSubscriptionPurchase, getUser, getVitalsAnomalies, getVitalsAnr, getVitalsBattery, getVitalsCrashes, getVitalsMemory, getVitalsOverview, getVitalsRendering, getVitalsStartup, importDataSafety, importTestersFromCsv, initAudit, inviteUser, isFinancialReportType, isStatsReportType, isValidBcp47, isValidReportType, isValidStatsDimension, listAuditEvents, listDeviceTiers, listGeneratedApks, listImages, listInAppProducts, listOffers, listOneTimeOffers, listOneTimeProducts, listPurchaseOptions, listRecoveryActions, listReports, listReviews, listSubscriptions, listTesters, listTracks, listUsers, listVoidedPurchases, migratePrices, parseAppfile, parseFastfile, parseGrantArg, parseMonth, promoteRelease, publish, pullListings, pushListings, readListingsFromDir, readReleaseNotesFromDir, redactAuditArgs, redactSensitive, refundExternalTransaction, refundOrder, removeTesters, removeUser, replyToReview, revokeSubscriptionPurchase, safePath, safePathWithin, scaffoldPlugin, searchAuditEvents, searchVitalsErrors, sendWebhook, sortResults, syncInAppProducts, updateAppDetails, updateDataSafety, updateInAppProduct, updateListing, updateOffer, updateOneTimeOffer, updateOneTimeProduct, updateRollout, updateSubscription, updateTrackConfig, updateUser, uploadExternallyHosted, uploadImage, uploadInternalSharing, uploadRelease, validateImage, validateLanguageCode, validatePackageName, validatePreSubmission, validateReleaseNotes, validateSku, validateTrackName, validateUploadFile, validateVersionCode, writeAuditLog, writeListingsToDir, writeMigrationOutput };
679
+ interface BundleEntry {
680
+ path: string;
681
+ module: string;
682
+ category: string;
683
+ compressedSize: number;
684
+ uncompressedSize: number;
685
+ }
686
+ interface BundleAnalysis {
687
+ filePath: string;
688
+ fileType: "aab" | "apk";
689
+ totalCompressed: number;
690
+ totalUncompressed: number;
691
+ entryCount: number;
692
+ modules: {
693
+ name: string;
694
+ compressedSize: number;
695
+ uncompressedSize: number;
696
+ entries: number;
697
+ }[];
698
+ categories: {
699
+ name: string;
700
+ compressedSize: number;
701
+ uncompressedSize: number;
702
+ entries: number;
703
+ }[];
704
+ entries: BundleEntry[];
705
+ }
706
+ interface BundleComparison {
707
+ before: {
708
+ path: string;
709
+ totalCompressed: number;
710
+ };
711
+ after: {
712
+ path: string;
713
+ totalCompressed: number;
714
+ };
715
+ sizeDelta: number;
716
+ sizeDeltaPercent: number;
717
+ moduleDeltas: {
718
+ module: string;
719
+ before: number;
720
+ after: number;
721
+ delta: number;
722
+ }[];
723
+ categoryDeltas: {
724
+ category: string;
725
+ before: number;
726
+ after: number;
727
+ delta: number;
728
+ }[];
729
+ }
730
+ declare function analyzeBundle(filePath: string): Promise<BundleAnalysis>;
731
+ declare function compareBundles(before: BundleAnalysis, after: BundleAnalysis): BundleComparison;
732
+
733
+ export { ApiError, type AppInfo, type AuditEntry, type BatchSyncResult, type BundleAnalysis, type BundleComparison, type BundleEntry, type CommandContext, ConfigError, type DiscoverPluginsOptions, type DryRunPublishResult, type DryRunResult, type DryRunUploadResult, type ExportImagesOptions, type ExportImagesSummary, type FastlaneDetection, type FastlaneLane, type FileValidationResult, GOOGLE_PLAY_LANGUAGES, type GitNotesOptions, type GitReleaseNotes, GpcError, type ImageValidationResult, type InternalSharingUploadResult, type ListIapOptions, type ListSubscriptionsOptions, type ListUsersOptions, type ListVoidedOptions, type ListingDiff, type ListingsResult, type LoadedPlugin, type MigrationResult, NetworkError, type OneTimeProductDiff, PERMISSION_PROPAGATION_WARNING, type ParsedMonth, PluginManager, type PublishOptions, type PublishResult, type PushResult, type ReleaseDiff, type ReleaseNotesValidation, type ReleaseStatusResult, type ReviewExportOptions, type ReviewsFilterOptions, SENSITIVE_ARG_KEYS, SENSITIVE_KEYS, type ScaffoldOptions, type ScaffoldResult, type Spinner, type SubscriptionDiff, type SyncResult, type ThresholdResult, type UploadResult, type ValidateCheck, type ValidateOptions, type ValidateResult, type VitalsOverview, type VitalsQueryOptions, type VitalsTrendComparison, type WebhookPayload, acknowledgeProductPurchase, activateBasePlan, activateOffer, activatePurchaseOption, addRecoveryTargeting, addTesters, analyzeBundle, batchSyncInAppProducts, cancelRecoveryAction, cancelSubscriptionPurchase, checkThreshold, clearAuditLog, compareBundles, compareVitalsTrend, consumeProductPurchase, convertRegionPrices, createAuditEntry, createDeviceTier, createExternalTransaction, createInAppProduct, createOffer, createOneTimeOffer, createOneTimeProduct, createPurchaseOption, createRecoveryAction, createSpinner, createSubscription, createTrack, deactivateBasePlan, deactivateOffer, deactivatePurchaseOption, deferSubscriptionPurchase, deleteBasePlan, deleteImage, deleteInAppProduct, deleteListing, deleteOffer, deleteOneTimeOffer, deleteOneTimeProduct, deleteSubscription, deployRecoveryAction, detectFastlane, detectOutputFormat, diffListings, diffListingsCommand, diffOneTimeProduct, diffReleases, diffSubscription, discoverPlugins, downloadGeneratedApk, downloadReport, exportDataSafety, exportImages, exportReviews, formatCustomPayload, formatDiscordPayload, formatJunit, formatOutput, formatSlackPayload, generateMigrationPlan, generateNotesFromGit, getAppInfo, getCountryAvailability, getDataSafety, getDeviceTier, getExternalTransaction, getInAppProduct, getListings, getOffer, getOneTimeOffer, getOneTimeProduct, getProductPurchase, getPurchaseOption, getReleasesStatus, getReview, getSubscription, getSubscriptionPurchase, getUser, getVitalsAnomalies, getVitalsAnr, getVitalsBattery, getVitalsCrashes, getVitalsMemory, getVitalsOverview, getVitalsRendering, getVitalsStartup, importDataSafety, importTestersFromCsv, initAudit, inviteUser, isFinancialReportType, isStatsReportType, isValidBcp47, isValidReportType, isValidStatsDimension, listAuditEvents, listDeviceTiers, listGeneratedApks, listImages, listInAppProducts, listOffers, listOneTimeOffers, listOneTimeProducts, listPurchaseOptions, listRecoveryActions, listReports, listReviews, listSubscriptions, listTesters, listTracks, listUsers, listVoidedPurchases, migratePrices, parseAppfile, parseFastfile, parseGrantArg, parseMonth, promoteRelease, publish, pullListings, pushListings, readListingsFromDir, readReleaseNotesFromDir, redactAuditArgs, redactSensitive, refundExternalTransaction, refundOrder, removeTesters, removeUser, replyToReview, revokeSubscriptionPurchase, safePath, safePathWithin, scaffoldPlugin, searchAuditEvents, searchVitalsErrors, sendWebhook, sortResults, syncInAppProducts, updateAppDetails, updateDataSafety, updateInAppProduct, updateListing, updateOffer, updateOneTimeOffer, updateOneTimeProduct, updateRollout, updateSubscription, updateTrackConfig, updateUser, uploadExternallyHosted, uploadImage, uploadInternalSharing, uploadRelease, validateImage, validateLanguageCode, validatePackageName, validatePreSubmission, validateReleaseNotes, validateSku, validateTrackName, validateUploadFile, validateVersionCode, writeAuditLog, writeListingsToDir, writeMigrationOutput };
package/dist/index.js CHANGED
@@ -2042,6 +2042,7 @@ async function compareVitalsTrend(reporting, packageName, metricSet, days = 7) {
2042
2042
  const currentStart = new Date(now);
2043
2043
  currentStart.setDate(currentStart.getDate() - days);
2044
2044
  const previousEnd = new Date(currentStart);
2045
+ previousEnd.setDate(previousEnd.getDate() - 1);
2045
2046
  const previousStart = new Date(previousEnd);
2046
2047
  previousStart.setDate(previousStart.getDate() - days);
2047
2048
  const metrics = METRIC_SET_METRICS[metricSet] ?? ["errorReportCount", "distinctUsers"];
@@ -3759,6 +3760,158 @@ async function deactivatePurchaseOption(client, packageName, purchaseOptionId) {
3759
3760
  );
3760
3761
  }
3761
3762
  }
3763
+
3764
+ // src/commands/bundle-analysis.ts
3765
+ import { readFile as readFile9, stat as stat6 } from "fs/promises";
3766
+ var EOCD_SIGNATURE = 101010256;
3767
+ var CD_SIGNATURE = 33639248;
3768
+ var MODULE_SUBDIRS = /* @__PURE__ */ new Set(["dex", "manifest", "res", "assets", "lib", "resources.pb", "root"]);
3769
+ function detectCategory(path) {
3770
+ const lower = path.toLowerCase();
3771
+ if (lower.endsWith(".dex") || /\/dex\/[^/]+\.dex$/.test(lower)) return "dex";
3772
+ if (lower === "resources.arsc" || lower.endsWith("/resources.arsc") || lower.endsWith("/resources.pb") || /^(([^/]+\/)?res\/)/.test(lower)) return "resources";
3773
+ if (/^(([^/]+\/)?assets\/)/.test(lower)) return "assets";
3774
+ if (/^(([^/]+\/)?lib\/)/.test(lower)) return "native-libs";
3775
+ if (lower === "androidmanifest.xml" || lower.endsWith("/androidmanifest.xml") || /^(([^/]+\/)?manifest\/)/.test(lower)) return "manifest";
3776
+ if (lower.startsWith("meta-inf/") || lower === "meta-inf") return "signing";
3777
+ return "other";
3778
+ }
3779
+ function detectModule(path, isAab) {
3780
+ if (!isAab) return "(root)";
3781
+ const slashIdx = path.indexOf("/");
3782
+ if (slashIdx === -1) return "(root)";
3783
+ const topDir = path.substring(0, slashIdx);
3784
+ const rest = path.substring(slashIdx + 1);
3785
+ if (topDir === "base") return "base";
3786
+ if (topDir === "BUNDLE-METADATA" || topDir === "META-INF") return "(root)";
3787
+ if (path === "BundleConfig.pb") return "(root)";
3788
+ const subDir = rest.split("/")[0] || "";
3789
+ if (MODULE_SUBDIRS.has(subDir)) return topDir;
3790
+ return "(root)";
3791
+ }
3792
+ function parseCentralDirectory(buf) {
3793
+ let eocdOffset = -1;
3794
+ for (let i = buf.length - 22; i >= 0 && i >= buf.length - 65557; i--) {
3795
+ if (buf.readUInt32LE(i) === EOCD_SIGNATURE) {
3796
+ eocdOffset = i;
3797
+ break;
3798
+ }
3799
+ }
3800
+ if (eocdOffset === -1) {
3801
+ throw new Error("Not a valid ZIP file: EOCD signature not found");
3802
+ }
3803
+ const cdSize = buf.readUInt32LE(eocdOffset + 12);
3804
+ let cdOffset = buf.readUInt32LE(eocdOffset + 16);
3805
+ if (cdOffset === 4294967295) {
3806
+ const zip64LocatorOffset = eocdOffset - 20;
3807
+ if (zip64LocatorOffset >= 0 && buf.readUInt32LE(zip64LocatorOffset) === 117853008) {
3808
+ const zip64EocdOffset = Number(buf.readBigUInt64LE(zip64LocatorOffset + 8));
3809
+ if (zip64EocdOffset >= 0 && zip64EocdOffset < buf.length) {
3810
+ cdOffset = Number(buf.readBigUInt64LE(zip64EocdOffset + 48));
3811
+ }
3812
+ }
3813
+ }
3814
+ const entries = [];
3815
+ let pos = cdOffset;
3816
+ const end = cdOffset + cdSize;
3817
+ while (pos < end && pos + 46 <= buf.length) {
3818
+ const sig = buf.readUInt32LE(pos);
3819
+ if (sig !== CD_SIGNATURE) break;
3820
+ const compressedSize = buf.readUInt32LE(pos + 20);
3821
+ const uncompressedSize = buf.readUInt32LE(pos + 24);
3822
+ const filenameLen = buf.readUInt16LE(pos + 28);
3823
+ const extraLen = buf.readUInt16LE(pos + 30);
3824
+ const commentLen = buf.readUInt16LE(pos + 32);
3825
+ const filename = buf.toString("utf-8", pos + 46, pos + 46 + filenameLen);
3826
+ if (!filename.endsWith("/")) {
3827
+ entries.push({ filename, compressedSize, uncompressedSize });
3828
+ }
3829
+ pos += 46 + filenameLen + extraLen + commentLen;
3830
+ }
3831
+ return entries;
3832
+ }
3833
+ function detectFileType2(filePath) {
3834
+ const lower = filePath.toLowerCase();
3835
+ if (lower.endsWith(".aab")) return "aab";
3836
+ return "apk";
3837
+ }
3838
+ async function analyzeBundle(filePath) {
3839
+ const fileInfo = await stat6(filePath).catch(() => null);
3840
+ if (!fileInfo || !fileInfo.isFile()) {
3841
+ throw new Error(`File not found: ${filePath}`);
3842
+ }
3843
+ const buf = await readFile9(filePath);
3844
+ const cdEntries = parseCentralDirectory(buf);
3845
+ const fileType = detectFileType2(filePath);
3846
+ const isAab = fileType === "aab";
3847
+ const entries = cdEntries.map((e) => ({
3848
+ path: e.filename,
3849
+ module: detectModule(e.filename, isAab),
3850
+ category: detectCategory(e.filename),
3851
+ compressedSize: e.compressedSize,
3852
+ uncompressedSize: e.uncompressedSize
3853
+ }));
3854
+ const moduleMap = /* @__PURE__ */ new Map();
3855
+ for (const entry of entries) {
3856
+ const existing = moduleMap.get(entry.module) ?? { compressedSize: 0, uncompressedSize: 0, entries: 0 };
3857
+ existing.compressedSize += entry.compressedSize;
3858
+ existing.uncompressedSize += entry.uncompressedSize;
3859
+ existing.entries += 1;
3860
+ moduleMap.set(entry.module, existing);
3861
+ }
3862
+ const categoryMap = /* @__PURE__ */ new Map();
3863
+ for (const entry of entries) {
3864
+ const existing = categoryMap.get(entry.category) ?? { compressedSize: 0, uncompressedSize: 0, entries: 0 };
3865
+ existing.compressedSize += entry.compressedSize;
3866
+ existing.uncompressedSize += entry.uncompressedSize;
3867
+ existing.entries += 1;
3868
+ categoryMap.set(entry.category, existing);
3869
+ }
3870
+ const modules = [...moduleMap.entries()].map(([name, data]) => ({ name, ...data })).sort((a, b) => b.compressedSize - a.compressedSize);
3871
+ const categories = [...categoryMap.entries()].map(([name, data]) => ({ name, ...data })).sort((a, b) => b.compressedSize - a.compressedSize);
3872
+ const totalCompressed = entries.reduce((sum, e) => sum + e.compressedSize, 0);
3873
+ const totalUncompressed = entries.reduce((sum, e) => sum + e.uncompressedSize, 0);
3874
+ return {
3875
+ filePath,
3876
+ fileType,
3877
+ totalCompressed,
3878
+ totalUncompressed,
3879
+ entryCount: entries.length,
3880
+ modules,
3881
+ categories,
3882
+ entries
3883
+ };
3884
+ }
3885
+ function compareBundles(before, after) {
3886
+ const sizeDelta = after.totalCompressed - before.totalCompressed;
3887
+ const sizeDeltaPercent = before.totalCompressed > 0 ? Math.round(sizeDelta / before.totalCompressed * 100 * 10) / 10 : 0;
3888
+ const allModules = /* @__PURE__ */ new Set([
3889
+ ...before.modules.map((m) => m.name),
3890
+ ...after.modules.map((m) => m.name)
3891
+ ]);
3892
+ const moduleDeltas = [...allModules].map((module) => {
3893
+ const b = before.modules.find((m) => m.name === module)?.compressedSize ?? 0;
3894
+ const a = after.modules.find((m) => m.name === module)?.compressedSize ?? 0;
3895
+ return { module, before: b, after: a, delta: a - b };
3896
+ }).sort((a, b) => Math.abs(b.delta) - Math.abs(a.delta));
3897
+ const allCategories = /* @__PURE__ */ new Set([
3898
+ ...before.categories.map((c) => c.name),
3899
+ ...after.categories.map((c) => c.name)
3900
+ ]);
3901
+ const categoryDeltas = [...allCategories].map((category) => {
3902
+ const b = before.categories.find((c) => c.name === category)?.compressedSize ?? 0;
3903
+ const a = after.categories.find((c) => c.name === category)?.compressedSize ?? 0;
3904
+ return { category, before: b, after: a, delta: a - b };
3905
+ }).sort((a, b) => Math.abs(b.delta) - Math.abs(a.delta));
3906
+ return {
3907
+ before: { path: before.filePath, totalCompressed: before.totalCompressed },
3908
+ after: { path: after.filePath, totalCompressed: after.totalCompressed },
3909
+ sizeDelta,
3910
+ sizeDeltaPercent,
3911
+ moduleDeltas,
3912
+ categoryDeltas
3913
+ };
3914
+ }
3762
3915
  export {
3763
3916
  ApiError,
3764
3917
  ConfigError,
@@ -3775,11 +3928,13 @@ export {
3775
3928
  activatePurchaseOption,
3776
3929
  addRecoveryTargeting,
3777
3930
  addTesters,
3931
+ analyzeBundle,
3778
3932
  batchSyncInAppProducts,
3779
3933
  cancelRecoveryAction,
3780
3934
  cancelSubscriptionPurchase,
3781
3935
  checkThreshold,
3782
3936
  clearAuditLog,
3937
+ compareBundles,
3783
3938
  compareVitalsTrend,
3784
3939
  consumeProductPurchase,
3785
3940
  convertRegionPrices,