@gpc-cli/api 1.0.25 → 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 +67 -1
- package/dist/index.js +167 -74
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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[];
|
|
@@ -990,6 +1009,8 @@ interface PlayApiClient {
|
|
|
990
1009
|
list(packageName: string, track: string): Promise<ReleaseSummary[]>;
|
|
991
1010
|
};
|
|
992
1011
|
apks: {
|
|
1012
|
+
list(packageName: string, editId: string): Promise<ApkInfo[]>;
|
|
1013
|
+
upload(packageName: string, editId: string, filePath: string, uploadOptions?: ResumableUploadOptions): Promise<ApkInfo>;
|
|
993
1014
|
addExternallyHosted(packageName: string, editId: string, data: ExternallyHostedApk): Promise<ExternallyHostedApkResponse>;
|
|
994
1015
|
};
|
|
995
1016
|
listings: {
|
|
@@ -1040,6 +1061,38 @@ interface PlayApiClient {
|
|
|
1040
1061
|
deleteOffer(packageName: string, productId: string, basePlanId: string, offerId: string): Promise<void>;
|
|
1041
1062
|
activateOffer(packageName: string, productId: string, basePlanId: string, offerId: string): Promise<SubscriptionOffer>;
|
|
1042
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>;
|
|
1043
1096
|
};
|
|
1044
1097
|
inappproducts: {
|
|
1045
1098
|
list(packageName: string, options?: {
|
|
@@ -1083,6 +1136,8 @@ interface PlayApiClient {
|
|
|
1083
1136
|
listVoided(packageName: string, options?: {
|
|
1084
1137
|
startTime?: string;
|
|
1085
1138
|
endTime?: string;
|
|
1139
|
+
type?: number;
|
|
1140
|
+
includeQuantityBasedPartialRefund?: boolean;
|
|
1086
1141
|
maxResults?: number;
|
|
1087
1142
|
token?: string;
|
|
1088
1143
|
}): Promise<VoidedPurchasesListResponse>;
|
|
@@ -1137,6 +1192,17 @@ interface PlayApiClient {
|
|
|
1137
1192
|
createOffer(packageName: string, productId: string, offer: OneTimeOffer): Promise<OneTimeOffer>;
|
|
1138
1193
|
updateOffer(packageName: string, productId: string, offerId: string, offer: Partial<OneTimeOffer>, updateMask?: string, regionsVersion?: string): Promise<OneTimeOffer>;
|
|
1139
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>;
|
|
1140
1206
|
};
|
|
1141
1207
|
purchaseOptions: {
|
|
1142
1208
|
list(packageName: string): Promise<PurchaseOptionsListResponse>;
|
|
@@ -1329,4 +1395,4 @@ declare class PlayApiError extends Error {
|
|
|
1329
1395
|
/** Files below this threshold use simple upload instead. */
|
|
1330
1396
|
declare const RESUMABLE_THRESHOLD: number;
|
|
1331
1397
|
|
|
1332
|
-
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 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 };
|
|
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) {
|
|
@@ -1057,6 +1144,29 @@ function createApiClient(options) {
|
|
|
1057
1144
|
}
|
|
1058
1145
|
},
|
|
1059
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
|
+
},
|
|
1060
1170
|
async addExternallyHosted(packageName, editId, apkData) {
|
|
1061
1171
|
const { data } = await http.post(
|
|
1062
1172
|
`/${packageName}/edits/${editId}/apks/externallyHosted`,
|
|
@@ -1154,7 +1264,6 @@ function createApiClient(options) {
|
|
|
1154
1264
|
},
|
|
1155
1265
|
reviews: {
|
|
1156
1266
|
async list(packageName, options2) {
|
|
1157
|
-
await rateLimit(limiter, "reviewsGet");
|
|
1158
1267
|
const params = {};
|
|
1159
1268
|
if (options2?.token) params["token"] = options2.token;
|
|
1160
1269
|
if (options2?.maxResults) params["maxResults"] = String(options2.maxResults);
|
|
@@ -1168,7 +1277,6 @@ function createApiClient(options) {
|
|
|
1168
1277
|
return data;
|
|
1169
1278
|
},
|
|
1170
1279
|
async get(packageName, reviewId, translationLanguage) {
|
|
1171
|
-
await rateLimit(limiter, "reviewsGet");
|
|
1172
1280
|
const params = {};
|
|
1173
1281
|
if (translationLanguage) params["translationLanguage"] = translationLanguage;
|
|
1174
1282
|
const hasParams = Object.keys(params).length > 0;
|
|
@@ -1179,7 +1287,6 @@ function createApiClient(options) {
|
|
|
1179
1287
|
return data;
|
|
1180
1288
|
},
|
|
1181
1289
|
async reply(packageName, reviewId, replyText) {
|
|
1182
|
-
await rateLimit(limiter, "reviewsPost");
|
|
1183
1290
|
const body = { replyText };
|
|
1184
1291
|
const { data } = await http.post(
|
|
1185
1292
|
`/${packageName}/reviews/${reviewId}:reply`,
|
|
@@ -1303,6 +1410,34 @@ function createApiClient(options) {
|
|
|
1303
1410
|
`/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}/offers/${offerId}:deactivate`
|
|
1304
1411
|
);
|
|
1305
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;
|
|
1306
1441
|
}
|
|
1307
1442
|
},
|
|
1308
1443
|
inappproducts: {
|
|
@@ -1443,11 +1578,11 @@ function createApiClient(options) {
|
|
|
1443
1578
|
return data;
|
|
1444
1579
|
},
|
|
1445
1580
|
async listVoided(packageName, options2) {
|
|
1446
|
-
await rateLimit(limiter, "voidedBurst");
|
|
1447
|
-
await rateLimit(limiter, "voidedDaily");
|
|
1448
1581
|
const params = {};
|
|
1449
1582
|
if (options2?.startTime) params["startTime"] = options2.startTime;
|
|
1450
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";
|
|
1451
1586
|
if (options2?.maxResults) params["maxResults"] = String(options2.maxResults);
|
|
1452
1587
|
if (options2?.token) params["token"] = options2.token;
|
|
1453
1588
|
const hasParams = Object.keys(params).length > 0;
|
|
@@ -1663,6 +1798,26 @@ function createApiClient(options) {
|
|
|
1663
1798
|
},
|
|
1664
1799
|
async deleteOffer(packageName, productId, offerId) {
|
|
1665
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
|
+
);
|
|
1666
1821
|
}
|
|
1667
1822
|
},
|
|
1668
1823
|
purchaseOptions: {
|
|
@@ -1727,69 +1882,6 @@ function createApiClient(options) {
|
|
|
1727
1882
|
};
|
|
1728
1883
|
}
|
|
1729
1884
|
|
|
1730
|
-
// src/rate-limiter.ts
|
|
1731
|
-
var RATE_LIMIT_BUCKETS = {
|
|
1732
|
-
default: { name: "default", maxTokens: 200, refillRate: 200, refillIntervalMs: 1e3 },
|
|
1733
|
-
reviewsGet: { name: "reviewsGet", maxTokens: 200, refillRate: 200, refillIntervalMs: 36e5 },
|
|
1734
|
-
reviewsPost: {
|
|
1735
|
-
name: "reviewsPost",
|
|
1736
|
-
maxTokens: 2e3,
|
|
1737
|
-
refillRate: 2e3,
|
|
1738
|
-
refillIntervalMs: 864e5
|
|
1739
|
-
},
|
|
1740
|
-
voidedBurst: { name: "voidedBurst", maxTokens: 30, refillRate: 30, refillIntervalMs: 3e4 },
|
|
1741
|
-
voidedDaily: {
|
|
1742
|
-
name: "voidedDaily",
|
|
1743
|
-
maxTokens: 6e3,
|
|
1744
|
-
refillRate: 6e3,
|
|
1745
|
-
refillIntervalMs: 864e5
|
|
1746
|
-
},
|
|
1747
|
-
reporting: { name: "reporting", maxTokens: 10, refillRate: 10, refillIntervalMs: 1e3 }
|
|
1748
|
-
};
|
|
1749
|
-
function createRateLimiter(buckets) {
|
|
1750
|
-
const states = /* @__PURE__ */ new Map();
|
|
1751
|
-
if (buckets) {
|
|
1752
|
-
for (const bucket of buckets) {
|
|
1753
|
-
states.set(bucket.name, {
|
|
1754
|
-
tokens: bucket.maxTokens,
|
|
1755
|
-
lastRefillTime: Date.now(),
|
|
1756
|
-
config: bucket
|
|
1757
|
-
});
|
|
1758
|
-
}
|
|
1759
|
-
}
|
|
1760
|
-
return {
|
|
1761
|
-
async acquire(bucket) {
|
|
1762
|
-
const state = states.get(bucket);
|
|
1763
|
-
if (!state) return;
|
|
1764
|
-
const now = Date.now();
|
|
1765
|
-
const elapsed = now - state.lastRefillTime;
|
|
1766
|
-
const refill = Math.floor(
|
|
1767
|
-
elapsed / state.config.refillIntervalMs * state.config.refillRate
|
|
1768
|
-
);
|
|
1769
|
-
if (refill > 0) {
|
|
1770
|
-
state.tokens = Math.min(state.config.maxTokens, state.tokens + refill);
|
|
1771
|
-
state.lastRefillTime = now;
|
|
1772
|
-
}
|
|
1773
|
-
if (state.tokens > 0) {
|
|
1774
|
-
state.tokens--;
|
|
1775
|
-
return;
|
|
1776
|
-
}
|
|
1777
|
-
const tokensNeeded = 1;
|
|
1778
|
-
const waitMs = Math.ceil(
|
|
1779
|
-
tokensNeeded / state.config.refillRate * state.config.refillIntervalMs
|
|
1780
|
-
);
|
|
1781
|
-
await new Promise((r) => setTimeout(r, waitMs));
|
|
1782
|
-
const afterWait = Date.now();
|
|
1783
|
-
const totalElapsed = afterWait - state.lastRefillTime;
|
|
1784
|
-
const newTokens = Math.floor(
|
|
1785
|
-
totalElapsed / state.config.refillIntervalMs * state.config.refillRate
|
|
1786
|
-
);
|
|
1787
|
-
state.tokens = Math.min(state.config.maxTokens, newTokens) - 1;
|
|
1788
|
-
state.lastRefillTime = afterWait;
|
|
1789
|
-
}
|
|
1790
|
-
};
|
|
1791
|
-
}
|
|
1792
|
-
|
|
1793
1885
|
// src/reporting-client.ts
|
|
1794
1886
|
var REPORTING_BASE_URL = "https://playdeveloperreporting.googleapis.com/v1beta1";
|
|
1795
1887
|
function createReportingClient(options) {
|
|
@@ -2038,6 +2130,7 @@ export {
|
|
|
2038
2130
|
createUsersClient,
|
|
2039
2131
|
paginate,
|
|
2040
2132
|
paginateAll,
|
|
2041
|
-
paginateParallel
|
|
2133
|
+
paginateParallel,
|
|
2134
|
+
resolveBucket
|
|
2042
2135
|
};
|
|
2043
2136
|
//# sourceMappingURL=index.js.map
|