@gpc-cli/api 1.0.24 → 1.0.26
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/README.md +1 -1
- package/dist/index.d.ts +116 -1
- package/dist/index.js +208 -74
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Typed Google Play Developer API v3 client for TypeScript. Part of [GPC](https://github.com/yasserstudio/gpc).
|
|
4
4
|
|
|
5
|
-
The entire Google Play Developer API in one typed client —
|
|
5
|
+
The entire Google Play Developer API in one typed client — 204 endpoints covering edits, releases, tracks, listings, images, subscriptions, in-app products, purchases, reviews, vitals, reports, users, and testers. Built-in rate limiting, retry logic, and pagination. Works with your existing service account — no new credentials required.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
package/dist/index.d.ts
CHANGED
|
@@ -7,7 +7,20 @@ interface RateLimitBucket {
|
|
|
7
7
|
interface RateLimiter {
|
|
8
8
|
acquire(bucket: string): Promise<void>;
|
|
9
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* Google Play Developer API quota model (as of 2025):
|
|
12
|
+
* - 200,000 queries per day total
|
|
13
|
+
* - 6 independent per-minute buckets at 3,000 queries/min each
|
|
14
|
+
*
|
|
15
|
+
* The per-minute buckets are the practical constraint for CLI usage.
|
|
16
|
+
* Daily limits are tracked locally via gpc quota.
|
|
17
|
+
*/
|
|
10
18
|
declare const RATE_LIMIT_BUCKETS: Record<string, RateLimitBucket>;
|
|
19
|
+
/**
|
|
20
|
+
* Map an API path to the appropriate rate-limit bucket.
|
|
21
|
+
* Google's quota is structured by resource type.
|
|
22
|
+
*/
|
|
23
|
+
declare function resolveBucket(path: string): string;
|
|
11
24
|
declare function createRateLimiter(buckets?: RateLimitBucket[]): RateLimiter;
|
|
12
25
|
|
|
13
26
|
interface RetryLogEntry {
|
|
@@ -127,6 +140,10 @@ interface ApkInfo {
|
|
|
127
140
|
sha256: string;
|
|
128
141
|
};
|
|
129
142
|
}
|
|
143
|
+
interface ApksListResponse {
|
|
144
|
+
apks: ApkInfo[];
|
|
145
|
+
kind?: string;
|
|
146
|
+
}
|
|
130
147
|
interface DeobfuscationFile {
|
|
131
148
|
symbolType: string;
|
|
132
149
|
}
|
|
@@ -508,12 +525,14 @@ interface SubscriptionDeferResponse {
|
|
|
508
525
|
newExpiryTimeMillis: string;
|
|
509
526
|
}
|
|
510
527
|
interface VoidedPurchase {
|
|
528
|
+
kind?: string;
|
|
511
529
|
purchaseToken: string;
|
|
512
530
|
purchaseTimeMillis: string;
|
|
513
531
|
voidedTimeMillis: string;
|
|
514
532
|
orderId: string;
|
|
515
533
|
voidedSource: number;
|
|
516
534
|
voidedReason: number;
|
|
535
|
+
voidedQuantity?: number;
|
|
517
536
|
}
|
|
518
537
|
interface VoidedPurchasesListResponse {
|
|
519
538
|
voidedPurchases: VoidedPurchase[];
|
|
@@ -922,6 +941,45 @@ interface ResumableUploadOptions {
|
|
|
922
941
|
/** Maximum resume attempts per chunk before giving up. Default: 5 */
|
|
923
942
|
maxResumeAttempts?: number;
|
|
924
943
|
}
|
|
944
|
+
interface ReleaseSummary {
|
|
945
|
+
releaseName?: string;
|
|
946
|
+
track: string;
|
|
947
|
+
activeArtifacts?: {
|
|
948
|
+
versionCode: number;
|
|
949
|
+
}[];
|
|
950
|
+
releaseLifecycleState?: 'RELEASE_LIFECYCLE_STATE_UNSPECIFIED' | 'DRAFT' | 'NOT_SENT_FOR_REVIEW' | 'IN_REVIEW' | 'APPROVED_NOT_PUBLISHED' | 'NOT_APPROVED' | 'PUBLISHED';
|
|
951
|
+
}
|
|
952
|
+
interface ReleasesListResponse {
|
|
953
|
+
releases: ReleaseSummary[];
|
|
954
|
+
nextPageToken?: string;
|
|
955
|
+
}
|
|
956
|
+
interface SubscriptionsBatchGetRequest {
|
|
957
|
+
productIds: string[];
|
|
958
|
+
}
|
|
959
|
+
interface SubscriptionsBatchGetResponse {
|
|
960
|
+
subscriptions: Subscription[];
|
|
961
|
+
}
|
|
962
|
+
interface SubscriptionsBatchUpdateRequest {
|
|
963
|
+
requests: {
|
|
964
|
+
subscription: Subscription;
|
|
965
|
+
updateMask?: string;
|
|
966
|
+
regionsVersion?: {
|
|
967
|
+
version?: string;
|
|
968
|
+
};
|
|
969
|
+
allowMissing?: boolean;
|
|
970
|
+
latencyTolerance?: string;
|
|
971
|
+
}[];
|
|
972
|
+
}
|
|
973
|
+
interface SubscriptionsBatchUpdateResponse {
|
|
974
|
+
subscriptions: Subscription[];
|
|
975
|
+
}
|
|
976
|
+
interface InAppProductsBatchDeleteRequest {
|
|
977
|
+
requests: {
|
|
978
|
+
packageName: string;
|
|
979
|
+
sku: string;
|
|
980
|
+
latencyTolerance?: string;
|
|
981
|
+
}[];
|
|
982
|
+
}
|
|
925
983
|
|
|
926
984
|
interface PlayApiClient {
|
|
927
985
|
edits: {
|
|
@@ -945,8 +1003,14 @@ interface PlayApiClient {
|
|
|
945
1003
|
get(packageName: string, editId: string, track: string): Promise<Track>;
|
|
946
1004
|
create(packageName: string, editId: string, trackName: string): Promise<Track>;
|
|
947
1005
|
update(packageName: string, editId: string, track: string, release: Release): Promise<Track>;
|
|
1006
|
+
patch(packageName: string, editId: string, track: string, release: Release): Promise<Track>;
|
|
1007
|
+
};
|
|
1008
|
+
releases: {
|
|
1009
|
+
list(packageName: string, track: string): Promise<ReleaseSummary[]>;
|
|
948
1010
|
};
|
|
949
1011
|
apks: {
|
|
1012
|
+
list(packageName: string, editId: string): Promise<ApkInfo[]>;
|
|
1013
|
+
upload(packageName: string, editId: string, filePath: string, uploadOptions?: ResumableUploadOptions): Promise<ApkInfo>;
|
|
950
1014
|
addExternallyHosted(packageName: string, editId: string, data: ExternallyHostedApk): Promise<ExternallyHostedApkResponse>;
|
|
951
1015
|
};
|
|
952
1016
|
listings: {
|
|
@@ -984,6 +1048,8 @@ interface PlayApiClient {
|
|
|
984
1048
|
create(packageName: string, data: Subscription, productId?: string): Promise<Subscription>;
|
|
985
1049
|
update(packageName: string, productId: string, data: Subscription, updateMask?: string, regionsVersion?: string): Promise<Subscription>;
|
|
986
1050
|
delete(packageName: string, productId: string): Promise<void>;
|
|
1051
|
+
batchGet(packageName: string, productIds: string[]): Promise<Subscription[]>;
|
|
1052
|
+
batchUpdate(packageName: string, requests: SubscriptionsBatchUpdateRequest): Promise<SubscriptionsBatchUpdateResponse>;
|
|
987
1053
|
activateBasePlan(packageName: string, productId: string, basePlanId: string): Promise<Subscription>;
|
|
988
1054
|
deactivateBasePlan(packageName: string, productId: string, basePlanId: string): Promise<Subscription>;
|
|
989
1055
|
deleteBasePlan(packageName: string, productId: string, basePlanId: string): Promise<void>;
|
|
@@ -995,6 +1061,38 @@ interface PlayApiClient {
|
|
|
995
1061
|
deleteOffer(packageName: string, productId: string, basePlanId: string, offerId: string): Promise<void>;
|
|
996
1062
|
activateOffer(packageName: string, productId: string, basePlanId: string, offerId: string): Promise<SubscriptionOffer>;
|
|
997
1063
|
deactivateOffer(packageName: string, productId: string, basePlanId: string, offerId: string): Promise<SubscriptionOffer>;
|
|
1064
|
+
batchUpdateBasePlanStates(packageName: string, productId: string, requests: {
|
|
1065
|
+
requests: Array<{
|
|
1066
|
+
activateBasePlanRequest?: {
|
|
1067
|
+
basePlanId: string;
|
|
1068
|
+
};
|
|
1069
|
+
deactivateBasePlanRequest?: {
|
|
1070
|
+
basePlanId: string;
|
|
1071
|
+
};
|
|
1072
|
+
}>;
|
|
1073
|
+
}): Promise<Subscription>;
|
|
1074
|
+
batchGetOffers(packageName: string, productId: string, basePlanId: string, offerIds: string[]): Promise<{
|
|
1075
|
+
subscriptionOffers: SubscriptionOffer[];
|
|
1076
|
+
}>;
|
|
1077
|
+
batchUpdateOffers(packageName: string, productId: string, basePlanId: string, requests: {
|
|
1078
|
+
requests: Array<{
|
|
1079
|
+
subscriptionOffer: Partial<SubscriptionOffer>;
|
|
1080
|
+
updateMask?: string;
|
|
1081
|
+
regionsVersion?: string;
|
|
1082
|
+
}>;
|
|
1083
|
+
}): Promise<{
|
|
1084
|
+
subscriptionOffers: SubscriptionOffer[];
|
|
1085
|
+
}>;
|
|
1086
|
+
batchUpdateOfferStates(packageName: string, productId: string, basePlanId: string, requests: {
|
|
1087
|
+
requests: Array<{
|
|
1088
|
+
activateSubscriptionOfferRequest?: {
|
|
1089
|
+
offerId: string;
|
|
1090
|
+
};
|
|
1091
|
+
deactivateSubscriptionOfferRequest?: {
|
|
1092
|
+
offerId: string;
|
|
1093
|
+
};
|
|
1094
|
+
}>;
|
|
1095
|
+
}): Promise<Subscription>;
|
|
998
1096
|
};
|
|
999
1097
|
inappproducts: {
|
|
1000
1098
|
list(packageName: string, options?: {
|
|
@@ -1012,6 +1110,7 @@ interface PlayApiClient {
|
|
|
1012
1110
|
delete(packageName: string, sku: string): Promise<void>;
|
|
1013
1111
|
batchUpdate(packageName: string, requests: InAppProductsBatchUpdateRequest): Promise<InAppProductsBatchUpdateResponse>;
|
|
1014
1112
|
batchGet(packageName: string, skus: string[]): Promise<InAppProduct[]>;
|
|
1113
|
+
batchDelete(packageName: string, skus: string[]): Promise<void>;
|
|
1015
1114
|
};
|
|
1016
1115
|
purchases: {
|
|
1017
1116
|
getProduct(packageName: string, productId: string, token: string): Promise<ProductPurchase>;
|
|
@@ -1023,6 +1122,9 @@ interface PlayApiClient {
|
|
|
1023
1122
|
getSubscriptionV1(packageName: string, subscriptionId: string, token: string): Promise<SubscriptionPurchase>;
|
|
1024
1123
|
cancelSubscription(packageName: string, subscriptionId: string, token: string): Promise<void>;
|
|
1025
1124
|
deferSubscription(packageName: string, subscriptionId: string, token: string, body: SubscriptionDeferRequest): Promise<SubscriptionDeferResponse>;
|
|
1125
|
+
acknowledgeSubscription(packageName: string, subscriptionId: string, token: string, body?: {
|
|
1126
|
+
developerPayload?: string;
|
|
1127
|
+
}): Promise<void>;
|
|
1026
1128
|
revokeSubscriptionV2(packageName: string, token: string): Promise<void>;
|
|
1027
1129
|
refundSubscriptionV2(packageName: string, token: string): Promise<void>;
|
|
1028
1130
|
/** V2 cancel with cancellationType support. (Sep 2025) */
|
|
@@ -1034,6 +1136,8 @@ interface PlayApiClient {
|
|
|
1034
1136
|
listVoided(packageName: string, options?: {
|
|
1035
1137
|
startTime?: string;
|
|
1036
1138
|
endTime?: string;
|
|
1139
|
+
type?: number;
|
|
1140
|
+
includeQuantityBasedPartialRefund?: boolean;
|
|
1037
1141
|
maxResults?: number;
|
|
1038
1142
|
token?: string;
|
|
1039
1143
|
}): Promise<VoidedPurchasesListResponse>;
|
|
@@ -1088,6 +1192,17 @@ interface PlayApiClient {
|
|
|
1088
1192
|
createOffer(packageName: string, productId: string, offer: OneTimeOffer): Promise<OneTimeOffer>;
|
|
1089
1193
|
updateOffer(packageName: string, productId: string, offerId: string, offer: Partial<OneTimeOffer>, updateMask?: string, regionsVersion?: string): Promise<OneTimeOffer>;
|
|
1090
1194
|
deleteOffer(packageName: string, productId: string, offerId: string): Promise<void>;
|
|
1195
|
+
batchGet(packageName: string, productIds: string[]): Promise<OneTimeProduct[]>;
|
|
1196
|
+
batchUpdate(packageName: string, requests: {
|
|
1197
|
+
requests: Array<{
|
|
1198
|
+
oneTimeProduct: Partial<OneTimeProduct>;
|
|
1199
|
+
updateMask?: string;
|
|
1200
|
+
regionsVersion?: string;
|
|
1201
|
+
}>;
|
|
1202
|
+
}): Promise<{
|
|
1203
|
+
oneTimeProducts: OneTimeProduct[];
|
|
1204
|
+
}>;
|
|
1205
|
+
batchDelete(packageName: string, productIds: string[]): Promise<void>;
|
|
1091
1206
|
};
|
|
1092
1207
|
purchaseOptions: {
|
|
1093
1208
|
list(packageName: string): Promise<PurchaseOptionsListResponse>;
|
|
@@ -1280,4 +1395,4 @@ declare class PlayApiError extends Error {
|
|
|
1280
1395
|
/** Files below this threshold use simple upload instead. */
|
|
1281
1396
|
declare const RESUMABLE_THRESHOLD: number;
|
|
1282
1397
|
|
|
1283
|
-
export { type Achievement, type Anomaly, type AnomalyDetectionResponse, type ApiClientOptions, type ApiResponse, type ApkInfo, type AppDetails, type AppEdit, type AppRecoveriesListResponse, type AppRecoveryAction, type AppRecoveryTargeting, type BasePlan, type BasePlanMigratePricesRequest, type BatchGetOrdersResponse, type Bundle, type BundleListResponse, type ConvertRegionPricesRequest, type ConvertRegionPricesResponse, type ConvertedRegionPrice, type CountryAvailability, type CreateAppRecoveryActionRequest, type CustomApp, type CustomAppsListResponse, type DataSafety, type DataSafetyDataType, type DataSafetyPurpose, type DeobfuscationFile, type DeobfuscationUploadResponse, type DeveloperComment, type DeveloperPermission, type DeviceGroup, type DeviceSelector, type DeviceTier, type DeviceTierConfig, type DeviceTierConfigsListResponse, type EnterpriseApiClient, type ErrorIssue, type ErrorIssuesResponse, type ErrorReport, type ErrorReportsResponse, type ExternalTransaction, type ExternalTransactionAmount, type ExternalTransactionRefund, type ExternallyHostedApk, type ExternallyHostedApkResponse, type GameEvent, type GamesApiClient, type GeneratedApk, type GeneratedApksPerVersion, type Grant, type GrantsListResponse, type HttpClient, type Image, type ImageType, type ImageUploadResponse, type ImagesDeleteAllResponse, type ImagesListResponse, type InAppProduct, type InAppProductListing, type InAppProductsBatchGetRequest, type InAppProductsBatchUpdateRequest, type InAppProductsBatchUpdateResponse, type InAppProductsListResponse, type InternalAppSharingArtifact, type Leaderboard, type LeaderboardScore, type Listing, type ListingsListResponse, type MetricRow, type MetricSetQuery, type MetricSetResponse, type Money, type OffersListResponse, type OneTimeOffer, type OneTimeOfferRegionalConfig, type OneTimeOffersListResponse, type OneTimeProduct, type OneTimeProductListing, type OneTimeProductsListResponse, type Order, type OrderLineItem, type PagedResponse, type PaginateOptions, type PlayApiClient, PlayApiError, type ProductPurchase, type ProductPurchaseLineItem, type ProductPurchaseV2, type PurchaseOption, type PurchaseOptionsListResponse, RATE_LIMIT_BUCKETS, RESUMABLE_THRESHOLD, type RateLimitBucket, type RateLimiter, type RegionalBasePlanConfig, type Release, type ReleaseNote, type ReleaseStatus, type ReportBucket, type ReportType, type ReportingAggregation, type ReportingApiClient, type ReportingDimension, type ReportsListResponse, type ResumableUploadOptions, type RetryLogEntry, type Review, type ReviewComment, type ReviewReplyRequest, type ReviewReplyResponse, type ReviewsListOptions, type ReviewsListResponse, type StatsDimension, type Subscription, type SubscriptionDeferRequest, type SubscriptionDeferResponse, type SubscriptionListing, type SubscriptionOffer, type SubscriptionOfferPhase, type SubscriptionPurchase, type SubscriptionPurchaseLineItem, type SubscriptionPurchaseV2, type SubscriptionsListResponse, type SubscriptionsV2CancelRequest, type SubscriptionsV2DeferRequest, type SubscriptionsV2DeferResponse, type TaxAndComplianceSettings, type Testers, type TokenPagination, type Track, type TrackListResponse, type UploadProgressEvent, type UploadResponse, type User, type UserComment, type UsersApiClient, type UsersListResponse, type VitalsMetricSet, type VoidedPurchase, type VoidedPurchasesListResponse, createApiClient, createEnterpriseClient, createGamesClient, createHttpClient, createRateLimiter, createReportingClient, createUsersClient, paginate, paginateAll, paginateParallel };
|
|
1398
|
+
export { type Achievement, type Anomaly, type AnomalyDetectionResponse, type ApiClientOptions, type ApiResponse, type ApkInfo, type ApksListResponse, type AppDetails, type AppEdit, type AppRecoveriesListResponse, type AppRecoveryAction, type AppRecoveryTargeting, type BasePlan, type BasePlanMigratePricesRequest, type BatchGetOrdersResponse, type Bundle, type BundleListResponse, type ConvertRegionPricesRequest, type ConvertRegionPricesResponse, type ConvertedRegionPrice, type CountryAvailability, type CreateAppRecoveryActionRequest, type CustomApp, type CustomAppsListResponse, type DataSafety, type DataSafetyDataType, type DataSafetyPurpose, type DeobfuscationFile, type DeobfuscationUploadResponse, type DeveloperComment, type DeveloperPermission, type DeviceGroup, type DeviceSelector, type DeviceTier, type DeviceTierConfig, type DeviceTierConfigsListResponse, type EnterpriseApiClient, type ErrorIssue, type ErrorIssuesResponse, type ErrorReport, type ErrorReportsResponse, type ExternalTransaction, type ExternalTransactionAmount, type ExternalTransactionRefund, type ExternallyHostedApk, type ExternallyHostedApkResponse, type GameEvent, type GamesApiClient, type GeneratedApk, type GeneratedApksPerVersion, type Grant, type GrantsListResponse, type HttpClient, type Image, type ImageType, type ImageUploadResponse, type ImagesDeleteAllResponse, type ImagesListResponse, type InAppProduct, type InAppProductListing, type InAppProductsBatchDeleteRequest, type InAppProductsBatchGetRequest, type InAppProductsBatchUpdateRequest, type InAppProductsBatchUpdateResponse, type InAppProductsListResponse, type InternalAppSharingArtifact, type Leaderboard, type LeaderboardScore, type Listing, type ListingsListResponse, type MetricRow, type MetricSetQuery, type MetricSetResponse, type Money, type OffersListResponse, type OneTimeOffer, type OneTimeOfferRegionalConfig, type OneTimeOffersListResponse, type OneTimeProduct, type OneTimeProductListing, type OneTimeProductsListResponse, type Order, type OrderLineItem, type PagedResponse, type PaginateOptions, type PlayApiClient, PlayApiError, type ProductPurchase, type ProductPurchaseLineItem, type ProductPurchaseV2, type PurchaseOption, type PurchaseOptionsListResponse, RATE_LIMIT_BUCKETS, RESUMABLE_THRESHOLD, type RateLimitBucket, type RateLimiter, type RegionalBasePlanConfig, type Release, type ReleaseNote, type ReleaseStatus, type ReleaseSummary, type ReleasesListResponse, type ReportBucket, type ReportType, type ReportingAggregation, type ReportingApiClient, type ReportingDimension, type ReportsListResponse, type ResumableUploadOptions, type RetryLogEntry, type Review, type ReviewComment, type ReviewReplyRequest, type ReviewReplyResponse, type ReviewsListOptions, type ReviewsListResponse, type StatsDimension, type Subscription, type SubscriptionDeferRequest, type SubscriptionDeferResponse, type SubscriptionListing, type SubscriptionOffer, type SubscriptionOfferPhase, type SubscriptionPurchase, type SubscriptionPurchaseLineItem, type SubscriptionPurchaseV2, type SubscriptionsBatchGetRequest, type SubscriptionsBatchGetResponse, type SubscriptionsBatchUpdateRequest, type SubscriptionsBatchUpdateResponse, type SubscriptionsListResponse, type SubscriptionsV2CancelRequest, type SubscriptionsV2DeferRequest, type SubscriptionsV2DeferResponse, type TaxAndComplianceSettings, type Testers, type TokenPagination, type Track, type TrackListResponse, type UploadProgressEvent, type UploadResponse, type User, type UserComment, type UsersApiClient, type UsersListResponse, type VitalsMetricSet, type VoidedPurchase, type VoidedPurchasesListResponse, createApiClient, createEnterpriseClient, createGamesClient, createHttpClient, createRateLimiter, createReportingClient, createUsersClient, paginate, paginateAll, paginateParallel, resolveBucket };
|
package/dist/index.js
CHANGED
|
@@ -733,7 +733,8 @@ function createHttpClient(options) {
|
|
|
733
733
|
return Math.max(timeout, 3e4 + Math.ceil(sizeMb) * 1e3);
|
|
734
734
|
}
|
|
735
735
|
async function uploadRequest(path, filePath, contentType, baseUrl = UPLOAD_BASE_URL) {
|
|
736
|
-
const
|
|
736
|
+
const separator = path.includes("?") ? "&" : "?";
|
|
737
|
+
const url = `${baseUrl}${path}${separator}uploadType=media`;
|
|
737
738
|
const safeFilePath = validateFilePath(filePath);
|
|
738
739
|
const fileBuffer = await readFile(safeFilePath);
|
|
739
740
|
const effectiveTimeout = computeUploadTimeout(fileBuffer.byteLength);
|
|
@@ -942,13 +943,99 @@ function createHttpClient(options) {
|
|
|
942
943
|
};
|
|
943
944
|
}
|
|
944
945
|
|
|
946
|
+
// src/rate-limiter.ts
|
|
947
|
+
var RATE_LIMIT_BUCKETS = {
|
|
948
|
+
edits: { name: "edits", maxTokens: 3e3, refillRate: 3e3, refillIntervalMs: 6e4 },
|
|
949
|
+
purchases: { name: "purchases", maxTokens: 3e3, refillRate: 3e3, refillIntervalMs: 6e4 },
|
|
950
|
+
reviews: { name: "reviews", maxTokens: 3e3, refillRate: 3e3, refillIntervalMs: 6e4 },
|
|
951
|
+
reporting: { name: "reporting", maxTokens: 3e3, refillRate: 3e3, refillIntervalMs: 6e4 },
|
|
952
|
+
monetization: { name: "monetization", maxTokens: 3e3, refillRate: 3e3, refillIntervalMs: 6e4 },
|
|
953
|
+
default: { name: "default", maxTokens: 3e3, refillRate: 3e3, refillIntervalMs: 6e4 }
|
|
954
|
+
};
|
|
955
|
+
function resolveBucket(path) {
|
|
956
|
+
if (path.includes("/edits/") || path.includes("/edits:")) return "edits";
|
|
957
|
+
if (path.includes("/purchases/") || path.includes("/orders")) return "purchases";
|
|
958
|
+
if (path.includes("/reviews")) return "reviews";
|
|
959
|
+
if (path.includes("playdeveloperreporting") || path.includes("MetricSet") || path.includes("anomalies")) return "reporting";
|
|
960
|
+
if (path.includes("/inappproducts") || path.includes("/oneTimeProducts") || path.includes("/subscriptions") || path.includes("/monetization")) return "monetization";
|
|
961
|
+
return "default";
|
|
962
|
+
}
|
|
963
|
+
function createRateLimiter(buckets) {
|
|
964
|
+
const states = /* @__PURE__ */ new Map();
|
|
965
|
+
const effectiveBuckets = buckets ?? Object.values(RATE_LIMIT_BUCKETS);
|
|
966
|
+
for (const bucket of effectiveBuckets) {
|
|
967
|
+
states.set(bucket.name, {
|
|
968
|
+
tokens: bucket.maxTokens,
|
|
969
|
+
lastRefillTime: Date.now(),
|
|
970
|
+
config: bucket
|
|
971
|
+
});
|
|
972
|
+
}
|
|
973
|
+
return {
|
|
974
|
+
async acquire(bucket) {
|
|
975
|
+
const state = states.get(bucket);
|
|
976
|
+
if (!state) return;
|
|
977
|
+
const now = Date.now();
|
|
978
|
+
const elapsed = now - state.lastRefillTime;
|
|
979
|
+
const refill = Math.floor(
|
|
980
|
+
elapsed / state.config.refillIntervalMs * state.config.refillRate
|
|
981
|
+
);
|
|
982
|
+
if (refill > 0) {
|
|
983
|
+
state.tokens = Math.min(state.config.maxTokens, state.tokens + refill);
|
|
984
|
+
state.lastRefillTime = now;
|
|
985
|
+
}
|
|
986
|
+
if (state.tokens > 0) {
|
|
987
|
+
state.tokens--;
|
|
988
|
+
return;
|
|
989
|
+
}
|
|
990
|
+
const tokensNeeded = 1;
|
|
991
|
+
const waitMs = Math.ceil(
|
|
992
|
+
tokensNeeded / state.config.refillRate * state.config.refillIntervalMs
|
|
993
|
+
);
|
|
994
|
+
await new Promise((r) => setTimeout(r, waitMs));
|
|
995
|
+
const afterWait = Date.now();
|
|
996
|
+
const totalElapsed = afterWait - state.lastRefillTime;
|
|
997
|
+
const newTokens = Math.floor(
|
|
998
|
+
totalElapsed / state.config.refillIntervalMs * state.config.refillRate
|
|
999
|
+
);
|
|
1000
|
+
state.tokens = Math.max(0, Math.min(state.config.maxTokens, newTokens) - 1);
|
|
1001
|
+
state.lastRefillTime = afterWait;
|
|
1002
|
+
}
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
|
|
945
1006
|
// src/client.ts
|
|
946
|
-
async function
|
|
947
|
-
if (limiter)
|
|
1007
|
+
async function autoRateLimit(limiter, path) {
|
|
1008
|
+
if (!limiter) return;
|
|
1009
|
+
const bucket = resolveBucket(path);
|
|
1010
|
+
await limiter.acquire(bucket);
|
|
948
1011
|
}
|
|
949
1012
|
function createApiClient(options) {
|
|
950
|
-
const
|
|
951
|
-
const
|
|
1013
|
+
const rawHttp = createHttpClient(options);
|
|
1014
|
+
const defaultLimiter = createRateLimiter();
|
|
1015
|
+
const limiter = options.rateLimiter || defaultLimiter;
|
|
1016
|
+
const http = {
|
|
1017
|
+
...rawHttp,
|
|
1018
|
+
async get(path, params) {
|
|
1019
|
+
await autoRateLimit(limiter, path);
|
|
1020
|
+
return rawHttp.get(path, params);
|
|
1021
|
+
},
|
|
1022
|
+
async post(path, body) {
|
|
1023
|
+
await autoRateLimit(limiter, path);
|
|
1024
|
+
return rawHttp.post(path, body);
|
|
1025
|
+
},
|
|
1026
|
+
async put(path, body) {
|
|
1027
|
+
await autoRateLimit(limiter, path);
|
|
1028
|
+
return rawHttp.put(path, body);
|
|
1029
|
+
},
|
|
1030
|
+
async patch(path, body) {
|
|
1031
|
+
await autoRateLimit(limiter, path);
|
|
1032
|
+
return rawHttp.patch(path, body);
|
|
1033
|
+
},
|
|
1034
|
+
async delete(path) {
|
|
1035
|
+
await autoRateLimit(limiter, path);
|
|
1036
|
+
return rawHttp.delete(path);
|
|
1037
|
+
}
|
|
1038
|
+
};
|
|
952
1039
|
return {
|
|
953
1040
|
edits: {
|
|
954
1041
|
async insert(packageName) {
|
|
@@ -1039,9 +1126,47 @@ function createApiClient(options) {
|
|
|
1039
1126
|
releases: [release]
|
|
1040
1127
|
});
|
|
1041
1128
|
return data;
|
|
1129
|
+
},
|
|
1130
|
+
async patch(packageName, editId, track, release) {
|
|
1131
|
+
const { data } = await http.patch(`/${packageName}/edits/${editId}/tracks/${track}`, {
|
|
1132
|
+
track,
|
|
1133
|
+
releases: [release]
|
|
1134
|
+
});
|
|
1135
|
+
return data;
|
|
1136
|
+
}
|
|
1137
|
+
},
|
|
1138
|
+
releases: {
|
|
1139
|
+
async list(packageName, track) {
|
|
1140
|
+
const { data } = await http.get(
|
|
1141
|
+
`/${packageName}/tracks/${track}/releases`
|
|
1142
|
+
);
|
|
1143
|
+
return data.releases ?? [];
|
|
1042
1144
|
}
|
|
1043
1145
|
},
|
|
1044
1146
|
apks: {
|
|
1147
|
+
async list(packageName, editId) {
|
|
1148
|
+
const { data } = await http.get(
|
|
1149
|
+
`/${packageName}/edits/${editId}/apks`
|
|
1150
|
+
);
|
|
1151
|
+
return data.apks || [];
|
|
1152
|
+
},
|
|
1153
|
+
async upload(packageName, editId, filePath, uploadOptions) {
|
|
1154
|
+
const { data } = await http.uploadResumable(
|
|
1155
|
+
`/${packageName}/edits/${editId}/apks`,
|
|
1156
|
+
filePath,
|
|
1157
|
+
"application/vnd.android.package-archive",
|
|
1158
|
+
uploadOptions
|
|
1159
|
+
);
|
|
1160
|
+
if (!data || !data.versionCode) {
|
|
1161
|
+
throw new PlayApiError(
|
|
1162
|
+
"Upload succeeded but no APK data returned",
|
|
1163
|
+
"API_EMPTY_RESPONSE",
|
|
1164
|
+
200,
|
|
1165
|
+
"This is unexpected. Retry the upload or contact Google Play support if the issue persists."
|
|
1166
|
+
);
|
|
1167
|
+
}
|
|
1168
|
+
return data;
|
|
1169
|
+
},
|
|
1045
1170
|
async addExternallyHosted(packageName, editId, apkData) {
|
|
1046
1171
|
const { data } = await http.post(
|
|
1047
1172
|
`/${packageName}/edits/${editId}/apks/externallyHosted`,
|
|
@@ -1139,7 +1264,6 @@ function createApiClient(options) {
|
|
|
1139
1264
|
},
|
|
1140
1265
|
reviews: {
|
|
1141
1266
|
async list(packageName, options2) {
|
|
1142
|
-
await rateLimit(limiter, "reviewsGet");
|
|
1143
1267
|
const params = {};
|
|
1144
1268
|
if (options2?.token) params["token"] = options2.token;
|
|
1145
1269
|
if (options2?.maxResults) params["maxResults"] = String(options2.maxResults);
|
|
@@ -1153,7 +1277,6 @@ function createApiClient(options) {
|
|
|
1153
1277
|
return data;
|
|
1154
1278
|
},
|
|
1155
1279
|
async get(packageName, reviewId, translationLanguage) {
|
|
1156
|
-
await rateLimit(limiter, "reviewsGet");
|
|
1157
1280
|
const params = {};
|
|
1158
1281
|
if (translationLanguage) params["translationLanguage"] = translationLanguage;
|
|
1159
1282
|
const hasParams = Object.keys(params).length > 0;
|
|
@@ -1164,7 +1287,6 @@ function createApiClient(options) {
|
|
|
1164
1287
|
return data;
|
|
1165
1288
|
},
|
|
1166
1289
|
async reply(packageName, reviewId, replyText) {
|
|
1167
|
-
await rateLimit(limiter, "reviewsPost");
|
|
1168
1290
|
const body = { replyText };
|
|
1169
1291
|
const { data } = await http.post(
|
|
1170
1292
|
`/${packageName}/reviews/${reviewId}:reply`,
|
|
@@ -1208,6 +1330,20 @@ function createApiClient(options) {
|
|
|
1208
1330
|
async delete(packageName, productId) {
|
|
1209
1331
|
await http.delete(`/${packageName}/subscriptions/${productId}`);
|
|
1210
1332
|
},
|
|
1333
|
+
async batchGet(packageName, productIds) {
|
|
1334
|
+
const params = productIds.map((id) => `productIds=${encodeURIComponent(id)}`).join("&");
|
|
1335
|
+
const { data } = await http.get(
|
|
1336
|
+
`/${packageName}/subscriptions:batchGet?${params}`
|
|
1337
|
+
);
|
|
1338
|
+
return data.subscriptions ?? [];
|
|
1339
|
+
},
|
|
1340
|
+
async batchUpdate(packageName, requests) {
|
|
1341
|
+
const { data } = await http.post(
|
|
1342
|
+
`/${packageName}/subscriptions:batchUpdate`,
|
|
1343
|
+
requests
|
|
1344
|
+
);
|
|
1345
|
+
return data;
|
|
1346
|
+
},
|
|
1211
1347
|
async activateBasePlan(packageName, productId, basePlanId) {
|
|
1212
1348
|
const { data } = await http.post(
|
|
1213
1349
|
`/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}:activate`
|
|
@@ -1274,6 +1410,34 @@ function createApiClient(options) {
|
|
|
1274
1410
|
`/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}/offers/${offerId}:deactivate`
|
|
1275
1411
|
);
|
|
1276
1412
|
return data;
|
|
1413
|
+
},
|
|
1414
|
+
async batchUpdateBasePlanStates(packageName, productId, requests) {
|
|
1415
|
+
const { data } = await http.post(
|
|
1416
|
+
`/${packageName}/subscriptions/${productId}/basePlans:batchUpdateStates`,
|
|
1417
|
+
requests
|
|
1418
|
+
);
|
|
1419
|
+
return data;
|
|
1420
|
+
},
|
|
1421
|
+
async batchGetOffers(packageName, productId, basePlanId, offerIds) {
|
|
1422
|
+
const { data } = await http.post(
|
|
1423
|
+
`/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}/offers:batchGet`,
|
|
1424
|
+
{ requests: offerIds.map((id) => ({ offerId: id })) }
|
|
1425
|
+
);
|
|
1426
|
+
return data;
|
|
1427
|
+
},
|
|
1428
|
+
async batchUpdateOffers(packageName, productId, basePlanId, requests) {
|
|
1429
|
+
const { data } = await http.post(
|
|
1430
|
+
`/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}/offers:batchUpdate`,
|
|
1431
|
+
requests
|
|
1432
|
+
);
|
|
1433
|
+
return data;
|
|
1434
|
+
},
|
|
1435
|
+
async batchUpdateOfferStates(packageName, productId, basePlanId, requests) {
|
|
1436
|
+
const { data } = await http.post(
|
|
1437
|
+
`/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}/offers:batchUpdateStates`,
|
|
1438
|
+
requests
|
|
1439
|
+
);
|
|
1440
|
+
return data;
|
|
1277
1441
|
}
|
|
1278
1442
|
},
|
|
1279
1443
|
inappproducts: {
|
|
@@ -1328,6 +1492,12 @@ function createApiClient(options) {
|
|
|
1328
1492
|
Object.keys(params).length > 0 ? params : void 0
|
|
1329
1493
|
);
|
|
1330
1494
|
return data.inappproduct || [];
|
|
1495
|
+
},
|
|
1496
|
+
async batchDelete(packageName, skus) {
|
|
1497
|
+
await http.post(
|
|
1498
|
+
`/${packageName}/inappproducts:batchDelete`,
|
|
1499
|
+
{ requests: skus.map((sku) => ({ packageName, sku })) }
|
|
1500
|
+
);
|
|
1331
1501
|
}
|
|
1332
1502
|
},
|
|
1333
1503
|
purchases: {
|
|
@@ -1376,6 +1546,12 @@ function createApiClient(options) {
|
|
|
1376
1546
|
);
|
|
1377
1547
|
return data;
|
|
1378
1548
|
},
|
|
1549
|
+
async acknowledgeSubscription(packageName, subscriptionId, token, body) {
|
|
1550
|
+
await http.post(
|
|
1551
|
+
`/${packageName}/purchases/subscriptions/${subscriptionId}/tokens/${token}:acknowledge`,
|
|
1552
|
+
body ?? {}
|
|
1553
|
+
);
|
|
1554
|
+
},
|
|
1379
1555
|
async revokeSubscriptionV2(packageName, token) {
|
|
1380
1556
|
await http.post(`/${packageName}/purchases/subscriptionsv2/tokens/${token}:revoke`);
|
|
1381
1557
|
},
|
|
@@ -1402,11 +1578,11 @@ function createApiClient(options) {
|
|
|
1402
1578
|
return data;
|
|
1403
1579
|
},
|
|
1404
1580
|
async listVoided(packageName, options2) {
|
|
1405
|
-
await rateLimit(limiter, "voidedBurst");
|
|
1406
|
-
await rateLimit(limiter, "voidedDaily");
|
|
1407
1581
|
const params = {};
|
|
1408
1582
|
if (options2?.startTime) params["startTime"] = options2.startTime;
|
|
1409
1583
|
if (options2?.endTime) params["endTime"] = options2.endTime;
|
|
1584
|
+
if (options2?.type !== void 0) params["type"] = String(options2.type);
|
|
1585
|
+
if (options2?.includeQuantityBasedPartialRefund) params["includeQuantityBasedPartialRefund"] = "true";
|
|
1410
1586
|
if (options2?.maxResults) params["maxResults"] = String(options2.maxResults);
|
|
1411
1587
|
if (options2?.token) params["token"] = options2.token;
|
|
1412
1588
|
const hasParams = Object.keys(params).length > 0;
|
|
@@ -1622,6 +1798,26 @@ function createApiClient(options) {
|
|
|
1622
1798
|
},
|
|
1623
1799
|
async deleteOffer(packageName, productId, offerId) {
|
|
1624
1800
|
await http.delete(`/${packageName}/oneTimeProducts/${productId}/offers/${offerId}`);
|
|
1801
|
+
},
|
|
1802
|
+
async batchGet(packageName, productIds) {
|
|
1803
|
+
const params = productIds.map((id) => `productIds=${encodeURIComponent(id)}`).join("&");
|
|
1804
|
+
const { data } = await http.get(
|
|
1805
|
+
`/${packageName}/oneTimeProducts:batchGet?${params}`
|
|
1806
|
+
);
|
|
1807
|
+
return data.oneTimeProducts || [];
|
|
1808
|
+
},
|
|
1809
|
+
async batchUpdate(packageName, requests) {
|
|
1810
|
+
const { data } = await http.post(
|
|
1811
|
+
`/${packageName}/oneTimeProducts:batchUpdate`,
|
|
1812
|
+
requests
|
|
1813
|
+
);
|
|
1814
|
+
return data;
|
|
1815
|
+
},
|
|
1816
|
+
async batchDelete(packageName, productIds) {
|
|
1817
|
+
await http.post(
|
|
1818
|
+
`/${packageName}/oneTimeProducts:batchDelete`,
|
|
1819
|
+
{ requests: productIds.map((id) => ({ productId: id })) }
|
|
1820
|
+
);
|
|
1625
1821
|
}
|
|
1626
1822
|
},
|
|
1627
1823
|
purchaseOptions: {
|
|
@@ -1686,69 +1882,6 @@ function createApiClient(options) {
|
|
|
1686
1882
|
};
|
|
1687
1883
|
}
|
|
1688
1884
|
|
|
1689
|
-
// src/rate-limiter.ts
|
|
1690
|
-
var RATE_LIMIT_BUCKETS = {
|
|
1691
|
-
default: { name: "default", maxTokens: 200, refillRate: 200, refillIntervalMs: 1e3 },
|
|
1692
|
-
reviewsGet: { name: "reviewsGet", maxTokens: 200, refillRate: 200, refillIntervalMs: 36e5 },
|
|
1693
|
-
reviewsPost: {
|
|
1694
|
-
name: "reviewsPost",
|
|
1695
|
-
maxTokens: 2e3,
|
|
1696
|
-
refillRate: 2e3,
|
|
1697
|
-
refillIntervalMs: 864e5
|
|
1698
|
-
},
|
|
1699
|
-
voidedBurst: { name: "voidedBurst", maxTokens: 30, refillRate: 30, refillIntervalMs: 3e4 },
|
|
1700
|
-
voidedDaily: {
|
|
1701
|
-
name: "voidedDaily",
|
|
1702
|
-
maxTokens: 6e3,
|
|
1703
|
-
refillRate: 6e3,
|
|
1704
|
-
refillIntervalMs: 864e5
|
|
1705
|
-
},
|
|
1706
|
-
reporting: { name: "reporting", maxTokens: 10, refillRate: 10, refillIntervalMs: 1e3 }
|
|
1707
|
-
};
|
|
1708
|
-
function createRateLimiter(buckets) {
|
|
1709
|
-
const states = /* @__PURE__ */ new Map();
|
|
1710
|
-
if (buckets) {
|
|
1711
|
-
for (const bucket of buckets) {
|
|
1712
|
-
states.set(bucket.name, {
|
|
1713
|
-
tokens: bucket.maxTokens,
|
|
1714
|
-
lastRefillTime: Date.now(),
|
|
1715
|
-
config: bucket
|
|
1716
|
-
});
|
|
1717
|
-
}
|
|
1718
|
-
}
|
|
1719
|
-
return {
|
|
1720
|
-
async acquire(bucket) {
|
|
1721
|
-
const state = states.get(bucket);
|
|
1722
|
-
if (!state) return;
|
|
1723
|
-
const now = Date.now();
|
|
1724
|
-
const elapsed = now - state.lastRefillTime;
|
|
1725
|
-
const refill = Math.floor(
|
|
1726
|
-
elapsed / state.config.refillIntervalMs * state.config.refillRate
|
|
1727
|
-
);
|
|
1728
|
-
if (refill > 0) {
|
|
1729
|
-
state.tokens = Math.min(state.config.maxTokens, state.tokens + refill);
|
|
1730
|
-
state.lastRefillTime = now;
|
|
1731
|
-
}
|
|
1732
|
-
if (state.tokens > 0) {
|
|
1733
|
-
state.tokens--;
|
|
1734
|
-
return;
|
|
1735
|
-
}
|
|
1736
|
-
const tokensNeeded = 1;
|
|
1737
|
-
const waitMs = Math.ceil(
|
|
1738
|
-
tokensNeeded / state.config.refillRate * state.config.refillIntervalMs
|
|
1739
|
-
);
|
|
1740
|
-
await new Promise((r) => setTimeout(r, waitMs));
|
|
1741
|
-
const afterWait = Date.now();
|
|
1742
|
-
const totalElapsed = afterWait - state.lastRefillTime;
|
|
1743
|
-
const newTokens = Math.floor(
|
|
1744
|
-
totalElapsed / state.config.refillIntervalMs * state.config.refillRate
|
|
1745
|
-
);
|
|
1746
|
-
state.tokens = Math.min(state.config.maxTokens, newTokens) - 1;
|
|
1747
|
-
state.lastRefillTime = afterWait;
|
|
1748
|
-
}
|
|
1749
|
-
};
|
|
1750
|
-
}
|
|
1751
|
-
|
|
1752
1885
|
// src/reporting-client.ts
|
|
1753
1886
|
var REPORTING_BASE_URL = "https://playdeveloperreporting.googleapis.com/v1beta1";
|
|
1754
1887
|
function createReportingClient(options) {
|
|
@@ -1997,6 +2130,7 @@ export {
|
|
|
1997
2130
|
createUsersClient,
|
|
1998
2131
|
paginate,
|
|
1999
2132
|
paginateAll,
|
|
2000
|
-
paginateParallel
|
|
2133
|
+
paginateParallel,
|
|
2134
|
+
resolveBucket
|
|
2001
2135
|
};
|
|
2002
2136
|
//# sourceMappingURL=index.js.map
|