@gpc-cli/core 0.10.7 → 0.11.0

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
@@ -1368,6 +1368,8 @@ interface ParsedManifest {
1368
1368
  services: ManifestComponent[];
1369
1369
  receivers: ManifestComponent[];
1370
1370
  providers: ManifestComponent[];
1371
+ /** `<meta-data>` entries of `<application>`, name to value. Entries without a value are omitted. */
1372
+ metaData: Record<string, string>;
1371
1373
  /** Android 16+ compat shim for non-16KB-aligned native libs. */
1372
1374
  pageSizeCompat?: boolean;
1373
1375
  /** Set when the manifest could not be fully parsed -- manifest-dependent scanners should skip. */
@@ -1668,6 +1670,12 @@ declare function runWatchLoop(opts: WatchOptions): Promise<void>;
1668
1670
  /** Returns true if breach state changed (breach started or cleared). */
1669
1671
  declare function trackBreachState(packageName: string, isBreaching: boolean): Promise<boolean>;
1670
1672
  declare function sendNotification(title: string, body: string): void;
1673
+ /**
1674
+ * Re-rates a saved status against the thresholds of the current run. A cached
1675
+ * status keeps the thresholds of the run that fetched it, so serving it as-is
1676
+ * would gate on values the caller never asked for.
1677
+ */
1678
+ declare function applyVitalThresholds(status: AppStatus, vitalThresholds: GetAppStatusOptions["vitalThresholds"]): AppStatus;
1671
1679
  declare function statusHasBreach(status: AppStatus): boolean;
1672
1680
 
1673
1681
  interface ChangelogEntry {
@@ -2052,4 +2060,4 @@ declare function readinessBadgeColor(grade: ReadinessGrade): string;
2052
2060
  declare function readinessBadgeUrl(score: ReadinessScore): string;
2053
2061
  declare function readinessBadgeMarkdown(score: ReadinessScore): string;
2054
2062
 
2055
- export { ApiError, type ApiSigningFingerprint, type AppInfo, type AppStatus, type ApplyReleaseNotesResult, type AuditEntry, type BatchSyncResult, type BundleAnalysis, type BundleComparison, type BundleEntry, type BundleSizeCheckResult, type BundleSizeConfig, type BundlesWaitOptions, type ChangelogEntry, type ChecklistInput, type ChecklistItem, type ChecklistResult, type CommandContext, type CommitCluster, type CommitResult, ConfigError, type CreateEnterpriseAppParams, DEFAULT_LIMITS, DEFAULT_MODELS, DEFAULT_PREFLIGHT_CONFIG, DEFAULT_WATCH_THRESHOLDS, type DecodedNotification, type DiffToken, type DiscoverPluginsOptions, type DiscoveredPlugin, type DryRunPublishResult, type DryRunResult, type DryRunUploadResult, type ErrorReason, type ExportImagesOptions, type ExportImagesSummary, type ExternalTransactionRefundOptions, type FastlaneDetection, type FastlaneLane, type FetchChangelogOptions, type FieldLintResult, type FileValidationResult, type FinancialReportDownload, type FindingSeverity, GOOGLE_PLAY_LANGUAGES, type GameConfigDiff, type GamesPushOptions, type GamesSyncResult, type GenerateOptions, type GeneratedChangelog, type GetAppStatusOptions, type GitNotesOptions, type GitReleaseNotes, type GitRunner, GpcError, type ImageSyncDetail, type ImageSyncOptions, type ImageSyncResult, type ImageValidationResult, type InitOptions, type InitResult, type InternalSharingUploadResult, type KeystoreFingerprint, type ListGamesConfigOptions, type ListIapOptions, type ListReportsResult, type ListSubscriptionsOptions, type ListUsersOptions, type ListVoidedOptions, type ListingDiff, type ListingFieldLimits, type ListingLintResult, type ListingsResult, type LoadedPlugin, type LocaleBundle, type LocaleEntry, type MigrationResult, NetworkError, type OneTimeProductDiff, type OutputMode, PERMISSION_PROPAGATION_WARNING, PLACEHOLDER_TEXT, PLAY_STORE_LIMIT, PROVIDER_WHITELIST, type ParsedCommit, type ParsedManifest, type ParsedMonth, type PlayStoreFormat, type PlayStoreRenderOptions, PluginManager, type PreflightConfig, type PreflightFinding, type PreflightOptions, type PreflightResult, type PreflightScanner, type Provider, type PublishOptions, type PublishResult, type PushResult, type QuotaUsage, RENDERERS, REVIEW_SORT_PRESETS, type RawCommit, type ReadinessBreakdownEntry, type ReadinessGrade, type ReadinessScore, type ReadinessSignal, type ReadinessSignalStatus, type RefundReviewPreference, type ReleaseDiff, type ReleaseNotesValidation, type ReleaseStatusResult, type Renderer, type ReportObject, type ReportsAuth, type ResolveAiConfigOptions, type ResolveLocalesOptions, type ReviewAnalysis, type ReviewExportOptions, type ReviewOrderRefundOptions, type ReviewOrderRefundResult, type ReviewSortPreset, type ReviewsFilterOptions, type RevocationRefundType, type RtdnStatus, SECTION_ORDER, SENSITIVE_ARG_KEYS, SENSITIVE_KEYS, SEVERITY_ORDER, type ScaffoldOptions, type ScaffoldResult, type SigningConsistencyResult, type SigningKeyComparison, type Spinner, type StatsReportDownload, type StatusDiff, type StatusRelease, type StatusReviews, type StatusVitalMetric, type SubscriptionAnalytics, type SubscriptionDiff, type SyncResult, type ThresholdResult, type TrainConfig, type TrainState, type TranslateBundleOptions, type TranslatedBundle, type TranslationFailure, type TranslationPath, type TranslationResult, type Translator, type TranslatorConfig, type UploadResult, VALID_WATCH_METRICS, type ValidateCheck, type ValidateOptions, type ValidateResult, type ValidatedPluginApproval, type VersionVitalsComparison, type VersionVitalsRow, type VitalsOverview, type VitalsQueryOptions, type VitalsTrendComparison, type WatchAction, type WatchCallbacks, type WatchConfig, type WatchEvent, type WatchMetric, type WatchOptions, type WatchRollout, type WatchSummary, type WatchVitalReading, type WatchVitalsOptions, type WebhookPayload, type ZipCsvEntry, abortTrain, acknowledgeProductPurchase, activateBasePlan, activateOffer, addRecoveryTargeting, addTesters, advanceTrain, analyzeBundle, analyzeRemoteListings, analyzeReviews, annotateListResult, applyReleaseNotes, assertRateThreshold, assignRelease, batchGetOrders, batchSyncInAppProducts, buildChecklist, buildExternalTransactionRefund, buildLocaleBundle, bundleToReleaseNotes, cancelRecoveryAction, cancelSubscriptionPurchase, cancelSubscriptionV2, checkBundleSize, checkSigningConsistency, checkThreshold, classifyError, clearAuditLog, commitWithRescue, compareBundles, compareFingerprints, compareVersionVitals, compareVitalsTrend, computeStatusDiff, consumeProductPurchase, convertRegionPrices, createAchievementConfig, createAuditEntry, createDeviceTier, createEnterpriseApp, createExternalTransaction, createGrant, createInAppProduct, createLeaderboardConfig, createOffer, createOneTimeOffer, createOneTimeProduct, createRecoveryAction, createSpinner, createSubscription, createTrack, createTranslator, deactivateBasePlan, deactivateOffer, decodeCsvText, decodeNotification, decodeStatsCsv, defaultGitRunner, deferSubscriptionPurchase, deferSubscriptionV2, deleteAchievementConfig, deleteBasePlan, deleteGrant, deleteImage, deleteInAppProduct, deleteLeaderboardConfig, deleteListing, deleteOffer, deleteOneTimeOffer, deleteOneTimeProduct, deleteSubscription, deployRecoveryAction, detectFastlane, detectOutputFormat, diffAchievementConfig, diffLeaderboardConfig, diffListings, diffListingsCommand, diffListingsEnhanced, diffOneTimeProduct, diffReleases, diffSubscription, discoverPluginEntries, discoverPlugins, dispatchWebhook, downloadFinancialReport, downloadGeneratedApk, downloadReportObject, downloadStatsReport, exportImages, exportReviews, extractCsvEntriesFromZip, fetchAggregateCost, fetchChangelog, fetchReleaseNotes, findBundle, formatChangelogEntry, formatCustomPayload, formatDiscordPayload, formatJunit, formatMoney, formatNotification, formatOutput, formatPathLabel, formatSlackPayload, formatStatusDiff, formatStatusSummary, formatStatusTable, formatWordDiff, generateChangelog, generateMigrationPlan, generateNotesFromGit, getAchievementConfig, getAllScannerNames, getApiSigningFingerprint, getAppInfo, getAppStatus, getCountryAvailability, getDeviceTier, getExternalTransaction, getInAppProduct, getKeystoreFingerprint, getLeaderboardConfig, getListings, getOffer, getOneTimeOffer, getOneTimeProduct, getOrderDetails, getProductPurchase, getProductPurchaseV2, getQuotaUsage, getReleasesStatus, getReview, getRtdnStatus, getSubscription, getSubscriptionAnalytics, getSubscriptionPurchase, getTrainStatus, getUser, getVitalsAnomalies, getVitalsAnr, getVitalsBattery, getVitalsCrashes, getVitalsErrorCount, getVitalsLmk, getVitalsMemory, getVitalsMemoryBitmap, getVitalsMemoryRss, getVitalsOverview, getVitalsRendering, getVitalsStartup, gradeFromPercent, gunzipIfNeeded, handleBreach, importDataSafety, importTestersFromCsv, initAudit, initProject, inviteUser, isFinancialReportType, isGzip, isStatsReportType, isValidBcp47, isValidReportType, isValidStatsDimension, isVersionedNotesDir, isZip, latestWorstValue, lintListing, lintListings, lintLocalListings, listAchievementConfigs, listAchievements, listAuditEvents, listBundles, listDeviceTiers, listGeneratedApks, listGrants, listImages, listInAppProducts, listLeaderboardConfigs, listLeaderboards, listOffers, listOneTimeOffers, listOneTimeProducts, listRecoveryActions, listReportObjects, listReports, listReviews, listSubscriptions, listTesters, listTracks, listUsers, listVoidedPurchases, loadPreflightConfig, loadStatusCache, maybePaginate, migratePrices, monthToken, moreResultsFooter, normalizeFingerprint, parseAppfile, parseCommit, parseFastfile, parseGrantArg, parseKeytoolOutput, parseMonth, parseRemoteUrl, pauseTrain, promoteRelease, publish, publishEnterpriseApp, pullAchievementConfigs, pullLeaderboardConfigs, pullListings, pushAchievementConfigs, pushLeaderboardConfigs, pushListings, readListingsFromDir, readMetricValue, readReleaseNotesForVersion, readReleaseNotesFromDir, readinessBadgeColor, readinessBadgeMarkdown, readinessBadgeUrl, redactAuditArgs, redactSensitive, refundExternalTransaction, refundOrder, relativeTime, removeTesters, removeUser, renderChecklistMarkdown, renderJson, renderMarkdown, renderPlayStore, renderPlayStoreJson, renderPlayStoreMd, renderPlayStorePrompt, renderPrompt, replyToReview, reportAccessDeniedError, reportBucketNotFoundError, reportObjectNotFoundError, reportPrefix, resolveAiConfig, resolveLocales, resolveReportsBucket, retryOnUploadNotComplete, reviewOrderRefund, revokeSubscriptionPurchase, runPreflight, runWatch, runWatchLoop, safePath, safePathWithin, saveStatusCache, scaffoldPlugin, scoreReadiness, searchAuditEvents, searchVitalsErrors, sendNotification, sendWebhook, setAchievementIcon, setLeaderboardIcon, sha256File, sortResults, sortReviews, startTrain, statusHasBreach, syncImages, syncInAppProducts, topFiles, trackBreachState, translateBundle, updateAchievementConfig, updateAppDetails, updateDataSafety, updateGrant, updateInAppProduct, updateLeaderboardConfig, updateListing, updateOffer, updateOneTimeOffer, updateOneTimeProduct, updateRollout, updateSubscription, updateTrackConfig, updateUser, uploadExternallyHosted, uploadImage, uploadInternalSharing, uploadRelease, validateAndCommit, validateBundleForApply, validateImage, validateLanguageCode, validatePackageName, validatePluginForApproval, validatePreSubmission, validateReleaseNotes, validateSku, validateTrackName, validateUploadFile, validateVersionCode, waitForBundle, waitForBundleProcessing, watchVitalsWithAutoHalt, wordDiff, worstSliceAverage, writeAuditLog, writeListingsToDir, writeMigrationOutput };
2063
+ export { ApiError, type ApiSigningFingerprint, type AppInfo, type AppStatus, type ApplyReleaseNotesResult, type AuditEntry, type BatchSyncResult, type BundleAnalysis, type BundleComparison, type BundleEntry, type BundleSizeCheckResult, type BundleSizeConfig, type BundlesWaitOptions, type ChangelogEntry, type ChecklistInput, type ChecklistItem, type ChecklistResult, type CommandContext, type CommitCluster, type CommitResult, ConfigError, type CreateEnterpriseAppParams, DEFAULT_LIMITS, DEFAULT_MODELS, DEFAULT_PREFLIGHT_CONFIG, DEFAULT_WATCH_THRESHOLDS, type DecodedNotification, type DiffToken, type DiscoverPluginsOptions, type DiscoveredPlugin, type DryRunPublishResult, type DryRunResult, type DryRunUploadResult, type ErrorReason, type ExportImagesOptions, type ExportImagesSummary, type ExternalTransactionRefundOptions, type FastlaneDetection, type FastlaneLane, type FetchChangelogOptions, type FieldLintResult, type FileValidationResult, type FinancialReportDownload, type FindingSeverity, GOOGLE_PLAY_LANGUAGES, type GameConfigDiff, type GamesPushOptions, type GamesSyncResult, type GenerateOptions, type GeneratedChangelog, type GetAppStatusOptions, type GitNotesOptions, type GitReleaseNotes, type GitRunner, GpcError, type ImageSyncDetail, type ImageSyncOptions, type ImageSyncResult, type ImageValidationResult, type InitOptions, type InitResult, type InternalSharingUploadResult, type KeystoreFingerprint, type ListGamesConfigOptions, type ListIapOptions, type ListReportsResult, type ListSubscriptionsOptions, type ListUsersOptions, type ListVoidedOptions, type ListingDiff, type ListingFieldLimits, type ListingLintResult, type ListingsResult, type LoadedPlugin, type LocaleBundle, type LocaleEntry, type MigrationResult, NetworkError, type OneTimeProductDiff, type OutputMode, PERMISSION_PROPAGATION_WARNING, PLACEHOLDER_TEXT, PLAY_STORE_LIMIT, PROVIDER_WHITELIST, type ParsedCommit, type ParsedManifest, type ParsedMonth, type PlayStoreFormat, type PlayStoreRenderOptions, PluginManager, type PreflightConfig, type PreflightFinding, type PreflightOptions, type PreflightResult, type PreflightScanner, type Provider, type PublishOptions, type PublishResult, type PushResult, type QuotaUsage, RENDERERS, REVIEW_SORT_PRESETS, type RawCommit, type ReadinessBreakdownEntry, type ReadinessGrade, type ReadinessScore, type ReadinessSignal, type ReadinessSignalStatus, type RefundReviewPreference, type ReleaseDiff, type ReleaseNotesValidation, type ReleaseStatusResult, type Renderer, type ReportObject, type ReportsAuth, type ResolveAiConfigOptions, type ResolveLocalesOptions, type ReviewAnalysis, type ReviewExportOptions, type ReviewOrderRefundOptions, type ReviewOrderRefundResult, type ReviewSortPreset, type ReviewsFilterOptions, type RevocationRefundType, type RtdnStatus, SECTION_ORDER, SENSITIVE_ARG_KEYS, SENSITIVE_KEYS, SEVERITY_ORDER, type ScaffoldOptions, type ScaffoldResult, type SigningConsistencyResult, type SigningKeyComparison, type Spinner, type StatsReportDownload, type StatusDiff, type StatusRelease, type StatusReviews, type StatusVitalMetric, type SubscriptionAnalytics, type SubscriptionDiff, type SyncResult, type ThresholdResult, type TrainConfig, type TrainState, type TranslateBundleOptions, type TranslatedBundle, type TranslationFailure, type TranslationPath, type TranslationResult, type Translator, type TranslatorConfig, type UploadResult, VALID_WATCH_METRICS, type ValidateCheck, type ValidateOptions, type ValidateResult, type ValidatedPluginApproval, type VersionVitalsComparison, type VersionVitalsRow, type VitalsOverview, type VitalsQueryOptions, type VitalsTrendComparison, type WatchAction, type WatchCallbacks, type WatchConfig, type WatchEvent, type WatchMetric, type WatchOptions, type WatchRollout, type WatchSummary, type WatchVitalReading, type WatchVitalsOptions, type WebhookPayload, type ZipCsvEntry, abortTrain, acknowledgeProductPurchase, activateBasePlan, activateOffer, addRecoveryTargeting, addTesters, advanceTrain, analyzeBundle, analyzeRemoteListings, analyzeReviews, annotateListResult, applyReleaseNotes, applyVitalThresholds, assertRateThreshold, assignRelease, batchGetOrders, batchSyncInAppProducts, buildChecklist, buildExternalTransactionRefund, buildLocaleBundle, bundleToReleaseNotes, cancelRecoveryAction, cancelSubscriptionPurchase, cancelSubscriptionV2, checkBundleSize, checkSigningConsistency, checkThreshold, classifyError, clearAuditLog, commitWithRescue, compareBundles, compareFingerprints, compareVersionVitals, compareVitalsTrend, computeStatusDiff, consumeProductPurchase, convertRegionPrices, createAchievementConfig, createAuditEntry, createDeviceTier, createEnterpriseApp, createExternalTransaction, createGrant, createInAppProduct, createLeaderboardConfig, createOffer, createOneTimeOffer, createOneTimeProduct, createRecoveryAction, createSpinner, createSubscription, createTrack, createTranslator, deactivateBasePlan, deactivateOffer, decodeCsvText, decodeNotification, decodeStatsCsv, defaultGitRunner, deferSubscriptionPurchase, deferSubscriptionV2, deleteAchievementConfig, deleteBasePlan, deleteGrant, deleteImage, deleteInAppProduct, deleteLeaderboardConfig, deleteListing, deleteOffer, deleteOneTimeOffer, deleteOneTimeProduct, deleteSubscription, deployRecoveryAction, detectFastlane, detectOutputFormat, diffAchievementConfig, diffLeaderboardConfig, diffListings, diffListingsCommand, diffListingsEnhanced, diffOneTimeProduct, diffReleases, diffSubscription, discoverPluginEntries, discoverPlugins, dispatchWebhook, downloadFinancialReport, downloadGeneratedApk, downloadReportObject, downloadStatsReport, exportImages, exportReviews, extractCsvEntriesFromZip, fetchAggregateCost, fetchChangelog, fetchReleaseNotes, findBundle, formatChangelogEntry, formatCustomPayload, formatDiscordPayload, formatJunit, formatMoney, formatNotification, formatOutput, formatPathLabel, formatSlackPayload, formatStatusDiff, formatStatusSummary, formatStatusTable, formatWordDiff, generateChangelog, generateMigrationPlan, generateNotesFromGit, getAchievementConfig, getAllScannerNames, getApiSigningFingerprint, getAppInfo, getAppStatus, getCountryAvailability, getDeviceTier, getExternalTransaction, getInAppProduct, getKeystoreFingerprint, getLeaderboardConfig, getListings, getOffer, getOneTimeOffer, getOneTimeProduct, getOrderDetails, getProductPurchase, getProductPurchaseV2, getQuotaUsage, getReleasesStatus, getReview, getRtdnStatus, getSubscription, getSubscriptionAnalytics, getSubscriptionPurchase, getTrainStatus, getUser, getVitalsAnomalies, getVitalsAnr, getVitalsBattery, getVitalsCrashes, getVitalsErrorCount, getVitalsLmk, getVitalsMemory, getVitalsMemoryBitmap, getVitalsMemoryRss, getVitalsOverview, getVitalsRendering, getVitalsStartup, gradeFromPercent, gunzipIfNeeded, handleBreach, importDataSafety, importTestersFromCsv, initAudit, initProject, inviteUser, isFinancialReportType, isGzip, isStatsReportType, isValidBcp47, isValidReportType, isValidStatsDimension, isVersionedNotesDir, isZip, latestWorstValue, lintListing, lintListings, lintLocalListings, listAchievementConfigs, listAchievements, listAuditEvents, listBundles, listDeviceTiers, listGeneratedApks, listGrants, listImages, listInAppProducts, listLeaderboardConfigs, listLeaderboards, listOffers, listOneTimeOffers, listOneTimeProducts, listRecoveryActions, listReportObjects, listReports, listReviews, listSubscriptions, listTesters, listTracks, listUsers, listVoidedPurchases, loadPreflightConfig, loadStatusCache, maybePaginate, migratePrices, monthToken, moreResultsFooter, normalizeFingerprint, parseAppfile, parseCommit, parseFastfile, parseGrantArg, parseKeytoolOutput, parseMonth, parseRemoteUrl, pauseTrain, promoteRelease, publish, publishEnterpriseApp, pullAchievementConfigs, pullLeaderboardConfigs, pullListings, pushAchievementConfigs, pushLeaderboardConfigs, pushListings, readListingsFromDir, readMetricValue, readReleaseNotesForVersion, readReleaseNotesFromDir, readinessBadgeColor, readinessBadgeMarkdown, readinessBadgeUrl, redactAuditArgs, redactSensitive, refundExternalTransaction, refundOrder, relativeTime, removeTesters, removeUser, renderChecklistMarkdown, renderJson, renderMarkdown, renderPlayStore, renderPlayStoreJson, renderPlayStoreMd, renderPlayStorePrompt, renderPrompt, replyToReview, reportAccessDeniedError, reportBucketNotFoundError, reportObjectNotFoundError, reportPrefix, resolveAiConfig, resolveLocales, resolveReportsBucket, retryOnUploadNotComplete, reviewOrderRefund, revokeSubscriptionPurchase, runPreflight, runWatch, runWatchLoop, safePath, safePathWithin, saveStatusCache, scaffoldPlugin, scoreReadiness, searchAuditEvents, searchVitalsErrors, sendNotification, sendWebhook, setAchievementIcon, setLeaderboardIcon, sha256File, sortResults, sortReviews, startTrain, statusHasBreach, syncImages, syncInAppProducts, topFiles, trackBreachState, translateBundle, updateAchievementConfig, updateAppDetails, updateDataSafety, updateGrant, updateInAppProduct, updateLeaderboardConfig, updateListing, updateOffer, updateOneTimeOffer, updateOneTimeProduct, updateRollout, updateSubscription, updateTrackConfig, updateUser, uploadExternallyHosted, uploadImage, uploadInternalSharing, uploadRelease, validateAndCommit, validateBundleForApply, validateImage, validateLanguageCode, validatePackageName, validatePluginForApproval, validatePreSubmission, validateReleaseNotes, validateSku, validateTrackName, validateUploadFile, validateVersionCode, waitForBundle, waitForBundleProcessing, watchVitalsWithAutoHalt, wordDiff, worstSliceAverage, writeAuditLog, writeListingsToDir, writeMigrationOutput };
package/dist/index.js CHANGED
@@ -3093,7 +3093,7 @@ async function loadStatusCache(packageName, ttlSeconds = DEFAULT_TTL_SECONDS) {
3093
3093
  const raw = await readFile4(cacheFilePath(packageName), "utf-8");
3094
3094
  const entry = JSON.parse(raw);
3095
3095
  const age = (Date.now() - new Date(entry.fetchedAt).getTime()) / 1e3;
3096
- if (age > (entry.ttl ?? ttlSeconds)) return null;
3096
+ if (age > ttlSeconds) return null;
3097
3097
  const data = entry.data;
3098
3098
  return {
3099
3099
  ...data,
@@ -3164,6 +3164,14 @@ async function queryVitalWithTrend(reporting, packageName, metricSet, days) {
3164
3164
  return { current, previous, trend };
3165
3165
  }
3166
3166
  var SKIPPED_VITAL = { current: void 0, previous: void 0, trend: null };
3167
+ function resolveStatusThresholds(overrides) {
3168
+ return {
3169
+ crashRate: overrides?.crashRate ?? DEFAULT_THRESHOLDS.crashRate,
3170
+ anrRate: overrides?.anrRate ?? DEFAULT_THRESHOLDS.anrRate,
3171
+ slowStartRate: overrides?.slowStartRate ?? DEFAULT_THRESHOLDS.slowStartRate,
3172
+ slowRenderingRate: overrides?.slowRenderingRate ?? DEFAULT_THRESHOLDS.slowRenderingRate
3173
+ };
3174
+ }
3167
3175
  function toVitalMetric(value, threshold, previousValue, trend) {
3168
3176
  const base = previousValue !== void 0 ? { value, threshold, status: "unknown", previousValue, trend: trend ?? null } : { value, threshold, status: "unknown" };
3169
3177
  if (value === void 0) return { ...base, status: "unknown" };
@@ -3210,12 +3218,7 @@ async function getAppStatus(client, reporting, packageName, options = {}) {
3210
3218
  const days = options.days ?? 7;
3211
3219
  const reviewDays = options.reviewDays ?? 30;
3212
3220
  const sections = new Set(options.sections ?? ["releases", "vitals", "reviews"]);
3213
- const thresholds = {
3214
- crashRate: options.vitalThresholds?.crashRate ?? DEFAULT_THRESHOLDS.crashRate,
3215
- anrRate: options.vitalThresholds?.anrRate ?? DEFAULT_THRESHOLDS.anrRate,
3216
- slowStartRate: options.vitalThresholds?.slowStartRate ?? DEFAULT_THRESHOLDS.slowStartRate,
3217
- slowRenderingRate: options.vitalThresholds?.slowRenderingRate ?? DEFAULT_THRESHOLDS.slowRenderingRate
3218
- };
3221
+ const thresholds = resolveStatusThresholds(options.vitalThresholds);
3219
3222
  const [
3220
3223
  releasesResult,
3221
3224
  crashesResult,
@@ -3592,6 +3595,22 @@ function sendNotification(title, body) {
3592
3595
  } catch {
3593
3596
  }
3594
3597
  }
3598
+ function applyVitalThresholds(status, vitalThresholds) {
3599
+ const v = status.vitals;
3600
+ if (!v) return status;
3601
+ const t = resolveStatusThresholds(vitalThresholds);
3602
+ const rate = (metric, threshold) => metric && toVitalMetric(metric.value, threshold, metric.previousValue, metric.trend);
3603
+ return {
3604
+ ...status,
3605
+ vitals: {
3606
+ ...v,
3607
+ crashes: rate(v.crashes, t.crashRate),
3608
+ anr: rate(v.anr, t.anrRate),
3609
+ slowStarts: rate(v.slowStarts, t.slowStartRate),
3610
+ slowRender: rate(v.slowRender, t.slowRenderingRate)
3611
+ }
3612
+ };
3613
+ }
3595
3614
  function statusHasBreach(status) {
3596
3615
  return status.vitals.crashes.status === "breach" || status.vitals.anr.status === "breach" || status.vitals.slowStarts.status === "breach" || status.vitals.slowRender.status === "breach";
3597
3616
  }
@@ -6285,6 +6304,7 @@ var RESOURCE_IDS = {
6285
6304
  16843376: "targetSdkVersion",
6286
6305
  16842782: "debuggable",
6287
6306
  16842786: "permission",
6307
+ 16842788: "value",
6288
6308
  16842795: "exported",
6289
6309
  16843378: "testOnly",
6290
6310
  16843760: "usesCleartextTraffic",
@@ -6413,6 +6433,7 @@ function extractManifestData(manifest) {
6413
6433
  const services = app ? extractComponents(app, "service") : [];
6414
6434
  const receivers = app ? extractComponents(app, "receiver") : [];
6415
6435
  const providers = app ? extractComponents(app, "provider") : [];
6436
+ const metaData = app ? extractMetaData(app) : {};
6416
6437
  return {
6417
6438
  packageName,
6418
6439
  versionCode,
@@ -6429,9 +6450,20 @@ function extractManifestData(manifest) {
6429
6450
  activities,
6430
6451
  services,
6431
6452
  receivers,
6432
- providers
6453
+ providers,
6454
+ metaData
6433
6455
  };
6434
6456
  }
6457
+ function extractMetaData(elem) {
6458
+ const entries = {};
6459
+ for (const el of getChildren(elem, "meta-data")) {
6460
+ const attrs = el.attribute || [];
6461
+ const name = getAttrByName(attrs, "name");
6462
+ const value = getAttrByName(attrs, "value");
6463
+ if (name && value !== void 0) entries[name] = value;
6464
+ }
6465
+ return entries;
6466
+ }
6435
6467
  function extractComponents(appElement, tagName) {
6436
6468
  return getChildren(appElement, tagName).map((el) => {
6437
6469
  const compAttrs = el.attribute || [];
@@ -6534,7 +6566,8 @@ function createFallbackManifest() {
6534
6566
  activities: [],
6535
6567
  services: [],
6536
6568
  receivers: [],
6537
- providers: []
6569
+ providers: [],
6570
+ metaData: {}
6538
6571
  };
6539
6572
  }
6540
6573
  function openAndScan(buffer, manifestPath = AAB_MANIFEST_PATH) {
@@ -6808,7 +6841,7 @@ var manifestScanner = {
6808
6841
  ruleId: "geofencing-foreground-service",
6809
6842
  severity: "warning",
6810
6843
  title: `Possible geofencing via foreground service "${service.name}"`,
6811
- message: `Service "${service.name}" uses foregroundServiceType "location" and the app declares ACCESS_BACKGROUND_LOCATION. Google Play no longer approves geofencing as a foreground service use case (sensitive-permissions policy announced April 15, 2026); enforcement for apps targeting Android 17 (API 37+) begins October 28, 2026.`,
6844
+ message: `Service "${service.name}" uses foregroundServiceType "location" and the app declares ACCESS_BACKGROUND_LOCATION. Google Play no longer approves geofencing as a foreground service use case (sensitive-permissions policy announced April 15, 2026).`,
6812
6845
  suggestion: 'If this service performs geofencing, migrate to the Geofencing API (LocationServices.getGeofencingClient) or WorkManager/AlarmManager. If this is legitimate background location tracking (navigation, fitness), suppress this rule via .preflightrc.json: "disabledRules": ["geofencing-foreground-service"].',
6813
6846
  policyUrl: "https://support.google.com/googleplay/android-developer/answer/16926792"
6814
6847
  });
@@ -6898,7 +6931,7 @@ var RESTRICTED_PERMISSIONS = [
6898
6931
  permission: "android.permission.READ_CALL_LOG",
6899
6932
  severity: "critical",
6900
6933
  title: "READ_CALL_LOG requires declaration form",
6901
- message: "READ_CALL_LOG is restricted to default phone/assistant handler apps. As of July 15, 2026, account verification via phone call is no longer an accepted use case for the Call Log permissions.",
6934
+ message: "READ_CALL_LOG is restricted to default phone/assistant handler apps. From January 27, 2027, account verification via phone call is no longer an accepted use case for the Call Log permissions.",
6902
6935
  suggestion: "Remove READ_CALL_LOG unless your app is a default phone or assistant handler. For phone-number verification, use the Digital Credentials API; for one-time codes, use the SMS Retriever API instead of reading the call log.",
6903
6936
  policyUrl: "https://support.google.com/googleplay/android-developer/answer/10208820"
6904
6937
  },
@@ -7041,14 +7074,14 @@ var permissionsScanner = {
7041
7074
  ].filter((p) => manifest.permissions.includes(p) && !allowed.has(p));
7042
7075
  if (contactsPerms.length > 0) {
7043
7076
  const names = contactsPerms.map((p) => p.split(".").pop()).join(", ");
7044
- const enforced = manifest.targetSdk >= 37 ? ` Your app targets API ${manifest.targetSdk}, so this is enforced.` : "";
7077
+ const enforced = manifest.targetSdk >= 37 ? ` Your app targets API ${manifest.targetSdk}, so this applies to it.` : "";
7045
7078
  findings.push({
7046
7079
  scanner: "permissions",
7047
7080
  ruleId: "contacts-permission-broad",
7048
7081
  severity: "warning",
7049
7082
  title: "Broad contacts access requires migration to Contact Picker",
7050
- message: `Your app declares ${names}. Google Play's Contacts Permissions policy requires the Android Contact Picker (Intent.ACTION_PICK_CONTACTS) unless broad access is core to your app. Mandatory for apps targeting Android 17 (API 37+) on October 28, 2026.${enforced}`,
7051
- suggestion: "Migrate to the Android Contact Picker (Intent.ACTION_PICK_CONTACTS) for user-initiated contact selection, or file a Permissions Declaration Form before October 28, 2026 if the picker cannot serve your core functionality. Remove READ_CONTACTS/WRITE_CONTACTS unless your app is a dialer, messaging, or contacts management app.",
7083
+ message: `Your app declares ${names}. Google Play's Contacts Permissions policy requires the Android Contact Picker (Intent.ACTION_PICK_CONTACTS) unless broad access is core to your app and you submit a Play Console declaration. Mandatory for apps targeting Android 17 (API 37+) on January 27, 2027.${enforced}`,
7084
+ suggestion: "Migrate to the Android Contact Picker (Intent.ACTION_PICK_CONTACTS) for user-initiated contact selection, or submit the contacts declaration in Play Console before January 27, 2027 if the picker cannot serve your core functionality. Remove READ_CONTACTS/WRITE_CONTACTS unless your app is a dialer, messaging, or contacts management app.",
7052
7085
  policyUrl: "https://support.google.com/googleplay/android-developer/answer/16935362"
7053
7086
  });
7054
7087
  }
@@ -7060,8 +7093,8 @@ var permissionsScanner = {
7060
7093
  ruleId: "location-minimal-scope",
7061
7094
  severity: targetsApi37 ? "warning" : "info",
7062
7095
  title: "Precise location \u2014 consider the location button (minimum scope)",
7063
- message: `Your app declares ACCESS_FINE_LOCATION. Google Play's Location Permissions policy makes the Android location button the recommended minimum scope for transactional (one-time) precise location. Mandatory for apps targeting Android 17 (API 37+) on October 28, 2026.${targetsApi37 ? ` Your app targets API ${manifest.targetSdk}.` : ""}`,
7064
- suggestion: 'For one-time/transactional precise location, use the system location button instead of requesting ACCESS_FINE_LOCATION up front. Only keep continuous fine location if navigation, fitness, or a similar feature is core functionality. Suppress via .preflightrc.json "disabledRules": ["location-minimal-scope"] if fine location is essential.',
7096
+ message: `Your app declares ACCESS_FINE_LOCATION. Google Play's Location Permissions policy requires the Android location button for one-time or infrequent precise location, and a Play Console declaration explaining why coarse location or the location button is not enough. Mandatory for apps targeting Android 17 (API 37+) on January 27, 2027.${targetsApi37 ? ` Your app targets API ${manifest.targetSdk}.` : ""}`,
7097
+ suggestion: 'For one-time/transactional precise location, use the system location button instead of requesting ACCESS_FINE_LOCATION up front. Only keep continuous fine location if navigation, fitness, or a similar feature is core functionality, and complete the precise location declaration in Play Console. Suppress via .preflightrc.json "disabledRules": ["location-minimal-scope"] if fine location is essential.',
7065
7098
  policyUrl: "https://support.google.com/googleplay/android-developer/answer/17033915"
7066
7099
  });
7067
7100
  }
@@ -7814,6 +7847,129 @@ var privacyScanner = {
7814
7847
  }
7815
7848
  };
7816
7849
 
7850
+ // src/preflight/billing-library.ts
7851
+ var BILLING_LIBRARY_DEADLINES = {
7852
+ 5: { updateDeadline: "2024-08-31", extensionDeadline: "2024-11-01" },
7853
+ 6: { updateDeadline: "2025-08-31", extensionDeadline: "2025-11-01" },
7854
+ 7: { updateDeadline: "2026-08-31", extensionDeadline: "2026-11-01" },
7855
+ 8: { updateDeadline: "2027-08-31", extensionDeadline: "2027-11-01" },
7856
+ 9: { updateDeadline: "2028-08-31", extensionDeadline: "2028-11-01" }
7857
+ };
7858
+ var BILLING_PERMISSION = "com.android.vending.BILLING";
7859
+ var VERSION_META_DATA = "com.google.android.play.billingclient.version";
7860
+ var DEPRECATION_FAQ = "https://developer.android.com/google/play/billing/deprecation-faq";
7861
+ var RELEASE_NOTES = "https://developer.android.com/google/play/billing/release-notes";
7862
+ var MIGRATION_GUIDES = {
7863
+ 8: "https://developer.android.com/google/play/billing/migrate-gpblv8",
7864
+ 9: "https://developer.android.com/google/play/billing/migrate-gpblv9"
7865
+ };
7866
+ var KNOWN_MAJORS = Object.keys(BILLING_LIBRARY_DEADLINES).map(Number).sort((a, b) => a - b);
7867
+ var OLDEST_KNOWN = KNOWN_MAJORS[0] ?? 0;
7868
+ var NEWEST_KNOWN = KNOWN_MAJORS[KNOWN_MAJORS.length - 1] ?? 0;
7869
+ var ISO_DAY_RE = /^(\d{4})-(\d{2})-(\d{2})$/;
7870
+ function utcDayStart(isoDay, { years = 0, days = 0 } = {}) {
7871
+ const match = ISO_DAY_RE.exec(isoDay);
7872
+ if (!match) throw new Error(`Invalid Billing Library deadline "${isoDay}" (expected YYYY-MM-DD)`);
7873
+ return Date.UTC(Number(match[1]) + years, Number(match[2]) - 1, Number(match[3]) + days);
7874
+ }
7875
+ var BOUNDARIES = new Map(
7876
+ Object.entries(BILLING_LIBRARY_DEADLINES).map(([key, { updateDeadline, extensionDeadline }]) => {
7877
+ const major = Number(key);
7878
+ return [
7879
+ major,
7880
+ {
7881
+ infoFrom: utcDayStart(updateDeadline, { years: -1 }),
7882
+ updatePastFrom: utcDayStart(updateDeadline, { days: 1 }),
7883
+ extensionPastFrom: utcDayStart(extensionDeadline, { days: 1 })
7884
+ }
7885
+ ];
7886
+ })
7887
+ );
7888
+ var STATUS_SEVERITY = {
7889
+ "older-than-table": "error",
7890
+ "past-extension-deadline": "error",
7891
+ "past-update-deadline": "warning",
7892
+ "update-deadline-within-a-year": "info"
7893
+ };
7894
+ function billingLibraryStatus(major, now) {
7895
+ if (major < OLDEST_KNOWN) return "older-than-table";
7896
+ const bounds = BOUNDARIES.get(major);
7897
+ if (!bounds) return void 0;
7898
+ const t = now.getTime();
7899
+ if (t >= bounds.extensionPastFrom) return "past-extension-deadline";
7900
+ if (t >= bounds.updatePastFrom) return "past-update-deadline";
7901
+ if (t >= bounds.infoFrom) return "update-deadline-within-a-year";
7902
+ return void 0;
7903
+ }
7904
+ function targetMajor(major, now) {
7905
+ const next = KNOWN_MAJORS.find((m) => {
7906
+ const bounds = BOUNDARIES.get(m);
7907
+ return m > major && bounds !== void 0 && now.getTime() < bounds.updatePastFrom;
7908
+ });
7909
+ return next ?? NEWEST_KNOWN + 1;
7910
+ }
7911
+ function migrationAdvice(target) {
7912
+ const guide = MIGRATION_GUIDES[target];
7913
+ return guide ? `Move to Billing Library ${target} or later. Migration guide: ${guide}` : `Move to Billing Library ${target} or later. Release notes: ${RELEASE_NOTES}`;
7914
+ }
7915
+ function billingLibraryFindings(manifest, now) {
7916
+ if (!manifest.permissions.includes(BILLING_PERMISSION)) return [];
7917
+ const version = manifest.metaData[VERSION_META_DATA];
7918
+ const majorMatch = version?.trim().match(/^\d+/);
7919
+ if (!version || !majorMatch) {
7920
+ const found = version ? `has an unreadable value ("${version}")` : "is missing";
7921
+ return [
7922
+ {
7923
+ scanner: "policy",
7924
+ ruleId: "billing-library-version-unknown",
7925
+ severity: "info",
7926
+ title: "Google Play Billing Library version could not be determined",
7927
+ message: `This bundle requests ${BILLING_PERMISSION}, but the ${VERSION_META_DATA} manifest entry ${found}. Manifest merging can remove it (for example a tools:node="remove" override), so preflight cannot compare the Billing Library version with Google Play's deadlines.`,
7928
+ suggestion: `Check the Billing Library version in your Gradle dependencies against Google's support timeline: ${DEPRECATION_FAQ}`,
7929
+ policyUrl: DEPRECATION_FAQ
7930
+ }
7931
+ ];
7932
+ }
7933
+ const major = Number(majorMatch[0]);
7934
+ const status = billingLibraryStatus(major, now);
7935
+ if (!status) return [];
7936
+ const shipsVersion = `This bundle ships Google Play Billing Library ${version}.`;
7937
+ const advice = migrationAdvice(targetMajor(major, now));
7938
+ let title;
7939
+ let message;
7940
+ let suggestion = advice;
7941
+ if (status === "older-than-table") {
7942
+ const oldest = BILLING_LIBRARY_DEADLINES[OLDEST_KNOWN];
7943
+ title = `Google Play Billing Library ${major} is no longer supported`;
7944
+ message = `${shipsVersion} It is older than Billing Library ${OLDEST_KNOWN}, whose extension deadline passed on ${oldest?.extensionDeadline ?? "an earlier date"}. Google Play no longer accepts new apps or app updates built on it.`;
7945
+ } else {
7946
+ const deadlines = BILLING_LIBRARY_DEADLINES[major];
7947
+ if (!deadlines) return [];
7948
+ if (status === "past-extension-deadline") {
7949
+ title = `Google Play Billing Library ${major} is no longer supported`;
7950
+ message = `${shipsVersion} Its extension deadline passed on ${deadlines.extensionDeadline}, so Google Play no longer accepts new apps or app updates built on Billing Library ${major}.`;
7951
+ } else if (status === "past-update-deadline") {
7952
+ title = `Google Play Billing Library ${major} is past its update deadline`;
7953
+ message = `${shipsVersion} Its update deadline passed on ${deadlines.updateDeadline}. Until ${deadlines.extensionDeadline}, Google Play accepts new apps and app updates on Billing Library ${major} only if the app was granted an extension. After that date it accepts none.`;
7954
+ suggestion = `To keep shipping while you migrate, request an extension from the warning on Play Console's Policy status page. ${advice}`;
7955
+ } else {
7956
+ title = `Google Play Billing Library ${major} update deadline is ${deadlines.updateDeadline}`;
7957
+ message = `${shipsVersion} After ${deadlines.updateDeadline}, Google Play accepts new apps and app updates on Billing Library ${major} only with an extension, and after ${deadlines.extensionDeadline} not at all.`;
7958
+ }
7959
+ }
7960
+ return [
7961
+ {
7962
+ scanner: "policy",
7963
+ ruleId: "billing-library-deprecated",
7964
+ severity: STATUS_SEVERITY[status],
7965
+ title,
7966
+ message,
7967
+ suggestion,
7968
+ policyUrl: DEPRECATION_FAQ
7969
+ }
7970
+ ];
7971
+ }
7972
+
7817
7973
  // src/preflight/scanners/policy-scanner.ts
7818
7974
  var policyScanner = {
7819
7975
  name: "policy",
@@ -7912,6 +8068,18 @@ var policyScanner = {
7912
8068
  policyUrl: "https://support.google.com/googleplay/android-developer/answer/13392821"
7913
8069
  });
7914
8070
  }
8071
+ if (perms.has("android.permission.ACCESS_LOCAL_NETWORK") && manifest.targetSdk >= 37) {
8072
+ findings.push({
8073
+ scanner: "policy",
8074
+ ruleId: "policy-local-network-declaration",
8075
+ severity: "info",
8076
+ title: "ACCESS_LOCAL_NETWORK requires a Play Console declaration",
8077
+ message: `This app targets API ${manifest.targetSdk} and requests ACCESS_LOCAL_NETWORK. From August 25, 2027, Google Play requires apps targeting Android 17 (API 37) or higher to submit a Play Console declaration to use this permission, and allows it only for core functionality that device pickers cannot provide. The declaration is stored in Play Console, so it cannot be verified from the AAB.`,
8078
+ suggestion: "If a device picker (for example NsdManager discovery through the system picker) covers your use case, use it and drop ACCESS_LOCAL_NETWORK. Otherwise submit the local network declaration in Play Console, explaining why device pickers are not sufficient.",
8079
+ policyUrl: "https://support.google.com/googleplay/android-developer/answer/16935362#local_network_permission"
8080
+ });
8081
+ }
8082
+ findings.push(...billingLibraryFindings(manifest, /* @__PURE__ */ new Date()));
7915
8083
  if (perms.has("android.permission.SYSTEM_ALERT_WINDOW")) {
7916
8084
  findings.push({
7917
8085
  scanner: "policy",
@@ -10529,6 +10697,7 @@ export {
10529
10697
  analyzeReviews2 as analyzeReviews,
10530
10698
  annotateListResult,
10531
10699
  applyReleaseNotes,
10700
+ applyVitalThresholds,
10532
10701
  assertRateThreshold,
10533
10702
  assignRelease,
10534
10703
  batchGetOrders,