@gpc-cli/api 1.0.27 → 1.0.28

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 — 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.
5
+ 208 endpoints across edits, releases, tracks, listings, subscriptions, in-app products, purchases, reviews, vitals, reports, users, and testers. Built-in rate limiting, retry logic, and pagination.
6
6
 
7
7
  ## Install
8
8
 
@@ -19,204 +19,53 @@ import { resolveAuth } from "@gpc-cli/auth";
19
19
  const auth = await resolveAuth({
20
20
  serviceAccountPath: "./service-account.json",
21
21
  });
22
-
23
22
  const client = createApiClient({ auth });
24
23
 
25
- // List all tracks
26
24
  const edit = await client.edits.insert("com.example.app");
27
25
  const tracks = await client.tracks.list("com.example.app", edit.id);
28
26
  console.log(tracks);
29
-
30
27
  await client.edits.delete("com.example.app", edit.id);
31
28
  ```
32
29
 
33
30
  ## Client Factories
34
31
 
35
- | Factory | Purpose |
36
- | -------------------------------- | ------------------------------------------------------------------ |
37
- | `createApiClient(options)` | Core Play API -- apps, releases, listings, monetization, purchases |
38
- | `createReportingClient(options)` | Vitals, crash rates, ANR, error reporting |
39
- | `createUsersClient(options)` | Developer account users and permission grants |
40
- | `createHttpClient(options)` | Low-level HTTP with auth, retry, and rate limiting |
41
-
42
- All factories accept `ApiClientOptions`:
32
+ | Factory | Purpose |
33
+ | ------- | ------- |
34
+ | `createApiClient(options)` | Core Play API: apps, releases, listings, monetization, purchases |
35
+ | `createReportingClient(options)` | Vitals, crash rates, ANR, error reporting |
36
+ | `createUsersClient(options)` | Developer account users and permission grants |
37
+ | `createHttpClient(options)` | Low-level HTTP with auth, retry, and rate limiting |
43
38
 
44
39
  ```typescript
45
- import type { ApiClientOptions } from "@gpc-cli/api";
46
-
47
40
  const options: ApiClientOptions = {
48
- auth, // Required: { getAccessToken(): Promise<string> }
49
- maxRetries: 3, // Default retry count
50
- timeout: 30_000, // Request timeout in ms
51
- rateLimiter: undefined, // Optional custom rate limiter
52
- onRetry: (entry) => console.warn(`Retry #${entry.attempt}: ${entry.error}`),
41
+ auth, // Required: { getAccessToken(): Promise<string> }
42
+ maxRetries: 3, // Default retry count
43
+ timeout: 30_000, // Request timeout in ms
44
+ onRetry: (entry) => console.warn(`Retry #${entry.attempt}`),
53
45
  };
54
46
  ```
55
47
 
56
- ## API Modules
57
-
58
- ### Edits
59
-
60
- Every modification to app metadata, tracks, or listings requires an edit session.
61
-
62
- ```typescript
63
- const edit = await client.edits.insert("com.example.app");
64
-
65
- // ... make changes within the edit ...
66
-
67
- await client.edits.validate("com.example.app", edit.id);
68
- await client.edits.commit("com.example.app", edit.id);
69
- ```
48
+ ## Common Workflows
70
49
 
71
- ### Bundles
50
+ ### Upload and release
72
51
 
73
52
  ```typescript
74
53
  const edit = await client.edits.insert("com.example.app");
75
- const bundle = await client.bundles.upload("com.example.app", edit.id, "./app.aab");
76
- const bundles = await client.bundles.list("com.example.app", edit.id);
77
- await client.edits.commit("com.example.app", edit.id);
78
- ```
79
-
80
- ### Tracks & Releases
81
-
82
- ```typescript
83
- const edit = await client.edits.insert("com.example.app");
84
-
85
- const tracks = await client.tracks.list("com.example.app", edit.id);
86
- const production = await client.tracks.get("com.example.app", edit.id, "production");
87
-
88
- await client.tracks.update("com.example.app", edit.id, "production", {
54
+ await client.bundles.upload("com.example.app", edit.id, "./app.aab");
55
+ await client.tracks.update("com.example.app", edit.id, "beta", {
89
56
  versionCodes: ["42"],
90
- status: "inProgress",
91
- userFraction: 0.1,
57
+ status: "completed",
92
58
  releaseNotes: [{ language: "en-US", text: "Bug fixes" }],
93
59
  });
94
-
95
- await client.edits.commit("com.example.app", edit.id);
96
- ```
97
-
98
- ### Listings
99
-
100
- ```typescript
101
- const edit = await client.edits.insert("com.example.app");
102
-
103
- const listings = await client.listings.list("com.example.app", edit.id);
104
- const en = await client.listings.get("com.example.app", edit.id, "en-US");
105
-
106
- await client.listings.update("com.example.app", edit.id, "en-US", {
107
- title: "My App",
108
- shortDescription: "A great app",
109
- fullDescription: "Full description here...",
110
- });
111
-
112
- await client.edits.commit("com.example.app", edit.id);
113
- ```
114
-
115
- ### Images
116
-
117
- ```typescript
118
- const edit = await client.edits.insert("com.example.app");
119
-
120
- const screenshots = await client.images.list(
121
- "com.example.app",
122
- edit.id,
123
- "en-US",
124
- "phoneScreenshots",
125
- );
126
-
127
- await client.images.upload("com.example.app", edit.id, "en-US", "featureGraphic", "./feature.png");
128
-
129
- await client.images.deleteAll("com.example.app", edit.id, "en-US", "phoneScreenshots");
130
60
  await client.edits.commit("com.example.app", edit.id);
131
61
  ```
132
62
 
133
- Image types: `phoneScreenshots`, `sevenInchScreenshots`, `tenInchScreenshots`, `tvScreenshots`, `wearScreenshots`, `icon`, `featureGraphic`, `tvBanner`.
134
-
135
- ### Subscriptions
136
-
137
- ```typescript
138
- const { subscriptions } = await client.subscriptions.list("com.example.app");
139
- const sub = await client.subscriptions.get("com.example.app", "premium_monthly");
140
-
141
- await client.subscriptions.activateBasePlan("com.example.app", "premium_monthly", "p1m");
142
- await client.subscriptions.deactivateBasePlan("com.example.app", "premium_monthly", "p1m");
143
- ```
144
-
145
- ### Subscription Offers
146
-
147
- ```typescript
148
- const { subscriptionOffers } = await client.subscriptions.listOffers(
149
- "com.example.app",
150
- "premium_monthly",
151
- "p1m",
152
- );
153
-
154
- const offer = await client.subscriptions.getOffer(
155
- "com.example.app",
156
- "premium_monthly",
157
- "p1m",
158
- "intro_offer",
159
- );
160
-
161
- await client.subscriptions.activateOffer(
162
- "com.example.app",
163
- "premium_monthly",
164
- "p1m",
165
- "intro_offer",
166
- );
167
- ```
168
-
169
- ### In-App Products
170
-
171
- ```typescript
172
- const { inappproduct } = await client.inappproducts.list("com.example.app");
173
- const product = await client.inappproducts.get("com.example.app", "coins_100");
174
-
175
- await client.inappproducts.create("com.example.app", {
176
- sku: "coins_500",
177
- status: "active",
178
- purchaseType: "managedUser",
179
- defaultPrice: { currencyCode: "USD", units: "4", nanos: 990_000_000 },
180
- });
181
- ```
182
-
183
- ### Purchases
184
-
185
- ```typescript
186
- // Verify a product purchase
187
- const purchase = await client.purchases.getProduct("com.example.app", "coins_100", purchaseToken);
188
-
189
- // Acknowledge it
190
- await client.purchases.acknowledgeProduct("com.example.app", "coins_100", purchaseToken);
191
-
192
- // Verify a subscription (v2)
193
- const sub = await client.purchases.getSubscriptionV2("com.example.app", purchaseToken);
194
-
195
- // List voided purchases
196
- const { voidedPurchases } = await client.purchases.listVoided("com.example.app", {
197
- startTime: "1700000000000",
198
- maxResults: 100,
199
- });
200
- ```
201
-
202
- ### Reviews
203
-
204
- ```typescript
205
- const { reviews } = await client.reviews.list("com.example.app", { maxResults: 50 });
206
- const review = await client.reviews.get("com.example.app", reviewId);
207
- await client.reviews.reply("com.example.app", reviewId, "Thanks for the feedback!");
208
- ```
209
-
210
- ### Vitals & Error Reporting
211
-
212
- Uses a separate client that targets the Play Developer Reporting API.
63
+ ### Query crash rates
213
64
 
214
65
  ```typescript
215
66
  import { createReportingClient } from "@gpc-cli/api";
216
67
 
217
68
  const reporting = createReportingClient({ auth });
218
-
219
- // Query crash rate
220
69
  const crashes = await reporting.queryMetricSet("com.example.app", "crashRateMetricSet", {
221
70
  metrics: ["crashRate", "userPerceivedCrashRate"],
222
71
  timelineSpec: {
@@ -225,75 +74,52 @@ const crashes = await reporting.queryMetricSet("com.example.app", "crashRateMetr
225
74
  endTime: { year: 2026, month: 3, day: 1 },
226
75
  },
227
76
  });
228
-
229
- // Detect anomalies
230
- const { anomalies } = await reporting.getAnomalies("com.example.app");
231
-
232
- // Search error issues
233
- const { errorIssues } = await reporting.searchErrorIssues("com.example.app");
234
77
  ```
235
78
 
236
- Available metric sets: `crashRateMetricSet`, `anrRateMetricSet`, `excessiveWakeupRateMetricSet`, `stuckBackgroundWakelockRateMetricSet`, `slowStartRateMetricSet`, `slowRenderingRateMetricSet`, `errorCountMetricSet`.
237
-
238
- ### Reports
79
+ ### Manage subscriptions
239
80
 
240
81
  ```typescript
241
- const { reports } = await client.reports.list("com.example.app", "earnings", 2026, 2);
242
- ```
243
-
244
- Report types: `earnings`, `sales`, `estimated_sales`, `installs`, `crashes`, `ratings`, `reviews`, `store_performance`, `subscriptions`, `play_balance`.
245
-
246
- ### Users & Grants
247
-
248
- Uses a separate client for developer account management.
249
-
250
- ```typescript
251
- import { createUsersClient } from "@gpc-cli/api";
252
-
253
- const users = createUsersClient({ auth });
254
-
255
- const { users: devUsers } = await users.list(developerId);
256
- const user = await users.get(developerId, userId);
257
-
258
- await users.create(developerId, {
259
- email: "dev@example.com",
260
- developerAccountPermission: ["CAN_MANAGE_PUBLIC_APKS", "CAN_REPLY_TO_REVIEWS"],
261
- });
262
- ```
263
-
264
- ### Testers
265
-
266
- ```typescript
267
- const edit = await client.edits.insert("com.example.app");
268
- const testers = await client.testers.get("com.example.app", edit.id, "internal");
269
-
270
- await client.testers.update("com.example.app", edit.id, "internal", {
271
- googleGroups: ["testers@example.com"],
272
- });
273
-
274
- await client.edits.commit("com.example.app", edit.id);
275
- ```
276
-
277
- ### Monetization
278
-
279
- ```typescript
280
- const { convertedRegionPrices } = await client.monetization.convertRegionPrices("com.example.app", {
281
- price: { currencyCode: "USD", units: "9", nanos: 990_000_000 },
282
- });
82
+ const { subscriptions } = await client.subscriptions.list("com.example.app");
83
+ await client.subscriptions.activateBasePlan("com.example.app", "premium_monthly", "p1m");
283
84
  ```
284
85
 
285
- ### Deobfuscation
286
-
287
- ```typescript
288
- const edit = await client.edits.insert("com.example.app");
289
- await client.deobfuscation.upload("com.example.app", edit.id, 42, "./mapping.txt");
290
- await client.edits.commit("com.example.app", edit.id);
291
- ```
86
+ ### Verify purchases
87
+
88
+ ```typescript
89
+ const purchase = await client.purchases.getProduct("com.example.app", "coins_100", token);
90
+ await client.purchases.acknowledgeProduct("com.example.app", "coins_100", token);
91
+ ```
92
+
93
+ ## All API Modules
94
+
95
+ | Module | Methods |
96
+ | ------ | ------- |
97
+ | `client.edits` | insert, get, validate, commit, delete |
98
+ | `client.bundles` | upload, list |
99
+ | `client.tracks` | list, get, update |
100
+ | `client.listings` | list, get, update, delete, deleteAll |
101
+ | `client.images` | list, upload, delete, deleteAll |
102
+ | `client.subscriptions` | list, get, create, patch, archive, activate/deactivate base plans and offers |
103
+ | `client.inappproducts` | list, get, create, update, delete, batchGet, batchUpdate, batchDelete |
104
+ | `client.oneTimeProducts` | list, get, create, patch, delete, batchGet, batchUpdate, batchDelete |
105
+ | `client.purchases` | getProduct, acknowledgeProduct, getSubscriptionV2, revokeSubscription, refund, listVoided |
106
+ | `client.reviews` | list, get, reply |
107
+ | `client.testers` | get, update |
108
+ | `client.reports` | list |
109
+ | `client.monetization` | convertRegionPrices |
110
+ | `client.deobfuscation` | upload |
111
+ | `client.expansionFiles` | get, update, patch, upload |
112
+ | `client.dataSafety` | get, update |
113
+ | `client.deviceTiers` | list, get, create |
114
+ | `client.internalSharing` | uploadBundle, uploadApk |
115
+ | `client.generatedApks` | list, download |
116
+ | `client.externalTransactions` | create, get, refund |
117
+ | `client.appRecovery` | create, deploy, cancel, list |
118
+ | `reporting.*` | queryMetricSet, getAnomalies, searchErrorIssues, searchErrorReports |
119
+ | `users.*` | list, get, create, patch, delete, listGrants, createGrant, patchGrant, deleteGrant |
292
120
 
293
121
  ## Pagination
294
122
 
295
- Built-in helpers for paginated endpoints:
296
-
297
123
  ```typescript
298
124
  import { paginateAll } from "@gpc-cli/api";
299
125
 
@@ -309,42 +135,9 @@ const allReviews = await paginateAll(async (pageToken) => {
309
135
  });
310
136
  ```
311
137
 
312
- ## Type Exports
313
-
314
- All Google Play API types are exported for use in your own code:
315
-
316
- ```typescript
317
- import type {
318
- PlayApiClient,
319
- ReportingApiClient,
320
- UsersApiClient,
321
- ApiClientOptions,
322
- Track,
323
- Release,
324
- ReleaseStatus,
325
- Bundle,
326
- Listing,
327
- Subscription,
328
- BasePlan,
329
- SubscriptionOffer,
330
- InAppProduct,
331
- Review,
332
- ProductPurchase,
333
- SubscriptionPurchaseV2,
334
- VoidedPurchase,
335
- MetricSetQuery,
336
- MetricSetResponse,
337
- ErrorIssue,
338
- User,
339
- Grant,
340
- ImageType,
341
- Money,
342
- } from "@gpc-cli/api";
343
- ```
344
-
345
138
  ## Error Handling
346
139
 
347
- API errors throw `ApiError` with a code, HTTP status, and actionable suggestion:
140
+ API errors throw `ApiError` with a code, HTTP status, and actionable suggestion. Retries are automatic for 429 and 5xx with exponential backoff and jitter.
348
141
 
349
142
  ```typescript
350
143
  import { ApiError } from "@gpc-cli/api";
@@ -353,20 +146,33 @@ try {
353
146
  await client.tracks.get("com.example.app", editId, "production");
354
147
  } catch (error) {
355
148
  if (error instanceof ApiError) {
356
- console.error(error.code); // e.g. "API_NOT_FOUND"
357
- console.error(error.statusCode); // e.g. 404
149
+ console.error(error.code); // "API_NOT_FOUND"
150
+ console.error(error.statusCode); // 404
358
151
  console.error(error.suggestion); // actionable fix
359
- console.error(error.toJSON()); // structured error object
360
152
  }
361
153
  }
362
154
  ```
363
155
 
364
- Retries are automatic for 429 (rate limit) and 5xx errors with exponential backoff and jitter.
156
+ ## Type Exports
157
+
158
+ All Google Play API types are exported:
159
+
160
+ ```typescript
161
+ import type {
162
+ PlayApiClient, ReportingApiClient, UsersApiClient, ApiClientOptions,
163
+ Track, Release, ReleaseStatus, Bundle, Listing,
164
+ Subscription, BasePlan, SubscriptionOffer, InAppProduct,
165
+ Review, ProductPurchase, SubscriptionPurchaseV2, VoidedPurchase,
166
+ MetricSetQuery, MetricSetResponse, ErrorIssue,
167
+ User, Grant, ImageType, Money,
168
+ } from "@gpc-cli/api";
169
+ ```
365
170
 
366
171
  ## Documentation
367
172
 
368
173
  - [Full documentation](https://yasserstudio.github.io/gpc/)
369
- - [SDK usage guide](https://yasserstudio.github.io/gpc/advanced/sdk-usage.html)
174
+ - [SDK usage guide](https://yasserstudio.github.io/gpc/advanced/sdk-usage)
175
+ - [API coverage map](https://yasserstudio.github.io/gpc/reference/api-coverage)
370
176
 
371
177
  ## License
372
178
 
package/dist/index.js CHANGED
@@ -1312,6 +1312,14 @@ function createApiClient(options) {
1312
1312
  filePath,
1313
1313
  "application/octet-stream"
1314
1314
  );
1315
+ if (!data.expansionFile) {
1316
+ throw new PlayApiError(
1317
+ "Upload succeeded but no expansion file data returned",
1318
+ "API_EMPTY_RESPONSE",
1319
+ 200,
1320
+ "This is unexpected. Retry the upload or contact Google Play support if the issue persists."
1321
+ );
1322
+ }
1315
1323
  return data.expansionFile;
1316
1324
  }
1317
1325
  },