@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 +55 -1
- package/dist/index.js +155 -0
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
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
|
-
|
|
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,
|