@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 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 — 187 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.
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 url = `${baseUrl}${path}`;
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 rateLimit(limiter, bucket) {
947
- if (limiter) await limiter.acquire(bucket);
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 http = createHttpClient(options);
951
- const limiter = options.rateLimiter || void 0;
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