@gpc-cli/core 0.9.36 → 0.9.38
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 +31 -2
- package/dist/index.js +160 -12
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { OutputFormat, ResolvedConfig, WebhookConfig } from '@gpc-cli/config';
|
|
2
2
|
import { AuthClient } from '@gpc-cli/auth';
|
|
3
3
|
import { GpcPlugin, PluginManifest, CommandEvent, CommandResult, PluginError, RequestEvent, ResponseEvent, PluginCommand } from '@gpc-cli/plugin-sdk';
|
|
4
|
-
import { PlayApiClient, Track, ExternallyHostedApk, ExternallyHostedApkResponse, UploadProgressEvent, ResumableUploadOptions, Listing, ImageType, CountryAvailability, Image, AppDetails, Review, ReviewReplyResponse, Subscription, SubscriptionOffer, OffersListResponse, BasePlanMigratePricesRequest, InAppProduct, SubscriptionDeferResponse, ProductPurchase, SubscriptionPurchaseV2, VoidedPurchase, UsersApiClient, User, DeveloperPermission, Grant, MetricRow, ReportingDimension, ReportingAggregation, VitalsMetricSet, ReportingApiClient, AnomalyDetectionResponse, MetricSetResponse, ErrorIssuesResponse, ConvertRegionPricesResponse, ReportType, StatsDimension, ReportBucket, Testers, AppRecoveryTargeting, AppRecoveryAction, CreateAppRecoveryActionRequest, DataSafety, ExternalTransaction, ExternalTransactionRefund, DeviceTierConfig, OneTimeOffer, OneTimeProduct, OneTimeOffersListResponse, OneTimeProductsListResponse, GamesApiClient, Achievement, GameEvent, Leaderboard, EnterpriseApiClient, CustomApp, GeneratedApk, PurchaseOption, PurchaseOptionsListResponse } from '@gpc-cli/api';
|
|
4
|
+
import { PlayApiClient, Track, ExternallyHostedApk, ExternallyHostedApkResponse, UploadProgressEvent, ResumableUploadOptions, Listing, ImageType, CountryAvailability, Image, AppDetails, Review, ReviewReplyResponse, Subscription, SubscriptionOffer, OffersListResponse, BasePlanMigratePricesRequest, InAppProduct, Order, SubscriptionDeferResponse, SubscriptionsV2DeferResponse, ProductPurchase, ProductPurchaseV2, SubscriptionPurchaseV2, VoidedPurchase, UsersApiClient, User, DeveloperPermission, Grant, MetricRow, ReportingDimension, ReportingAggregation, VitalsMetricSet, ReportingApiClient, AnomalyDetectionResponse, MetricSetResponse, ErrorIssuesResponse, ConvertRegionPricesResponse, ReportType, StatsDimension, ReportBucket, Testers, AppRecoveryTargeting, AppRecoveryAction, CreateAppRecoveryActionRequest, DataSafety, ExternalTransaction, ExternalTransactionRefund, DeviceTierConfig, OneTimeOffer, OneTimeProduct, OneTimeOffersListResponse, OneTimeProductsListResponse, GamesApiClient, Achievement, GameEvent, Leaderboard, EnterpriseApiClient, CustomApp, GeneratedApk, PurchaseOption, PurchaseOptionsListResponse } from '@gpc-cli/api';
|
|
5
5
|
|
|
6
6
|
declare class GpcError extends Error {
|
|
7
7
|
readonly code: string;
|
|
@@ -541,6 +541,11 @@ declare function refundOrder(client: PlayApiClient, packageName: string, orderId
|
|
|
541
541
|
fullRefund?: boolean;
|
|
542
542
|
proratedRefund?: boolean;
|
|
543
543
|
}): Promise<void>;
|
|
544
|
+
declare function getOrderDetails(client: PlayApiClient, packageName: string, orderId: string): Promise<Order>;
|
|
545
|
+
declare function batchGetOrders(client: PlayApiClient, packageName: string, orderIds: string[]): Promise<Order[]>;
|
|
546
|
+
declare function getProductPurchaseV2(client: PlayApiClient, packageName: string, token: string): Promise<ProductPurchaseV2>;
|
|
547
|
+
declare function cancelSubscriptionV2(client: PlayApiClient, packageName: string, token: string, cancellationType?: string): Promise<void>;
|
|
548
|
+
declare function deferSubscriptionV2(client: PlayApiClient, packageName: string, token: string, desiredExpiryTime: string): Promise<SubscriptionsV2DeferResponse>;
|
|
544
549
|
|
|
545
550
|
declare const PERMISSION_PROPAGATION_WARNING = "Note: Permission changes may take up to 48 hours to propagate.";
|
|
546
551
|
interface ListUsersOptions {
|
|
@@ -885,6 +890,8 @@ interface ParsedManifest {
|
|
|
885
890
|
services: ManifestComponent[];
|
|
886
891
|
receivers: ManifestComponent[];
|
|
887
892
|
providers: ManifestComponent[];
|
|
893
|
+
/** Set when the manifest could not be fully parsed — manifest-dependent scanners should skip. */
|
|
894
|
+
_parseError?: string;
|
|
888
895
|
}
|
|
889
896
|
interface ManifestFeature {
|
|
890
897
|
name: string;
|
|
@@ -1174,4 +1181,26 @@ declare function trackBreachState(packageName: string, isBreaching: boolean): Pr
|
|
|
1174
1181
|
declare function sendNotification(title: string, body: string): void;
|
|
1175
1182
|
declare function statusHasBreach(status: AppStatus): boolean;
|
|
1176
1183
|
|
|
1177
|
-
|
|
1184
|
+
interface ChangelogEntry {
|
|
1185
|
+
version: string;
|
|
1186
|
+
title: string;
|
|
1187
|
+
date: string;
|
|
1188
|
+
body: string;
|
|
1189
|
+
url: string;
|
|
1190
|
+
}
|
|
1191
|
+
interface FetchChangelogOptions {
|
|
1192
|
+
limit?: number;
|
|
1193
|
+
version?: string;
|
|
1194
|
+
}
|
|
1195
|
+
/**
|
|
1196
|
+
* Fetch release history from GitHub Releases API.
|
|
1197
|
+
* Public endpoint — no auth required (60 req/hour rate limit).
|
|
1198
|
+
*/
|
|
1199
|
+
declare function fetchChangelog(options?: FetchChangelogOptions): Promise<ChangelogEntry[]>;
|
|
1200
|
+
/**
|
|
1201
|
+
* Format a changelog entry as readable terminal text.
|
|
1202
|
+
* Strips markdown formatting for clean terminal output.
|
|
1203
|
+
*/
|
|
1204
|
+
declare function formatChangelogEntry(entry: ChangelogEntry): string;
|
|
1205
|
+
|
|
1206
|
+
export { ApiError, type AppInfo, type AppStatus, type AuditEntry, type BatchSyncResult, type BundleAnalysis, type BundleComparison, type BundleEntry, type BundleSizeCheckResult, type BundleSizeConfig, type ChangelogEntry, type CommandContext, ConfigError, DEFAULT_LIMITS, DEFAULT_PREFLIGHT_CONFIG, type DiffToken, type DiscoverPluginsOptions, type DryRunPublishResult, type DryRunResult, type DryRunUploadResult, type ExportImagesOptions, type ExportImagesSummary, type FastlaneDetection, type FastlaneLane, type FetchChangelogOptions, type FieldLintResult, type FileValidationResult, type FindingSeverity, GOOGLE_PLAY_LANGUAGES, type GetAppStatusOptions, type GitNotesOptions, type GitReleaseNotes, GpcError, type ImageValidationResult, type InitOptions, type InitResult, type InternalSharingUploadResult, type ListIapOptions, type ListSubscriptionsOptions, type ListUsersOptions, type ListVoidedOptions, type ListingDiff, type ListingFieldLimits, type ListingLintResult, type ListingsResult, type LoadedPlugin, type MigrationResult, NetworkError, type OneTimeProductDiff, PERMISSION_PROPAGATION_WARNING, type ParsedManifest, type ParsedMonth, PluginManager, type PreflightConfig, type PreflightFinding, type PreflightOptions, type PreflightResult, type PreflightScanner, type PublishOptions, type PublishResult, type PushResult, type QuotaUsage, type ReleaseDiff, type ReleaseNotesValidation, type ReleaseStatusResult, type ReviewAnalysis, type ReviewExportOptions, type ReviewsFilterOptions, SENSITIVE_ARG_KEYS, SENSITIVE_KEYS, SEVERITY_ORDER, type ScaffoldOptions, type ScaffoldResult, type Spinner, type StatusDiff, type StatusRelease, type StatusReviews, type StatusVitalMetric, type SubscriptionAnalytics, type SubscriptionDiff, type SyncResult, type ThresholdResult, type TrainConfig, type TrainState, type UploadResult, type ValidateCheck, type ValidateOptions, type ValidateResult, type VersionVitalsComparison, type VersionVitalsRow, type VitalsOverview, type VitalsQueryOptions, type VitalsTrendComparison, type WatchOptions, type WatchVitalsOptions, type WebhookPayload, abortTrain, acknowledgeProductPurchase, activateBasePlan, activateOffer, activatePurchaseOption, addRecoveryTargeting, addTesters, advanceTrain, analyzeBundle, analyzeRemoteListings, analyzeReviews, batchGetOrders, batchSyncInAppProducts, cancelRecoveryAction, cancelSubscriptionPurchase, cancelSubscriptionV2, checkBundleSize, checkThreshold, clearAuditLog, compareBundles, compareVersionVitals, compareVitalsTrend, computeStatusDiff, consumeProductPurchase, convertRegionPrices, createAuditEntry, createDeviceTier, createEnterpriseApp, createExternalTransaction, createGrant, createInAppProduct, createOffer, createOneTimeOffer, createOneTimeProduct, createPurchaseOption, createRecoveryAction, createSpinner, createSubscription, createTrack, deactivateBasePlan, deactivateOffer, deactivatePurchaseOption, deferSubscriptionPurchase, deferSubscriptionV2, deleteBasePlan, deleteGrant, deleteImage, deleteInAppProduct, deleteListing, deleteOffer, deleteOneTimeOffer, deleteOneTimeProduct, deleteSubscription, deployRecoveryAction, detectFastlane, detectOutputFormat, diffListings, diffListingsCommand, diffListingsEnhanced, diffOneTimeProduct, diffReleases, diffSubscription, discoverPlugins, downloadGeneratedApk, downloadReport, exportDataSafety, exportImages, exportReviews, fetchChangelog, fetchReleaseNotes, formatChangelogEntry, formatCustomPayload, formatDiscordPayload, formatJunit, formatOutput, formatSlackPayload, formatStatusDiff, formatStatusSummary, formatStatusTable, formatWordDiff, generateMigrationPlan, generateNotesFromGit, getAllScannerNames, getAppInfo, getAppStatus, getCountryAvailability, getDataSafety, getDeviceTier, getExternalTransaction, getInAppProduct, getListings, getOffer, getOneTimeOffer, getOneTimeProduct, getOrderDetails, getProductPurchase, getProductPurchaseV2, getPurchaseOption, getQuotaUsage, getReleasesStatus, getReview, getSubscription, getSubscriptionAnalytics, getSubscriptionPurchase, getTrainStatus, getUser, getVitalsAnomalies, getVitalsAnr, getVitalsBattery, getVitalsCrashes, getVitalsLmk, getVitalsMemory, getVitalsOverview, getVitalsRendering, getVitalsStartup, importDataSafety, importTestersFromCsv, initAudit, initProject, inviteUser, isFinancialReportType, isStatsReportType, isValidBcp47, isValidReportType, isValidStatsDimension, lintListing, lintListings, lintLocalListings, listAchievements, listAuditEvents, listDeviceTiers, listEnterpriseApps, listEvents, listGeneratedApks, listGrants, listImages, listInAppProducts, listLeaderboards, listOffers, listOneTimeOffers, listOneTimeProducts, listPurchaseOptions, listRecoveryActions, listReports, listReviews, listSubscriptions, listTesters, listTracks, listUsers, listVoidedPurchases, loadPreflightConfig, loadStatusCache, maybePaginate, migratePrices, parseAppfile, parseFastfile, parseGrantArg, parseMonth, pauseTrain, promoteRelease, publish, pullListings, pushListings, readListingsFromDir, readReleaseNotesFromDir, redactAuditArgs, redactSensitive, refundExternalTransaction, refundOrder, refundSubscriptionV2, removeTesters, removeUser, replyToReview, revokeSubscriptionPurchase, runPreflight, runWatchLoop, safePath, safePathWithin, saveStatusCache, scaffoldPlugin, searchAuditEvents, searchVitalsErrors, sendNotification, sendWebhook, sortResults, startTrain, statusHasBreach, syncInAppProducts, topFiles, trackBreachState, updateAppDetails, updateDataSafety, updateGrant, updateInAppProduct, updateListing, updateOffer, updateOneTimeOffer, updateOneTimeProduct, updateRollout, updateSubscription, updateTrackConfig, updateUser, uploadExternallyHosted, uploadImage, uploadInternalSharing, uploadRelease, validateImage, validateLanguageCode, validatePackageName, validatePreSubmission, validateReleaseNotes, validateSku, validateTrackName, validateUploadFile, validateVersionCode, watchVitalsWithAutoHalt, wordDiff, writeAuditLog, writeListingsToDir, writeMigrationOutput };
|
package/dist/index.js
CHANGED
|
@@ -115,7 +115,7 @@ function redactSensitive(data) {
|
|
|
115
115
|
if (typeof data === "object") {
|
|
116
116
|
const result = {};
|
|
117
117
|
for (const [key, value] of Object.entries(data)) {
|
|
118
|
-
if (SENSITIVE_KEYS.has(key)
|
|
118
|
+
if (SENSITIVE_KEYS.has(key)) {
|
|
119
119
|
result[key] = REDACTED;
|
|
120
120
|
} else {
|
|
121
121
|
result[key] = redactSensitive(value);
|
|
@@ -363,6 +363,10 @@ 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
370
|
var PluginManager = class {
|
|
367
371
|
plugins = [];
|
|
368
372
|
beforeHandlers = [];
|
|
@@ -373,7 +377,7 @@ var PluginManager = class {
|
|
|
373
377
|
registeredCommands = [];
|
|
374
378
|
/** Load and register a plugin */
|
|
375
379
|
async load(plugin, manifest) {
|
|
376
|
-
const isTrusted = manifest?.trusted ?? plugin.name
|
|
380
|
+
const isTrusted = manifest?.trusted ?? FIRST_PARTY_PLUGINS.has(plugin.name);
|
|
377
381
|
if (!isTrusted && manifest?.permissions) {
|
|
378
382
|
validatePermissions(manifest.permissions);
|
|
379
383
|
}
|
|
@@ -1658,6 +1662,14 @@ async function exportImages(client, packageName, dir, options) {
|
|
|
1658
1662
|
const dirPath = join12(dir, task.language, task.imageType);
|
|
1659
1663
|
await mkdir8(dirPath, { recursive: true });
|
|
1660
1664
|
const response = await fetch(task.url);
|
|
1665
|
+
if (!response.ok) {
|
|
1666
|
+
throw new GpcError(
|
|
1667
|
+
`Failed to download image: HTTP ${response.status} for ${task.imageType} (${task.language})`,
|
|
1668
|
+
"LISTINGS_IMAGE_DOWNLOAD_FAILED",
|
|
1669
|
+
4,
|
|
1670
|
+
"Check that the image URL is still valid. Re-run the export to retry."
|
|
1671
|
+
);
|
|
1672
|
+
}
|
|
1661
1673
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
1662
1674
|
const filePath = join12(dirPath, `${task.index}.png`);
|
|
1663
1675
|
await writeFile10(filePath, buffer);
|
|
@@ -3292,6 +3304,35 @@ async function refundOrder(client, packageName, orderId, options) {
|
|
|
3292
3304
|
validatePackageName(packageName);
|
|
3293
3305
|
return client.orders.refund(packageName, orderId, options);
|
|
3294
3306
|
}
|
|
3307
|
+
async function getOrderDetails(client, packageName, orderId) {
|
|
3308
|
+
validatePackageName(packageName);
|
|
3309
|
+
return client.orders.get(packageName, orderId);
|
|
3310
|
+
}
|
|
3311
|
+
async function batchGetOrders(client, packageName, orderIds) {
|
|
3312
|
+
validatePackageName(packageName);
|
|
3313
|
+
if (orderIds.length === 0) {
|
|
3314
|
+
throw new GpcError("No order IDs provided", "ORDERS_BATCH_EMPTY", 2, "Pass at least one order ID with --ids");
|
|
3315
|
+
}
|
|
3316
|
+
if (orderIds.length > 1e3) {
|
|
3317
|
+
throw new GpcError(`Too many order IDs (${orderIds.length}). Maximum is 1000.`, "ORDERS_BATCH_LIMIT", 2, "Split into multiple requests of 1000 or fewer");
|
|
3318
|
+
}
|
|
3319
|
+
return client.orders.batchGet(packageName, orderIds);
|
|
3320
|
+
}
|
|
3321
|
+
async function getProductPurchaseV2(client, packageName, token) {
|
|
3322
|
+
validatePackageName(packageName);
|
|
3323
|
+
return client.purchases.getProductV2(packageName, token);
|
|
3324
|
+
}
|
|
3325
|
+
async function cancelSubscriptionV2(client, packageName, token, cancellationType) {
|
|
3326
|
+
validatePackageName(packageName);
|
|
3327
|
+
const body = cancellationType ? { cancellationType } : void 0;
|
|
3328
|
+
return client.purchases.cancelSubscriptionV2(packageName, token, body);
|
|
3329
|
+
}
|
|
3330
|
+
async function deferSubscriptionV2(client, packageName, token, desiredExpiryTime) {
|
|
3331
|
+
validatePackageName(packageName);
|
|
3332
|
+
return client.purchases.deferSubscriptionV2(packageName, token, {
|
|
3333
|
+
deferralInfo: { desiredExpiryTime }
|
|
3334
|
+
});
|
|
3335
|
+
}
|
|
3295
3336
|
|
|
3296
3337
|
// src/commands/pricing.ts
|
|
3297
3338
|
async function convertRegionPrices(client, packageName, currencyCode, amount) {
|
|
@@ -3970,7 +4011,7 @@ function createSpinner(message) {
|
|
|
3970
4011
|
|
|
3971
4012
|
// src/utils/train-state.ts
|
|
3972
4013
|
import { mkdir as mkdir3, readFile as readFile7, writeFile as writeFile4 } from "fs/promises";
|
|
3973
|
-
import { join as join5 } from "path";
|
|
4014
|
+
import { dirname, join as join5 } from "path";
|
|
3974
4015
|
import { getCacheDir } from "@gpc-cli/config";
|
|
3975
4016
|
function stateFile(packageName) {
|
|
3976
4017
|
return join5(getCacheDir(), `train-${packageName}.json`);
|
|
@@ -3986,7 +4027,7 @@ async function readTrainState(packageName) {
|
|
|
3986
4027
|
}
|
|
3987
4028
|
async function writeTrainState(packageName, state) {
|
|
3988
4029
|
const path = stateFile(packageName);
|
|
3989
|
-
const dir =
|
|
4030
|
+
const dir = dirname(path);
|
|
3990
4031
|
await mkdir3(dir, { recursive: true });
|
|
3991
4032
|
await writeFile4(path, JSON.stringify(state, null, 2), "utf-8");
|
|
3992
4033
|
}
|
|
@@ -4085,8 +4126,11 @@ async function advanceTrain(apiClient, reportingClient, packageName) {
|
|
|
4085
4126
|
state.status = "paused";
|
|
4086
4127
|
state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4087
4128
|
await writeTrainState(packageName, state);
|
|
4088
|
-
throw new
|
|
4089
|
-
`Crash gate failed: ${(value * 100).toFixed(3)}% > max ${state.gates.crashes.max}%. Train paused
|
|
4129
|
+
throw new GpcError(
|
|
4130
|
+
`Crash gate failed: ${(value * 100).toFixed(3)}% > max ${state.gates.crashes.max}%. Train paused.`,
|
|
4131
|
+
"TRAIN_CRASH_GATE_FAILED",
|
|
4132
|
+
6,
|
|
4133
|
+
"Review crash data with: gpc vitals crashes --days 1"
|
|
4090
4134
|
);
|
|
4091
4135
|
}
|
|
4092
4136
|
}
|
|
@@ -4099,8 +4143,11 @@ async function advanceTrain(apiClient, reportingClient, packageName) {
|
|
|
4099
4143
|
state.status = "paused";
|
|
4100
4144
|
state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4101
4145
|
await writeTrainState(packageName, state);
|
|
4102
|
-
throw new
|
|
4103
|
-
`ANR gate failed: ${(value * 100).toFixed(3)}% > max ${state.gates.anr.max}%. Train paused
|
|
4146
|
+
throw new GpcError(
|
|
4147
|
+
`ANR gate failed: ${(value * 100).toFixed(3)}% > max ${state.gates.anr.max}%. Train paused.`,
|
|
4148
|
+
"TRAIN_ANR_GATE_FAILED",
|
|
4149
|
+
6,
|
|
4150
|
+
"Review ANR data with: gpc vitals anr --days 1"
|
|
4104
4151
|
);
|
|
4105
4152
|
}
|
|
4106
4153
|
}
|
|
@@ -4110,7 +4157,7 @@ async function advanceTrain(apiClient, reportingClient, packageName) {
|
|
|
4110
4157
|
}
|
|
4111
4158
|
async function executeStage(apiClient, packageName, state, stageIndex) {
|
|
4112
4159
|
const stage = state.stages[stageIndex];
|
|
4113
|
-
if (!stage) throw new
|
|
4160
|
+
if (!stage) throw new GpcError(`Stage ${stageIndex} not found`, "TRAIN_STAGE_NOT_FOUND", 1, "Check your release train configuration.");
|
|
4114
4161
|
const rolloutFraction = stage.rollout / 100;
|
|
4115
4162
|
await updateRollout(apiClient, packageName, stage.track, "increase", rolloutFraction);
|
|
4116
4163
|
stage.executedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -4556,7 +4603,7 @@ async function loadPreflightConfig(configPath) {
|
|
|
4556
4603
|
import { open as yauzlOpen } from "yauzl";
|
|
4557
4604
|
|
|
4558
4605
|
// src/preflight/manifest-parser.ts
|
|
4559
|
-
import
|
|
4606
|
+
import protobuf from "protobufjs";
|
|
4560
4607
|
var RESOURCE_IDS = {
|
|
4561
4608
|
16842752: "theme",
|
|
4562
4609
|
16842753: "label",
|
|
@@ -4717,9 +4764,35 @@ async function readAab(aabPath) {
|
|
|
4717
4764
|
`AAB is missing ${MANIFEST_PATH}. This does not appear to be a valid Android App Bundle.`
|
|
4718
4765
|
);
|
|
4719
4766
|
}
|
|
4720
|
-
|
|
4767
|
+
let manifest;
|
|
4768
|
+
try {
|
|
4769
|
+
manifest = decodeManifest(manifestBuf);
|
|
4770
|
+
} catch (err) {
|
|
4771
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
4772
|
+
manifest = createFallbackManifest();
|
|
4773
|
+
manifest._parseError = `Manifest could not be fully parsed: ${errMsg}. Manifest-dependent checks will be skipped.`;
|
|
4774
|
+
}
|
|
4721
4775
|
return { manifest, entries };
|
|
4722
4776
|
}
|
|
4777
|
+
function createFallbackManifest() {
|
|
4778
|
+
return {
|
|
4779
|
+
packageName: "",
|
|
4780
|
+
versionCode: 0,
|
|
4781
|
+
versionName: "",
|
|
4782
|
+
minSdk: 0,
|
|
4783
|
+
targetSdk: 0,
|
|
4784
|
+
debuggable: false,
|
|
4785
|
+
testOnly: false,
|
|
4786
|
+
usesCleartextTraffic: false,
|
|
4787
|
+
extractNativeLibs: true,
|
|
4788
|
+
permissions: [],
|
|
4789
|
+
features: [],
|
|
4790
|
+
activities: [],
|
|
4791
|
+
services: [],
|
|
4792
|
+
receivers: [],
|
|
4793
|
+
providers: []
|
|
4794
|
+
};
|
|
4795
|
+
}
|
|
4723
4796
|
function openAndScan(aabPath) {
|
|
4724
4797
|
return new Promise((resolve2, reject) => {
|
|
4725
4798
|
yauzlOpen(aabPath, { lazyEntries: true, autoClose: false }, (err, zipfile) => {
|
|
@@ -5868,11 +5941,23 @@ async function runPreflight(options) {
|
|
|
5868
5941
|
failOn: options.failOn ?? fileConfig.failOn ?? DEFAULT_PREFLIGHT_CONFIG.failOn
|
|
5869
5942
|
};
|
|
5870
5943
|
const ctx = { config };
|
|
5944
|
+
const earlyFindings = [];
|
|
5871
5945
|
if (options.aabPath) {
|
|
5872
5946
|
ctx.aabPath = options.aabPath;
|
|
5873
5947
|
const aab = await readAab(options.aabPath);
|
|
5874
5948
|
ctx.manifest = aab.manifest;
|
|
5875
5949
|
ctx.zipEntries = aab.entries;
|
|
5950
|
+
if (aab.manifest._parseError) {
|
|
5951
|
+
earlyFindings.push({
|
|
5952
|
+
scanner: "manifest-parser",
|
|
5953
|
+
ruleId: "manifest-parse-error",
|
|
5954
|
+
severity: "warning",
|
|
5955
|
+
title: "Manifest could not be fully parsed",
|
|
5956
|
+
message: aab.manifest._parseError,
|
|
5957
|
+
suggestion: "Manifest-dependent scanners (manifest, permissions, policy, privacy) were skipped. Other scanners (native-libs, size, secrets, billing) still ran."
|
|
5958
|
+
});
|
|
5959
|
+
ctx.manifest = void 0;
|
|
5960
|
+
}
|
|
5876
5961
|
}
|
|
5877
5962
|
if (options.metadataDir) ctx.metadataDir = options.metadataDir;
|
|
5878
5963
|
if (options.sourceDir) ctx.sourceDir = options.sourceDir;
|
|
@@ -5888,7 +5973,7 @@ async function runPreflight(options) {
|
|
|
5888
5973
|
return true;
|
|
5889
5974
|
});
|
|
5890
5975
|
const settled = await Promise.allSettled(applicableScanners.map((scanner) => scanner.scan(ctx)));
|
|
5891
|
-
let findings = [];
|
|
5976
|
+
let findings = [...earlyFindings];
|
|
5892
5977
|
for (let i = 0; i < settled.length; i++) {
|
|
5893
5978
|
const result = settled[i];
|
|
5894
5979
|
if (result.status === "fulfilled") {
|
|
@@ -6990,6 +7075,62 @@ function sendNotification(title, body) {
|
|
|
6990
7075
|
function statusHasBreach(status) {
|
|
6991
7076
|
return status.vitals.crashes.status === "breach" || status.vitals.anr.status === "breach" || status.vitals.slowStarts.status === "breach" || status.vitals.slowRender.status === "breach";
|
|
6992
7077
|
}
|
|
7078
|
+
|
|
7079
|
+
// src/commands/changelog.ts
|
|
7080
|
+
var GITHUB_RELEASES_URL = "https://api.github.com/repos/yasserstudio/gpc/releases";
|
|
7081
|
+
var DOCS_CHANGELOG_URL = "https://yasserstudio.github.io/gpc/reference/changelog";
|
|
7082
|
+
async function fetchChangelog(options) {
|
|
7083
|
+
const limit = options?.limit ?? 5;
|
|
7084
|
+
const url = options?.version ? `${GITHUB_RELEASES_URL}/tags/${options.version.startsWith("v") ? options.version : `v${options.version}`}` : `${GITHUB_RELEASES_URL}?per_page=${Math.min(limit, 100)}`;
|
|
7085
|
+
const controller = new AbortController();
|
|
7086
|
+
const timer = setTimeout(() => controller.abort(), 1e4);
|
|
7087
|
+
let response;
|
|
7088
|
+
try {
|
|
7089
|
+
response = await fetch(url, {
|
|
7090
|
+
headers: {
|
|
7091
|
+
Accept: "application/vnd.github+json",
|
|
7092
|
+
"User-Agent": "gpc-cli"
|
|
7093
|
+
},
|
|
7094
|
+
signal: controller.signal
|
|
7095
|
+
});
|
|
7096
|
+
} catch {
|
|
7097
|
+
throw new Error(
|
|
7098
|
+
`Could not fetch changelog. View online: ${DOCS_CHANGELOG_URL}`
|
|
7099
|
+
);
|
|
7100
|
+
} finally {
|
|
7101
|
+
clearTimeout(timer);
|
|
7102
|
+
}
|
|
7103
|
+
if (!response.ok) {
|
|
7104
|
+
if (response.status === 404 && options?.version) {
|
|
7105
|
+
throw new Error(
|
|
7106
|
+
`Version ${options.version} not found. Run: gpc changelog --limit 10`
|
|
7107
|
+
);
|
|
7108
|
+
}
|
|
7109
|
+
throw new Error(
|
|
7110
|
+
`GitHub API returned ${response.status}. View online: ${DOCS_CHANGELOG_URL}`
|
|
7111
|
+
);
|
|
7112
|
+
}
|
|
7113
|
+
const data = await response.json();
|
|
7114
|
+
const releases = Array.isArray(data) ? data : [data];
|
|
7115
|
+
return releases.filter((r) => r.tag_name.startsWith("v")).map((r) => ({
|
|
7116
|
+
version: r.tag_name,
|
|
7117
|
+
title: r.name || r.tag_name,
|
|
7118
|
+
date: r.published_at ? r.published_at.split("T")[0] : "unknown",
|
|
7119
|
+
body: r.body || "No release notes.",
|
|
7120
|
+
url: r.html_url
|
|
7121
|
+
}));
|
|
7122
|
+
}
|
|
7123
|
+
function formatChangelogEntry(entry) {
|
|
7124
|
+
const lines = [];
|
|
7125
|
+
lines.push(`${entry.version} \u2014 ${entry.title}`);
|
|
7126
|
+
lines.push(`Released: ${entry.date}`);
|
|
7127
|
+
lines.push("");
|
|
7128
|
+
const body = entry.body.replace(/^### /gm, "").replace(/^## /gm, "").replace(/^# /gm, "").replace(/\*\*(.*?)\*\*/g, "$1").replace(/`([^`]+)`/g, "$1").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").trim();
|
|
7129
|
+
lines.push(body);
|
|
7130
|
+
lines.push("");
|
|
7131
|
+
lines.push(`Full notes: ${entry.url}`);
|
|
7132
|
+
return lines.join("\n");
|
|
7133
|
+
}
|
|
6993
7134
|
export {
|
|
6994
7135
|
ApiError,
|
|
6995
7136
|
ConfigError,
|
|
@@ -7014,9 +7155,11 @@ export {
|
|
|
7014
7155
|
analyzeBundle,
|
|
7015
7156
|
analyzeRemoteListings,
|
|
7016
7157
|
analyzeReviews2 as analyzeReviews,
|
|
7158
|
+
batchGetOrders,
|
|
7017
7159
|
batchSyncInAppProducts,
|
|
7018
7160
|
cancelRecoveryAction,
|
|
7019
7161
|
cancelSubscriptionPurchase,
|
|
7162
|
+
cancelSubscriptionV2,
|
|
7020
7163
|
checkBundleSize,
|
|
7021
7164
|
checkThreshold,
|
|
7022
7165
|
clearAuditLog,
|
|
@@ -7044,6 +7187,7 @@ export {
|
|
|
7044
7187
|
deactivateOffer,
|
|
7045
7188
|
deactivatePurchaseOption,
|
|
7046
7189
|
deferSubscriptionPurchase,
|
|
7190
|
+
deferSubscriptionV2,
|
|
7047
7191
|
deleteBasePlan,
|
|
7048
7192
|
deleteGrant,
|
|
7049
7193
|
deleteImage,
|
|
@@ -7068,7 +7212,9 @@ export {
|
|
|
7068
7212
|
exportDataSafety,
|
|
7069
7213
|
exportImages,
|
|
7070
7214
|
exportReviews,
|
|
7215
|
+
fetchChangelog,
|
|
7071
7216
|
fetchReleaseNotes,
|
|
7217
|
+
formatChangelogEntry,
|
|
7072
7218
|
formatCustomPayload,
|
|
7073
7219
|
formatDiscordPayload,
|
|
7074
7220
|
formatJunit,
|
|
@@ -7092,7 +7238,9 @@ export {
|
|
|
7092
7238
|
getOffer,
|
|
7093
7239
|
getOneTimeOffer,
|
|
7094
7240
|
getOneTimeProduct,
|
|
7241
|
+
getOrderDetails,
|
|
7095
7242
|
getProductPurchase,
|
|
7243
|
+
getProductPurchaseV2,
|
|
7096
7244
|
getPurchaseOption,
|
|
7097
7245
|
getQuotaUsage,
|
|
7098
7246
|
getReleasesStatus,
|