@gpc-cli/api 1.0.37 → 1.0.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -35,6 +35,37 @@ function isResumeIncomplete(response) {
35
35
  return response.headers.get("X-Http-Status-Code-Override") === "308";
36
36
  }
37
37
  var DEFAULT_CHUNK_SIZE = 8 * 1024 * 1024;
38
+ function validateSessionUri(sessionUri, uploadUrl) {
39
+ let session;
40
+ let upload;
41
+ try {
42
+ session = new URL(sessionUri);
43
+ upload = new URL(uploadUrl);
44
+ } catch {
45
+ throw new PlayApiError(
46
+ "Invalid session URI or upload URL",
47
+ "UPLOAD_INVALID_URI",
48
+ 0,
49
+ "The resumable session URI must be a valid HTTPS URL."
50
+ );
51
+ }
52
+ if (session.protocol !== "https:") {
53
+ throw new PlayApiError(
54
+ "Resumable session URI must use HTTPS",
55
+ "UPLOAD_INSECURE_URI",
56
+ 0,
57
+ "Only HTTPS session URIs are accepted."
58
+ );
59
+ }
60
+ if (session.hostname !== upload.hostname && !session.hostname.endsWith(".googleapis.com")) {
61
+ throw new PlayApiError(
62
+ `Session URI host "${session.hostname}" does not match upload host "${upload.hostname}"`,
63
+ "UPLOAD_URI_HOST_MISMATCH",
64
+ 0,
65
+ "The session URI must point to the same Google API host as the upload URL."
66
+ );
67
+ }
68
+ }
38
69
  var RESUMABLE_THRESHOLD = 5 * 1024 * 1024;
39
70
  function envInt(name) {
40
71
  const val = process.env[name];
@@ -66,6 +97,9 @@ async function resumableUpload(uploadUrl, filePath, contentType, ctx, options) {
66
97
  const fileStats = await stat(filePath);
67
98
  const totalBytes = fileStats.size;
68
99
  let sessionUri = options?.resumeSessionUri;
100
+ if (sessionUri) {
101
+ validateSessionUri(sessionUri, uploadUrl);
102
+ }
69
103
  if (!sessionUri) {
70
104
  sessionUri = await initiateSession(
71
105
  uploadUrl,
@@ -225,6 +259,7 @@ async function initiateSession(uploadUrl, contentType, totalBytes, ctx, initialM
225
259
  "This is a Google API issue. Try again."
226
260
  );
227
261
  }
262
+ validateSessionUri(location, url);
228
263
  return location;
229
264
  }
230
265
  async function sendChunk(sessionUri, chunk, contentRange, ctx) {
@@ -399,6 +434,12 @@ function sanitizeErrorBody(body) {
399
434
  const cleaned = body.startsWith("<") ? stripHtml(body) : body;
400
435
  return cleaned.length > 200 ? cleaned.slice(0, 200) + "..." : cleaned;
401
436
  }
437
+ var SENSITIVE_PATH_SEGMENTS = /\/(tokens|purchases|purchaseToken)\/([^/?#]*)/gi;
438
+ function redactPath(path) {
439
+ return path.replace(SENSITIVE_PATH_SEGMENTS, (_match, segment) => {
440
+ return `/${segment}/***REDACTED***`;
441
+ });
442
+ }
402
443
  function validateFilePath(filePath) {
403
444
  const resolved = resolve(filePath);
404
445
  if (!isAbsolute(resolved)) {
@@ -694,7 +735,7 @@ function createHttpClient(options) {
694
735
  const errorBody = await response.text();
695
736
  const mapped = mapStatusToError(response.status, errorBody);
696
737
  const err = new PlayApiError(
697
- mapped.message ?? `${method} ${path} failed with status ${response.status}: ${sanitizeErrorBody(errorBody)}`,
738
+ mapped.message ?? `${method} ${redactPath(path)} failed with status ${response.status}: ${sanitizeErrorBody(errorBody)}`,
698
739
  mapped.code,
699
740
  response.status,
700
741
  mapped.suggestion
@@ -705,7 +746,7 @@ function createHttpClient(options) {
705
746
  onRetry?.({
706
747
  attempt: attempt + 1,
707
748
  method,
708
- path,
749
+ path: redactPath(path),
709
750
  status: response.status,
710
751
  error: err.message,
711
752
  delayMs: Math.round(delay),
@@ -725,7 +766,7 @@ function createHttpClient(options) {
725
766
  }
726
767
  if (error instanceof DOMException && error.name === "AbortError") {
727
768
  const timeoutErr = new PlayApiError(
728
- `${method} ${path} timed out after ${timeout}ms`,
769
+ `${method} ${redactPath(path)} timed out after ${timeout}ms`,
729
770
  "API_TIMEOUT",
730
771
  void 0,
731
772
  "The request exceeded the configured timeout. Consider increasing the timeout value."
@@ -735,7 +776,7 @@ function createHttpClient(options) {
735
776
  onRetry?.({
736
777
  attempt: attempt + 1,
737
778
  method,
738
- path,
779
+ path: redactPath(path),
739
780
  error: timeoutErr.message,
740
781
  delayMs: Math.round(jitteredDelay2(baseDelay, attempt, maxDelay)),
741
782
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
@@ -745,7 +786,7 @@ function createHttpClient(options) {
745
786
  throw timeoutErr;
746
787
  }
747
788
  const networkErr = new PlayApiError(
748
- `${method} ${path} failed: ${error instanceof Error ? error.message : String(error)}`,
789
+ `${method} ${redactPath(path)} failed: ${error instanceof Error ? error.message : String(error)}`,
749
790
  "API_NETWORK_ERROR",
750
791
  void 0,
751
792
  "A network error occurred. Check your internet connection."
@@ -755,7 +796,7 @@ function createHttpClient(options) {
755
796
  onRetry?.({
756
797
  attempt: attempt + 1,
757
798
  method,
758
- path,
799
+ path: redactPath(path),
759
800
  error: networkErr.message,
760
801
  delayMs: Math.round(jitteredDelay2(baseDelay, attempt, maxDelay)),
761
802
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
@@ -848,7 +889,7 @@ function createHttpClient(options) {
848
889
  if (error instanceof DOMException && error.name === "AbortError") {
849
890
  const sizeMb = Math.round(fileBuffer.byteLength / (1024 * 1024));
850
891
  const timeoutErr = new PlayApiError(
851
- `POST upload ${path} timed out after ${effectiveTimeout}ms (file: ${sizeMb} MB)`,
892
+ `POST upload ${redactPath(path)} timed out after ${effectiveTimeout}ms (file: ${sizeMb} MB)`,
852
893
  "API_TIMEOUT",
853
894
  void 0,
854
895
  `Upload timed out. Set GPC_UPLOAD_TIMEOUT=${effectiveTimeout * 2} (ms) or use --timeout to increase.`
@@ -858,7 +899,7 @@ function createHttpClient(options) {
858
899
  onRetry?.({
859
900
  attempt: attempt + 1,
860
901
  method: "POST",
861
- path: `upload ${path}`,
902
+ path: `upload ${redactPath(path)}`,
862
903
  error: timeoutErr.message,
863
904
  delayMs: Math.round(jitteredDelay2(baseDelay, attempt, maxDelay)),
864
905
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
@@ -868,7 +909,7 @@ function createHttpClient(options) {
868
909
  throw timeoutErr;
869
910
  }
870
911
  const networkErr = new PlayApiError(
871
- `POST upload ${path} failed: ${error instanceof Error ? error.message : String(error)}`,
912
+ `POST upload ${redactPath(path)} failed: ${error instanceof Error ? error.message : String(error)}`,
872
913
  "API_NETWORK_ERROR",
873
914
  void 0,
874
915
  "A network error occurred. Check your internet connection."
@@ -878,7 +919,7 @@ function createHttpClient(options) {
878
919
  onRetry?.({
879
920
  attempt: attempt + 1,
880
921
  method: "POST",
881
- path: `upload ${path}`,
922
+ path: `upload ${redactPath(path)}`,
882
923
  error: networkErr.message,
883
924
  delayMs: Math.round(jitteredDelay2(baseDelay, attempt, maxDelay)),
884
925
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
@@ -993,7 +1034,7 @@ function createHttpClient(options) {
993
1034
  const errorBody = await response.text();
994
1035
  const mapped = mapStatusToError(response.status, errorBody);
995
1036
  throw new PlayApiError(
996
- mapped.message ?? `GET ${path} failed with status ${response.status}: ${sanitizeErrorBody(errorBody)}`,
1037
+ mapped.message ?? `GET ${redactPath(path)} failed with status ${response.status}: ${sanitizeErrorBody(errorBody)}`,
997
1038
  mapped.code,
998
1039
  response.status,
999
1040
  mapped.suggestion
@@ -1041,40 +1082,54 @@ function createRateLimiter(buckets) {
1041
1082
  config: bucket
1042
1083
  });
1043
1084
  }
1085
+ const mutexes = /* @__PURE__ */ new Map();
1086
+ function acquireSync(state) {
1087
+ const now = Date.now();
1088
+ const elapsed = now - state.lastRefillTime;
1089
+ const refill = Math.floor(elapsed / state.config.refillIntervalMs * state.config.refillRate);
1090
+ if (refill > 0) {
1091
+ state.tokens = Math.min(state.config.maxTokens, state.tokens + refill);
1092
+ state.lastRefillTime = now;
1093
+ }
1094
+ if (state.tokens > 0) {
1095
+ state.tokens--;
1096
+ return 0;
1097
+ }
1098
+ return Math.ceil(1 / state.config.refillRate * state.config.refillIntervalMs);
1099
+ }
1044
1100
  return {
1045
1101
  async acquire(bucket) {
1046
1102
  const state = states.get(bucket);
1047
1103
  if (!state) return;
1048
- const now = Date.now();
1049
- const elapsed = now - state.lastRefillTime;
1050
- const refill = Math.floor(
1051
- elapsed / state.config.refillIntervalMs * state.config.refillRate
1052
- );
1053
- if (refill > 0) {
1054
- state.tokens = Math.min(state.config.maxTokens, state.tokens + refill);
1055
- state.lastRefillTime = now;
1056
- }
1057
- if (state.tokens > 0) {
1058
- state.tokens--;
1059
- return;
1060
- }
1061
- const tokensNeeded = 1;
1062
- const waitMs = Math.ceil(
1063
- tokensNeeded / state.config.refillRate * state.config.refillIntervalMs
1064
- );
1065
- await new Promise((r) => setTimeout(r, waitMs));
1066
- const afterWait = Date.now();
1067
- const totalElapsed = afterWait - state.lastRefillTime;
1068
- const newTokens = Math.floor(
1069
- totalElapsed / state.config.refillIntervalMs * state.config.refillRate
1070
- );
1071
- state.tokens = Math.max(0, Math.min(state.config.maxTokens, newTokens) - 1);
1072
- state.lastRefillTime = afterWait;
1104
+ const prev = mutexes.get(bucket) ?? Promise.resolve();
1105
+ const next = prev.then(async () => {
1106
+ const waitMs = acquireSync(state);
1107
+ if (waitMs <= 0) return;
1108
+ await new Promise((r) => setTimeout(r, waitMs));
1109
+ const afterWait = Date.now();
1110
+ const totalElapsed = afterWait - state.lastRefillTime;
1111
+ const newTokens = Math.floor(
1112
+ totalElapsed / state.config.refillIntervalMs * state.config.refillRate
1113
+ );
1114
+ state.tokens = Math.max(0, Math.min(state.config.maxTokens, newTokens) - 1);
1115
+ state.lastRefillTime = afterWait;
1116
+ });
1117
+ mutexes.set(bucket, next);
1118
+ await next;
1073
1119
  }
1074
1120
  };
1075
1121
  }
1076
1122
 
1077
1123
  // src/client.ts
1124
+ var p = (segment) => encodeURIComponent(segment);
1125
+ var _deprecationWarned = /* @__PURE__ */ new Set();
1126
+ function warnOnce(code, msg) {
1127
+ if (_deprecationWarned.has(code)) return;
1128
+ _deprecationWarned.add(code);
1129
+ if (typeof process !== "undefined" && process.emitWarning) {
1130
+ process.emitWarning(msg, { type: "DeprecationWarning", code });
1131
+ }
1132
+ }
1078
1133
  var DEFAULT_REGIONS_VERSION = "2022/02";
1079
1134
  function applyMutationOptions(params, options) {
1080
1135
  if (options?.allowMissing) params["allowMissing"] = "true";
@@ -1115,19 +1170,19 @@ function createApiClient(options) {
1115
1170
  return {
1116
1171
  edits: {
1117
1172
  async insert(packageName) {
1118
- const { data } = await http.post(`/${packageName}/edits`);
1173
+ const { data } = await http.post(`/${p(packageName)}/edits`);
1119
1174
  return data;
1120
1175
  },
1121
1176
  async get(packageName, editId) {
1122
- const { data } = await http.get(`/${packageName}/edits/${editId}`);
1177
+ const { data } = await http.get(`/${p(packageName)}/edits/${p(editId)}`);
1123
1178
  return data;
1124
1179
  },
1125
1180
  async validate(packageName, editId) {
1126
- const { data } = await http.post(`/${packageName}/edits/${editId}:validate`);
1181
+ const { data } = await http.post(`/${p(packageName)}/edits/${p(editId)}:validate`);
1127
1182
  return data;
1128
1183
  },
1129
1184
  async commit(packageName, editId, options2) {
1130
- let path = `/${packageName}/edits/${editId}:commit`;
1185
+ let path = `/${p(packageName)}/edits/${p(editId)}:commit`;
1131
1186
  if (options2?.changesNotSentForReview || options2?.changesInReviewBehavior) {
1132
1187
  const params = new URLSearchParams();
1133
1188
  if (options2.changesNotSentForReview) params.set("changesNotSentForReview", "true");
@@ -1139,24 +1194,26 @@ function createApiClient(options) {
1139
1194
  return data;
1140
1195
  },
1141
1196
  async delete(packageName, editId) {
1142
- await http.delete(`/${packageName}/edits/${editId}`);
1197
+ await http.delete(`/${p(packageName)}/edits/${p(editId)}`);
1143
1198
  }
1144
1199
  },
1145
1200
  details: {
1146
1201
  async get(packageName, editId) {
1147
- const { data } = await http.get(`/${packageName}/edits/${editId}/details`);
1202
+ const { data } = await http.get(
1203
+ `/${p(packageName)}/edits/${p(editId)}/details`
1204
+ );
1148
1205
  return data;
1149
1206
  },
1150
1207
  async update(packageName, editId, details) {
1151
1208
  const { data } = await http.put(
1152
- `/${packageName}/edits/${editId}/details`,
1209
+ `/${p(packageName)}/edits/${p(editId)}/details`,
1153
1210
  details
1154
1211
  );
1155
1212
  return data;
1156
1213
  },
1157
1214
  async patch(packageName, editId, partial) {
1158
1215
  const { data } = await http.patch(
1159
- `/${packageName}/edits/${editId}/details`,
1216
+ `/${p(packageName)}/edits/${p(editId)}/details`,
1160
1217
  partial
1161
1218
  );
1162
1219
  return data;
@@ -1165,12 +1222,12 @@ function createApiClient(options) {
1165
1222
  bundles: {
1166
1223
  async list(packageName, editId) {
1167
1224
  const { data } = await http.get(
1168
- `/${packageName}/edits/${editId}/bundles`
1225
+ `/${p(packageName)}/edits/${p(editId)}/bundles`
1169
1226
  );
1170
1227
  return data.bundles;
1171
1228
  },
1172
1229
  async upload(packageName, editId, filePath, uploadOptions, deviceTierConfigId) {
1173
- let bundlePath = `/${packageName}/edits/${editId}/bundles`;
1230
+ let bundlePath = `/${p(packageName)}/edits/${p(editId)}/bundles`;
1174
1231
  if (deviceTierConfigId) {
1175
1232
  bundlePath += `?${new URLSearchParams({ deviceTierConfigId }).toString()}`;
1176
1233
  }
@@ -1194,30 +1251,35 @@ function createApiClient(options) {
1194
1251
  tracks: {
1195
1252
  async list(packageName, editId) {
1196
1253
  const { data } = await http.get(
1197
- `/${packageName}/edits/${editId}/tracks`
1254
+ `/${p(packageName)}/edits/${p(editId)}/tracks`
1198
1255
  );
1199
1256
  return data.tracks;
1200
1257
  },
1201
1258
  async get(packageName, editId, track) {
1202
- const { data } = await http.get(`/${packageName}/edits/${editId}/tracks/${track}`);
1259
+ const { data } = await http.get(
1260
+ `/${p(packageName)}/edits/${p(editId)}/tracks/${p(track)}`
1261
+ );
1203
1262
  return data;
1204
1263
  },
1205
1264
  async create(packageName, editId, trackName) {
1206
- const { data } = await http.post(`/${packageName}/edits/${editId}/tracks`, {
1265
+ const { data } = await http.post(`/${p(packageName)}/edits/${p(editId)}/tracks`, {
1207
1266
  track: trackName
1208
1267
  });
1209
1268
  return data;
1210
1269
  },
1211
1270
  async update(packageName, editId, track, release) {
1212
- const { data } = await http.put(`/${packageName}/edits/${editId}/tracks/${track}`, {
1213
- track,
1214
- releases: [release]
1215
- });
1271
+ const { data } = await http.put(
1272
+ `/${p(packageName)}/edits/${p(editId)}/tracks/${p(track)}`,
1273
+ {
1274
+ track,
1275
+ releases: [release]
1276
+ }
1277
+ );
1216
1278
  return data;
1217
1279
  },
1218
1280
  async patch(packageName, editId, track, release) {
1219
1281
  const { data } = await http.patch(
1220
- `/${packageName}/edits/${editId}/tracks/${track}`,
1282
+ `/${p(packageName)}/edits/${p(editId)}/tracks/${p(track)}`,
1221
1283
  {
1222
1284
  track,
1223
1285
  releases: [release]
@@ -1229,19 +1291,21 @@ function createApiClient(options) {
1229
1291
  releases: {
1230
1292
  async list(packageName, track) {
1231
1293
  const { data } = await http.get(
1232
- `/${packageName}/tracks/${track}/releases`
1294
+ `/${p(packageName)}/tracks/${p(track)}/releases`
1233
1295
  );
1234
1296
  return data.releases ?? [];
1235
1297
  }
1236
1298
  },
1237
1299
  apks: {
1238
1300
  async list(packageName, editId) {
1239
- const { data } = await http.get(`/${packageName}/edits/${editId}/apks`);
1301
+ const { data } = await http.get(
1302
+ `/${p(packageName)}/edits/${p(editId)}/apks`
1303
+ );
1240
1304
  return data.apks || [];
1241
1305
  },
1242
1306
  async upload(packageName, editId, filePath, uploadOptions) {
1243
1307
  const { data } = await http.uploadResumable(
1244
- `/${packageName}/edits/${editId}/apks`,
1308
+ `/${p(packageName)}/edits/${p(editId)}/apks`,
1245
1309
  filePath,
1246
1310
  "application/vnd.android.package-archive",
1247
1311
  uploadOptions
@@ -1258,7 +1322,7 @@ function createApiClient(options) {
1258
1322
  },
1259
1323
  async addExternallyHosted(packageName, editId, apkData) {
1260
1324
  const { data } = await http.post(
1261
- `/${packageName}/edits/${editId}/apks/externallyHosted`,
1325
+ `/${p(packageName)}/edits/${p(editId)}/apks/externallyHosted`,
1262
1326
  { externallyHostedApk: apkData }
1263
1327
  );
1264
1328
  return data;
@@ -1267,47 +1331,47 @@ function createApiClient(options) {
1267
1331
  listings: {
1268
1332
  async list(packageName, editId) {
1269
1333
  const { data } = await http.get(
1270
- `/${packageName}/edits/${editId}/listings`
1334
+ `/${p(packageName)}/edits/${p(editId)}/listings`
1271
1335
  );
1272
1336
  return data.listings || [];
1273
1337
  },
1274
1338
  async get(packageName, editId, language) {
1275
1339
  const { data } = await http.get(
1276
- `/${packageName}/edits/${editId}/listings/${language}`
1340
+ `/${p(packageName)}/edits/${p(editId)}/listings/${p(language)}`
1277
1341
  );
1278
1342
  return data;
1279
1343
  },
1280
1344
  async update(packageName, editId, language, listing) {
1281
1345
  const { data } = await http.put(
1282
- `/${packageName}/edits/${editId}/listings/${language}`,
1346
+ `/${p(packageName)}/edits/${p(editId)}/listings/${p(language)}`,
1283
1347
  listing
1284
1348
  );
1285
1349
  return data;
1286
1350
  },
1287
1351
  async patch(packageName, editId, language, partial) {
1288
1352
  const { data } = await http.patch(
1289
- `/${packageName}/edits/${editId}/listings/${language}`,
1353
+ `/${p(packageName)}/edits/${p(editId)}/listings/${p(language)}`,
1290
1354
  partial
1291
1355
  );
1292
1356
  return data;
1293
1357
  },
1294
1358
  async delete(packageName, editId, language) {
1295
- await http.delete(`/${packageName}/edits/${editId}/listings/${language}`);
1359
+ await http.delete(`/${p(packageName)}/edits/${p(editId)}/listings/${p(language)}`);
1296
1360
  },
1297
1361
  async deleteAll(packageName, editId) {
1298
- await http.delete(`/${packageName}/edits/${editId}/listings`);
1362
+ await http.delete(`/${p(packageName)}/edits/${p(editId)}/listings`);
1299
1363
  }
1300
1364
  },
1301
1365
  images: {
1302
1366
  async list(packageName, editId, language, imageType) {
1303
1367
  const { data } = await http.get(
1304
- `/${packageName}/edits/${editId}/listings/${language}/${imageType}`
1368
+ `/${p(packageName)}/edits/${p(editId)}/listings/${p(language)}/${p(imageType)}`
1305
1369
  );
1306
1370
  return data.images || [];
1307
1371
  },
1308
1372
  async upload(packageName, editId, language, imageType, filePath) {
1309
1373
  const { data } = await http.upload(
1310
- `/${packageName}/edits/${editId}/listings/${language}/${imageType}`,
1374
+ `/${p(packageName)}/edits/${p(editId)}/listings/${p(language)}/${p(imageType)}`,
1311
1375
  filePath,
1312
1376
  filePath.endsWith(".png") ? "image/png" : "image/jpeg"
1313
1377
  );
@@ -1323,12 +1387,12 @@ function createApiClient(options) {
1323
1387
  },
1324
1388
  async delete(packageName, editId, language, imageType, imageId) {
1325
1389
  await http.delete(
1326
- `/${packageName}/edits/${editId}/listings/${language}/${imageType}/${imageId}`
1390
+ `/${p(packageName)}/edits/${p(editId)}/listings/${p(language)}/${p(imageType)}/${p(imageId)}`
1327
1391
  );
1328
1392
  },
1329
1393
  async deleteAll(packageName, editId, language, imageType) {
1330
1394
  const { data } = await http.delete(
1331
- `/${packageName}/edits/${editId}/listings/${language}/${imageType}`
1395
+ `/${p(packageName)}/edits/${p(editId)}/listings/${p(language)}/${p(imageType)}`
1332
1396
  );
1333
1397
  return data.deleted || [];
1334
1398
  }
@@ -1336,27 +1400,27 @@ function createApiClient(options) {
1336
1400
  expansionFiles: {
1337
1401
  async get(packageName, editId, apkVersionCode, expansionFileType) {
1338
1402
  const { data } = await http.get(
1339
- `/${packageName}/edits/${editId}/apks/${apkVersionCode}/expansionFiles/${expansionFileType}`
1403
+ `/${p(packageName)}/edits/${p(editId)}/apks/${p(String(apkVersionCode))}/expansionFiles/${p(expansionFileType)}`
1340
1404
  );
1341
1405
  return data;
1342
1406
  },
1343
1407
  async update(packageName, editId, apkVersionCode, expansionFileType, body) {
1344
1408
  const { data } = await http.put(
1345
- `/${packageName}/edits/${editId}/apks/${apkVersionCode}/expansionFiles/${expansionFileType}`,
1409
+ `/${p(packageName)}/edits/${p(editId)}/apks/${p(String(apkVersionCode))}/expansionFiles/${p(expansionFileType)}`,
1346
1410
  body
1347
1411
  );
1348
1412
  return data;
1349
1413
  },
1350
1414
  async patch(packageName, editId, apkVersionCode, expansionFileType, body) {
1351
1415
  const { data } = await http.patch(
1352
- `/${packageName}/edits/${editId}/apks/${apkVersionCode}/expansionFiles/${expansionFileType}`,
1416
+ `/${p(packageName)}/edits/${p(editId)}/apks/${p(String(apkVersionCode))}/expansionFiles/${p(expansionFileType)}`,
1353
1417
  body
1354
1418
  );
1355
1419
  return data;
1356
1420
  },
1357
1421
  async upload(packageName, editId, apkVersionCode, expansionFileType, filePath) {
1358
1422
  const { data } = await http.upload(
1359
- `/${packageName}/edits/${editId}/apks/${apkVersionCode}/expansionFiles/${expansionFileType}`,
1423
+ `/${p(packageName)}/edits/${p(editId)}/apks/${p(String(apkVersionCode))}/expansionFiles/${p(expansionFileType)}`,
1360
1424
  filePath,
1361
1425
  "application/octet-stream"
1362
1426
  );
@@ -1374,14 +1438,14 @@ function createApiClient(options) {
1374
1438
  countryAvailability: {
1375
1439
  async get(packageName, editId, track) {
1376
1440
  const { data } = await http.get(
1377
- `/${packageName}/edits/${editId}/countryAvailability/${track}`
1441
+ `/${p(packageName)}/edits/${p(editId)}/countryAvailability/${p(track)}`
1378
1442
  );
1379
1443
  return data;
1380
1444
  }
1381
1445
  },
1382
1446
  dataSafety: {
1383
1447
  async update(packageName, body) {
1384
- const { data } = await http.post(`/${packageName}/dataSafety`, body);
1448
+ const { data } = await http.post(`/${p(packageName)}/dataSafety`, body);
1385
1449
  return data;
1386
1450
  }
1387
1451
  },
@@ -1395,7 +1459,7 @@ function createApiClient(options) {
1395
1459
  params["translationLanguage"] = options2.translationLanguage;
1396
1460
  const hasParams = Object.keys(params).length > 0;
1397
1461
  const { data } = await http.get(
1398
- `/${packageName}/reviews`,
1462
+ `/${p(packageName)}/reviews`,
1399
1463
  hasParams ? params : void 0
1400
1464
  );
1401
1465
  return data;
@@ -1405,7 +1469,7 @@ function createApiClient(options) {
1405
1469
  if (translationLanguage) params["translationLanguage"] = translationLanguage;
1406
1470
  const hasParams = Object.keys(params).length > 0;
1407
1471
  const { data } = await http.get(
1408
- `/${packageName}/reviews/${reviewId}`,
1472
+ `/${p(packageName)}/reviews/${p(reviewId)}`,
1409
1473
  hasParams ? params : void 0
1410
1474
  );
1411
1475
  return data;
@@ -1413,7 +1477,7 @@ function createApiClient(options) {
1413
1477
  async reply(packageName, reviewId, replyText) {
1414
1478
  const body = { replyText };
1415
1479
  const { data } = await http.post(
1416
- `/${packageName}/reviews/${reviewId}:reply`,
1480
+ `/${p(packageName)}/reviews/${p(reviewId)}:reply`,
1417
1481
  body
1418
1482
  );
1419
1483
  return data;
@@ -1426,20 +1490,22 @@ function createApiClient(options) {
1426
1490
  if (options2?.pageSize) params["pageSize"] = String(options2.pageSize);
1427
1491
  const hasParams = Object.keys(params).length > 0;
1428
1492
  const { data } = await http.get(
1429
- `/${packageName}/subscriptions`,
1493
+ `/${p(packageName)}/subscriptions`,
1430
1494
  hasParams ? params : void 0
1431
1495
  );
1432
1496
  return data;
1433
1497
  },
1434
1498
  async get(packageName, productId) {
1435
- const { data } = await http.get(`/${packageName}/subscriptions/${productId}`);
1499
+ const { data } = await http.get(
1500
+ `/${p(packageName)}/subscriptions/${p(productId)}`
1501
+ );
1436
1502
  return data;
1437
1503
  },
1438
1504
  async create(packageName, body, productId, regionsVersion) {
1439
1505
  const params = {};
1440
1506
  if (productId) params["productId"] = productId;
1441
1507
  params["regionsVersion.version"] = regionsVersion || DEFAULT_REGIONS_VERSION;
1442
- const path = `/${packageName}/subscriptions?${new URLSearchParams(params).toString()}`;
1508
+ const path = `/${p(packageName)}/subscriptions?${new URLSearchParams(params).toString()}`;
1443
1509
  const { data } = await http.post(path, body);
1444
1510
  return data;
1445
1511
  },
@@ -1448,65 +1514,67 @@ function createApiClient(options) {
1448
1514
  if (updateMask) params["updateMask"] = updateMask;
1449
1515
  params["regionsVersion.version"] = regionsVersion || DEFAULT_REGIONS_VERSION;
1450
1516
  applyMutationOptions(params, options2);
1451
- const path = `/${packageName}/subscriptions/${productId}?${new URLSearchParams(params).toString()}`;
1517
+ const path = `/${p(packageName)}/subscriptions/${p(productId)}?${new URLSearchParams(params).toString()}`;
1452
1518
  const { data } = await http.patch(path, body);
1453
1519
  return data;
1454
1520
  },
1455
1521
  async delete(packageName, productId) {
1456
- await http.delete(`/${packageName}/subscriptions/${productId}`);
1522
+ await http.delete(`/${p(packageName)}/subscriptions/${p(productId)}`);
1457
1523
  },
1458
1524
  async batchGet(packageName, productIds) {
1459
1525
  const params = productIds.map((id) => `productIds=${encodeURIComponent(id)}`).join("&");
1460
1526
  const { data } = await http.get(
1461
- `/${packageName}/subscriptions:batchGet?${params}`
1527
+ `/${p(packageName)}/subscriptions:batchGet?${params}`
1462
1528
  );
1463
1529
  return data.subscriptions ?? [];
1464
1530
  },
1465
1531
  async batchUpdate(packageName, requests) {
1466
1532
  const { data } = await http.post(
1467
- `/${packageName}/subscriptions:batchUpdate`,
1533
+ `/${p(packageName)}/subscriptions:batchUpdate`,
1468
1534
  requests
1469
1535
  );
1470
1536
  return data;
1471
1537
  },
1472
1538
  async activateBasePlan(packageName, productId, basePlanId) {
1473
1539
  const { data } = await http.post(
1474
- `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}:activate`
1540
+ `/${p(packageName)}/subscriptions/${p(productId)}/basePlans/${p(basePlanId)}:activate`
1475
1541
  );
1476
1542
  return data;
1477
1543
  },
1478
1544
  async deactivateBasePlan(packageName, productId, basePlanId) {
1479
1545
  const { data } = await http.post(
1480
- `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}:deactivate`
1546
+ `/${p(packageName)}/subscriptions/${p(productId)}/basePlans/${p(basePlanId)}:deactivate`
1481
1547
  );
1482
1548
  return data;
1483
1549
  },
1484
1550
  async deleteBasePlan(packageName, productId, basePlanId) {
1485
- await http.delete(`/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}`);
1551
+ await http.delete(
1552
+ `/${p(packageName)}/subscriptions/${p(productId)}/basePlans/${p(basePlanId)}`
1553
+ );
1486
1554
  },
1487
1555
  async migratePrices(packageName, productId, basePlanId, body) {
1488
1556
  const { data } = await http.post(
1489
- `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}:migratePrices`,
1557
+ `/${p(packageName)}/subscriptions/${p(productId)}/basePlans/${p(basePlanId)}:migratePrices`,
1490
1558
  body
1491
1559
  );
1492
1560
  return data;
1493
1561
  },
1494
1562
  async batchMigratePrices(packageName, productId, body) {
1495
1563
  const { data } = await http.post(
1496
- `/${packageName}/subscriptions/${productId}/basePlans:batchMigratePrices`,
1564
+ `/${p(packageName)}/subscriptions/${p(productId)}/basePlans:batchMigratePrices`,
1497
1565
  body
1498
1566
  );
1499
1567
  return data;
1500
1568
  },
1501
1569
  async listOffers(packageName, productId, basePlanId) {
1502
1570
  const { data } = await http.get(
1503
- `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}/offers`
1571
+ `/${p(packageName)}/subscriptions/${p(productId)}/basePlans/${p(basePlanId)}/offers`
1504
1572
  );
1505
1573
  return data;
1506
1574
  },
1507
1575
  async getOffer(packageName, productId, basePlanId, offerId) {
1508
1576
  const { data } = await http.get(
1509
- `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}/offers/${offerId}`
1577
+ `/${p(packageName)}/subscriptions/${p(productId)}/basePlans/${p(basePlanId)}/offers/${p(offerId)}`
1510
1578
  );
1511
1579
  return data;
1512
1580
  },
@@ -1514,7 +1582,7 @@ function createApiClient(options) {
1514
1582
  const params = {};
1515
1583
  if (offerId) params["offerId"] = offerId;
1516
1584
  params["regionsVersion.version"] = regionsVersion || DEFAULT_REGIONS_VERSION;
1517
- const path = `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}/offers?${new URLSearchParams(params).toString()}`;
1585
+ const path = `/${p(packageName)}/subscriptions/${p(productId)}/basePlans/${p(basePlanId)}/offers?${new URLSearchParams(params).toString()}`;
1518
1586
  const { data } = await http.post(path, body);
1519
1587
  return data;
1520
1588
  },
@@ -1523,51 +1591,51 @@ function createApiClient(options) {
1523
1591
  if (updateMask) params["updateMask"] = updateMask;
1524
1592
  params["regionsVersion.version"] = regionsVersion || DEFAULT_REGIONS_VERSION;
1525
1593
  applyMutationOptions(params, options2);
1526
- const path = `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}/offers/${offerId}?${new URLSearchParams(params).toString()}`;
1594
+ const path = `/${p(packageName)}/subscriptions/${p(productId)}/basePlans/${p(basePlanId)}/offers/${p(offerId)}?${new URLSearchParams(params).toString()}`;
1527
1595
  const { data } = await http.patch(path, body);
1528
1596
  return data;
1529
1597
  },
1530
1598
  async deleteOffer(packageName, productId, basePlanId, offerId) {
1531
1599
  await http.delete(
1532
- `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}/offers/${offerId}`
1600
+ `/${p(packageName)}/subscriptions/${p(productId)}/basePlans/${p(basePlanId)}/offers/${p(offerId)}`
1533
1601
  );
1534
1602
  },
1535
1603
  async activateOffer(packageName, productId, basePlanId, offerId) {
1536
1604
  const { data } = await http.post(
1537
- `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}/offers/${offerId}:activate`
1605
+ `/${p(packageName)}/subscriptions/${p(productId)}/basePlans/${p(basePlanId)}/offers/${p(offerId)}:activate`
1538
1606
  );
1539
1607
  return data;
1540
1608
  },
1541
1609
  async deactivateOffer(packageName, productId, basePlanId, offerId) {
1542
1610
  const { data } = await http.post(
1543
- `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}/offers/${offerId}:deactivate`
1611
+ `/${p(packageName)}/subscriptions/${p(productId)}/basePlans/${p(basePlanId)}/offers/${p(offerId)}:deactivate`
1544
1612
  );
1545
1613
  return data;
1546
1614
  },
1547
1615
  async batchUpdateBasePlanStates(packageName, productId, requests) {
1548
1616
  const { data } = await http.post(
1549
- `/${packageName}/subscriptions/${productId}/basePlans:batchUpdateStates`,
1617
+ `/${p(packageName)}/subscriptions/${p(productId)}/basePlans:batchUpdateStates`,
1550
1618
  requests
1551
1619
  );
1552
1620
  return data;
1553
1621
  },
1554
1622
  async batchGetOffers(packageName, productId, basePlanId, offerIds) {
1555
1623
  const { data } = await http.post(
1556
- `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}/offers:batchGet`,
1624
+ `/${p(packageName)}/subscriptions/${p(productId)}/basePlans/${p(basePlanId)}/offers:batchGet`,
1557
1625
  { requests: offerIds.map((id) => ({ offerId: id })) }
1558
1626
  );
1559
1627
  return data;
1560
1628
  },
1561
1629
  async batchUpdateOffers(packageName, productId, basePlanId, requests) {
1562
1630
  const { data } = await http.post(
1563
- `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}/offers:batchUpdate`,
1631
+ `/${p(packageName)}/subscriptions/${p(productId)}/basePlans/${p(basePlanId)}/offers:batchUpdate`,
1564
1632
  requests
1565
1633
  );
1566
1634
  return data;
1567
1635
  },
1568
1636
  async batchUpdateOfferStates(packageName, productId, basePlanId, requests) {
1569
1637
  const { data } = await http.post(
1570
- `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}/offers:batchUpdateStates`,
1638
+ `/${p(packageName)}/subscriptions/${p(productId)}/basePlans/${p(basePlanId)}/offers:batchUpdateStates`,
1571
1639
  requests
1572
1640
  );
1573
1641
  return data;
@@ -1579,20 +1647,20 @@ function createApiClient(options) {
1579
1647
  if (options2?.token) params["token"] = options2.token;
1580
1648
  const hasParams = Object.keys(params).length > 0;
1581
1649
  const { data } = await http.get(
1582
- `/${packageName}/inappproducts`,
1650
+ `/${p(packageName)}/inappproducts`,
1583
1651
  hasParams ? params : void 0
1584
1652
  );
1585
1653
  return data;
1586
1654
  },
1587
1655
  async get(packageName, sku) {
1588
- const { data } = await http.get(`/${packageName}/inappproducts/${sku}`);
1656
+ const { data } = await http.get(`/${p(packageName)}/inappproducts/${p(sku)}`);
1589
1657
  return data;
1590
1658
  },
1591
1659
  async create(packageName, body, options2) {
1592
1660
  const params = {};
1593
1661
  if (options2?.autoConvertMissingPrices) params["autoConvertMissingPrices"] = "true";
1594
1662
  const hasParams = Object.keys(params).length > 0;
1595
- const path = hasParams ? `/${packageName}/inappproducts?${new URLSearchParams(params).toString()}` : `/${packageName}/inappproducts`;
1663
+ const path = hasParams ? `/${p(packageName)}/inappproducts?${new URLSearchParams(params).toString()}` : `/${p(packageName)}/inappproducts`;
1596
1664
  const { data } = await http.post(path, body);
1597
1665
  return data;
1598
1666
  },
@@ -1601,7 +1669,7 @@ function createApiClient(options) {
1601
1669
  if (options2?.autoConvertMissingPrices) params["autoConvertMissingPrices"] = "true";
1602
1670
  if (options2?.allowMissing) params["allowMissing"] = "true";
1603
1671
  const hasParams = Object.keys(params).length > 0;
1604
- const path = hasParams ? `/${packageName}/inappproducts/${sku}?${new URLSearchParams(params).toString()}` : `/${packageName}/inappproducts/${sku}`;
1672
+ const path = hasParams ? `/${p(packageName)}/inappproducts/${p(sku)}?${new URLSearchParams(params).toString()}` : `/${p(packageName)}/inappproducts/${p(sku)}`;
1605
1673
  const { data } = await http.put(path, body);
1606
1674
  return data;
1607
1675
  },
@@ -1610,16 +1678,16 @@ function createApiClient(options) {
1610
1678
  if (options2?.autoConvertMissingPrices) params["autoConvertMissingPrices"] = "true";
1611
1679
  if (options2?.latencyTolerance) params["latencyTolerance"] = options2.latencyTolerance;
1612
1680
  const hasParams = Object.keys(params).length > 0;
1613
- const path = hasParams ? `/${packageName}/inappproducts/${sku}?${new URLSearchParams(params).toString()}` : `/${packageName}/inappproducts/${sku}`;
1681
+ const path = hasParams ? `/${p(packageName)}/inappproducts/${p(sku)}?${new URLSearchParams(params).toString()}` : `/${p(packageName)}/inappproducts/${p(sku)}`;
1614
1682
  const { data } = await http.patch(path, body);
1615
1683
  return data;
1616
1684
  },
1617
1685
  async delete(packageName, sku) {
1618
- await http.delete(`/${packageName}/inappproducts/${sku}`);
1686
+ await http.delete(`/${p(packageName)}/inappproducts/${p(sku)}`);
1619
1687
  },
1620
1688
  async batchUpdate(packageName, requests) {
1621
1689
  const { data } = await http.post(
1622
- `/${packageName}/inappproducts:batchUpdate`,
1690
+ `/${p(packageName)}/inappproducts:batchUpdate`,
1623
1691
  requests
1624
1692
  );
1625
1693
  return data;
@@ -1630,13 +1698,13 @@ function createApiClient(options) {
1630
1698
  params["sku"] = skus.join(",");
1631
1699
  }
1632
1700
  const { data } = await http.get(
1633
- `/${packageName}/inappproducts:batchGet`,
1701
+ `/${p(packageName)}/inappproducts:batchGet`,
1634
1702
  Object.keys(params).length > 0 ? params : void 0
1635
1703
  );
1636
1704
  return data.inappproduct || [];
1637
1705
  },
1638
1706
  async batchDelete(packageName, skus) {
1639
- await http.post(`/${packageName}/inappproducts:batchDelete`, {
1707
+ await http.post(`/${p(packageName)}/inappproducts:batchDelete`, {
1640
1708
  requests: skus.map((sku) => ({ packageName, sku }))
1641
1709
  });
1642
1710
  }
@@ -1644,74 +1712,89 @@ function createApiClient(options) {
1644
1712
  purchases: {
1645
1713
  async getProduct(packageName, productId, token) {
1646
1714
  const { data } = await http.get(
1647
- `/${packageName}/purchases/products/${productId}/tokens/${token}`
1715
+ `/${p(packageName)}/purchases/products/${p(productId)}/tokens/${p(token)}`
1648
1716
  );
1649
1717
  return data;
1650
1718
  },
1651
1719
  async acknowledgeProduct(packageName, productId, token, body) {
1652
1720
  await http.post(
1653
- `/${packageName}/purchases/products/${productId}/tokens/${token}:acknowledge`,
1721
+ `/${p(packageName)}/purchases/products/${p(productId)}/tokens/${p(token)}:acknowledge`,
1654
1722
  body
1655
1723
  );
1656
1724
  },
1657
1725
  async consumeProduct(packageName, productId, token) {
1658
- await http.post(`/${packageName}/purchases/products/${productId}/tokens/${token}:consume`);
1726
+ await http.post(
1727
+ `/${p(packageName)}/purchases/products/${p(productId)}/tokens/${p(token)}:consume`
1728
+ );
1659
1729
  },
1660
1730
  async getSubscriptionV2(packageName, token) {
1661
1731
  const { data } = await http.get(
1662
- `/${packageName}/purchases/subscriptionsv2/tokens/${token}`
1732
+ `/${p(packageName)}/purchases/subscriptionsv2/tokens/${p(token)}`
1663
1733
  );
1664
1734
  return data;
1665
1735
  },
1666
1736
  async getSubscriptionV1(packageName, subscriptionId, token) {
1667
- if (typeof process !== "undefined" && process.emitWarning) {
1668
- process.emitWarning(
1669
- "purchases.subscriptions.get (v1) is deprecated by Google (shutdown Aug 2027). Use getSubscriptionV2() instead.",
1670
- "DeprecationWarning"
1671
- );
1672
- }
1737
+ warnOnce(
1738
+ "GPC_DEP001",
1739
+ "purchases.subscriptions.get (v1) is deprecated by Google (shutdown Aug 2027). Use getSubscriptionV2() instead."
1740
+ );
1673
1741
  const { data } = await http.get(
1674
- `/${packageName}/purchases/subscriptions/${subscriptionId}/tokens/${token}`
1742
+ `/${p(packageName)}/purchases/subscriptions/${p(subscriptionId)}/tokens/${p(token)}`
1675
1743
  );
1676
1744
  return data;
1677
1745
  },
1678
1746
  async cancelSubscription(packageName, subscriptionId, token) {
1747
+ warnOnce(
1748
+ "GPC_DEP002",
1749
+ "purchases.subscriptions.cancel (v1) is deprecated by Google (shutdown Aug 2027). Use cancelSubscriptionV2() instead."
1750
+ );
1679
1751
  await http.post(
1680
- `/${packageName}/purchases/subscriptions/${subscriptionId}/tokens/${token}:cancel`
1752
+ `/${p(packageName)}/purchases/subscriptions/${p(subscriptionId)}/tokens/${p(token)}:cancel`
1681
1753
  );
1682
1754
  },
1683
1755
  async deferSubscription(packageName, subscriptionId, token, body) {
1756
+ warnOnce(
1757
+ "GPC_DEP003",
1758
+ "purchases.subscriptions.defer (v1) is deprecated by Google (shutdown Aug 2027). Use deferSubscriptionV2() instead."
1759
+ );
1684
1760
  const { data } = await http.post(
1685
- `/${packageName}/purchases/subscriptions/${subscriptionId}/tokens/${token}:defer`,
1761
+ `/${p(packageName)}/purchases/subscriptions/${p(subscriptionId)}/tokens/${p(token)}:defer`,
1686
1762
  body
1687
1763
  );
1688
1764
  return data;
1689
1765
  },
1690
1766
  async acknowledgeSubscription(packageName, subscriptionId, token, body) {
1767
+ warnOnce(
1768
+ "GPC_DEP004",
1769
+ "purchases.subscriptions.acknowledge (v1) is deprecated by Google (shutdown Aug 2027). No direct v2 replacement exists; prefer subscriptionsv2 surfaces where possible."
1770
+ );
1691
1771
  await http.post(
1692
- `/${packageName}/purchases/subscriptions/${subscriptionId}/tokens/${token}:acknowledge`,
1772
+ `/${p(packageName)}/purchases/subscriptions/${p(subscriptionId)}/tokens/${p(token)}:acknowledge`,
1693
1773
  body ?? {}
1694
1774
  );
1695
1775
  },
1696
1776
  async revokeSubscriptionV2(packageName, token, body) {
1697
1777
  await http.post(
1698
- `/${packageName}/purchases/subscriptionsv2/tokens/${token}:revoke`,
1778
+ `/${p(packageName)}/purchases/subscriptionsv2/tokens/${p(token)}:revoke`,
1699
1779
  body ?? {}
1700
1780
  );
1701
1781
  },
1702
1782
  async cancelSubscriptionV2(packageName, token, body) {
1703
- await http.post(`/${packageName}/purchases/subscriptionsv2/tokens/${token}:cancel`, body);
1783
+ await http.post(
1784
+ `/${p(packageName)}/purchases/subscriptionsv2/tokens/${p(token)}:cancel`,
1785
+ body
1786
+ );
1704
1787
  },
1705
1788
  async deferSubscriptionV2(packageName, token, body) {
1706
1789
  const { data } = await http.post(
1707
- `/${packageName}/purchases/subscriptionsv2/tokens/${token}:defer`,
1790
+ `/${p(packageName)}/purchases/subscriptionsv2/tokens/${p(token)}:defer`,
1708
1791
  body
1709
1792
  );
1710
1793
  return data;
1711
1794
  },
1712
1795
  async getProductV2(packageName, token) {
1713
1796
  const { data } = await http.get(
1714
- `/${packageName}/purchases/productsv2/tokens/${token}`
1797
+ `/${p(packageName)}/purchases/productsv2/tokens/${p(token)}`
1715
1798
  );
1716
1799
  return data;
1717
1800
  },
@@ -1726,7 +1809,7 @@ function createApiClient(options) {
1726
1809
  if (options2?.token) params["token"] = options2.token;
1727
1810
  const hasParams = Object.keys(params).length > 0;
1728
1811
  const { data } = await http.get(
1729
- `/${packageName}/purchases/voidedpurchases`,
1812
+ `/${p(packageName)}/purchases/voidedpurchases`,
1730
1813
  hasParams ? params : void 0
1731
1814
  );
1732
1815
  return data;
@@ -1734,24 +1817,24 @@ function createApiClient(options) {
1734
1817
  },
1735
1818
  orders: {
1736
1819
  async get(packageName, orderId) {
1737
- const { data } = await http.get(`/${packageName}/orders/${orderId}`);
1820
+ const { data } = await http.get(`/${p(packageName)}/orders/${p(orderId)}`);
1738
1821
  return data;
1739
1822
  },
1740
1823
  async batchGet(packageName, orderIds) {
1741
1824
  const { data } = await http.post(
1742
- `/${packageName}/orders:batchGet`,
1825
+ `/${p(packageName)}/orders:batchGet`,
1743
1826
  { orderIds }
1744
1827
  );
1745
1828
  return data.orders || [];
1746
1829
  },
1747
1830
  async refund(packageName, orderId, body) {
1748
- await http.post(`/${packageName}/orders/${orderId}:refund`, body);
1831
+ await http.post(`/${p(packageName)}/orders/${p(orderId)}:refund`, body);
1749
1832
  }
1750
1833
  },
1751
1834
  monetization: {
1752
1835
  async convertRegionPrices(packageName, price) {
1753
1836
  const { data } = await http.post(
1754
- `/${packageName}/pricing:convertRegionPrices`,
1837
+ `/${p(packageName)}/pricing:convertRegionPrices`,
1755
1838
  price
1756
1839
  );
1757
1840
  return data;
@@ -1761,7 +1844,7 @@ function createApiClient(options) {
1761
1844
  async list(packageName, reportType, year, month) {
1762
1845
  const monthStr = String(month).padStart(2, "0");
1763
1846
  const { data } = await http.get(
1764
- `/${packageName}/reports/${reportType}/${year}/${monthStr}`
1847
+ `/${p(packageName)}/reports/${reportType}/${year}/${monthStr}`
1765
1848
  );
1766
1849
  return data;
1767
1850
  }
@@ -1769,20 +1852,20 @@ function createApiClient(options) {
1769
1852
  testers: {
1770
1853
  async get(packageName, editId, track) {
1771
1854
  const { data } = await http.get(
1772
- `/${packageName}/edits/${editId}/testers/${track}`
1855
+ `/${p(packageName)}/edits/${p(editId)}/testers/${p(track)}`
1773
1856
  );
1774
1857
  return data;
1775
1858
  },
1776
1859
  async update(packageName, editId, track, testersData) {
1777
1860
  const { data } = await http.put(
1778
- `/${packageName}/edits/${editId}/testers/${track}`,
1861
+ `/${p(packageName)}/edits/${p(editId)}/testers/${p(track)}`,
1779
1862
  testersData
1780
1863
  );
1781
1864
  return data;
1782
1865
  },
1783
1866
  async patch(packageName, editId, track, testersData) {
1784
1867
  const { data } = await http.patch(
1785
- `/${packageName}/edits/${editId}/testers/${track}`,
1868
+ `/${p(packageName)}/edits/${p(editId)}/testers/${p(track)}`,
1786
1869
  testersData
1787
1870
  );
1788
1871
  return data;
@@ -1792,7 +1875,7 @@ function createApiClient(options) {
1792
1875
  async upload(packageName, editId, versionCode, filePath, fileType) {
1793
1876
  const deobType = fileType || "proguard";
1794
1877
  const { data } = await http.upload(
1795
- `/${packageName}/edits/${editId}/apks/${versionCode}/deobfuscationFiles/${deobType}`,
1878
+ `/${p(packageName)}/edits/${p(editId)}/apks/${p(String(versionCode))}/deobfuscationFiles/${deobType}`,
1796
1879
  filePath,
1797
1880
  "application/octet-stream"
1798
1881
  );
@@ -1813,27 +1896,27 @@ function createApiClient(options) {
1813
1896
  if (versionCode !== void 0) params["versionCode"] = String(versionCode);
1814
1897
  const hasParams = Object.keys(params).length > 0;
1815
1898
  const { data } = await http.get(
1816
- `/${packageName}/appRecoveries`,
1899
+ `/${p(packageName)}/appRecoveries`,
1817
1900
  hasParams ? params : void 0
1818
1901
  );
1819
1902
  return data.recoveryActions || [];
1820
1903
  },
1821
1904
  async cancel(packageName, appRecoveryId) {
1822
- await http.post(`/${packageName}/appRecoveries/${appRecoveryId}:cancel`);
1905
+ await http.post(`/${p(packageName)}/appRecoveries/${p(appRecoveryId)}:cancel`);
1823
1906
  },
1824
1907
  async deploy(packageName, appRecoveryId) {
1825
- await http.post(`/${packageName}/appRecoveries/${appRecoveryId}:deploy`);
1908
+ await http.post(`/${p(packageName)}/appRecoveries/${p(appRecoveryId)}:deploy`);
1826
1909
  },
1827
1910
  async create(packageName, request) {
1828
1911
  const { data } = await http.post(
1829
- `/${packageName}/appRecoveries`,
1912
+ `/${p(packageName)}/appRecoveries`,
1830
1913
  request
1831
1914
  );
1832
1915
  return data;
1833
1916
  },
1834
1917
  async addTargeting(packageName, appRecoveryId, targeting) {
1835
1918
  const { data } = await http.post(
1836
- `/${packageName}/appRecoveries/${appRecoveryId}:addTargeting`,
1919
+ `/${p(packageName)}/appRecoveries/${p(appRecoveryId)}:addTargeting`,
1837
1920
  targeting
1838
1921
  );
1839
1922
  return data;
@@ -1842,20 +1925,20 @@ function createApiClient(options) {
1842
1925
  externalTransactions: {
1843
1926
  async create(packageName, body) {
1844
1927
  const { data } = await http.post(
1845
- `/${packageName}/externalTransactions`,
1928
+ `/${p(packageName)}/externalTransactions`,
1846
1929
  body
1847
1930
  );
1848
1931
  return data;
1849
1932
  },
1850
1933
  async get(packageName, transactionId) {
1851
1934
  const { data } = await http.get(
1852
- `/${packageName}/externalTransactions/${transactionId}`
1935
+ `/${p(packageName)}/externalTransactions/${p(transactionId)}`
1853
1936
  );
1854
1937
  return data;
1855
1938
  },
1856
1939
  async refund(packageName, transactionId, refundData) {
1857
1940
  const { data } = await http.post(
1858
- `/${packageName}/externalTransactions/${transactionId}:refund`,
1941
+ `/${p(packageName)}/externalTransactions/${p(transactionId)}:refund`,
1859
1942
  refundData
1860
1943
  );
1861
1944
  return data;
@@ -1864,19 +1947,19 @@ function createApiClient(options) {
1864
1947
  deviceTiers: {
1865
1948
  async list(packageName) {
1866
1949
  const { data } = await http.get(
1867
- `/${packageName}/deviceTierConfigs`
1950
+ `/${p(packageName)}/deviceTierConfigs`
1868
1951
  );
1869
1952
  return data.deviceTierConfigs || [];
1870
1953
  },
1871
1954
  async get(packageName, configId) {
1872
1955
  const { data } = await http.get(
1873
- `/${packageName}/deviceTierConfigs/${configId}`
1956
+ `/${p(packageName)}/deviceTierConfigs/${p(configId)}`
1874
1957
  );
1875
1958
  return data;
1876
1959
  },
1877
1960
  async create(packageName, config) {
1878
1961
  const { data } = await http.post(
1879
- `/${packageName}/deviceTierConfigs`,
1962
+ `/${p(packageName)}/deviceTierConfigs`,
1880
1963
  config
1881
1964
  );
1882
1965
  return data;
@@ -1889,14 +1972,14 @@ function createApiClient(options) {
1889
1972
  if (options2?.pageSize) params["pageSize"] = String(options2.pageSize);
1890
1973
  const hasParams = Object.keys(params).length > 0;
1891
1974
  const { data } = await http.get(
1892
- `/${packageName}/oneTimeProducts`,
1975
+ `/${p(packageName)}/oneTimeProducts`,
1893
1976
  hasParams ? params : void 0
1894
1977
  );
1895
1978
  return data;
1896
1979
  },
1897
1980
  async get(packageName, productId) {
1898
1981
  const { data } = await http.get(
1899
- `/${packageName}/oneTimeProducts/${productId}`
1982
+ `/${p(packageName)}/oneTimeProducts/${p(productId)}`
1900
1983
  );
1901
1984
  return data;
1902
1985
  },
@@ -1908,7 +1991,7 @@ function createApiClient(options) {
1908
1991
  allowMissing: "true"
1909
1992
  });
1910
1993
  const { data } = await http.patch(
1911
- `/${packageName}/oneTimeProducts/${productId}?${params.toString()}`,
1994
+ `/${p(packageName)}/oneTimeProducts/${p(productId)}?${params.toString()}`,
1912
1995
  body
1913
1996
  );
1914
1997
  return data;
@@ -1918,22 +2001,22 @@ function createApiClient(options) {
1918
2001
  if (updateMask) params["updateMask"] = updateMask;
1919
2002
  params["regionsVersion.version"] = regionsVersion || DEFAULT_REGIONS_VERSION;
1920
2003
  applyMutationOptions(params, options2);
1921
- const path = `/${packageName}/oneTimeProducts/${productId}?${new URLSearchParams(params).toString()}`;
2004
+ const path = `/${p(packageName)}/oneTimeProducts/${p(productId)}?${new URLSearchParams(params).toString()}`;
1922
2005
  const { data } = await http.patch(path, body);
1923
2006
  return data;
1924
2007
  },
1925
2008
  async delete(packageName, productId) {
1926
- await http.delete(`/${packageName}/oneTimeProducts/${productId}`);
2009
+ await http.delete(`/${p(packageName)}/oneTimeProducts/${p(productId)}`);
1927
2010
  },
1928
2011
  async listOffers(packageName, productId, purchaseOptionId = "-") {
1929
2012
  const { data } = await http.get(
1930
- `/${packageName}/oneTimeProducts/${productId}/purchaseOptions/${purchaseOptionId}/offers`
2013
+ `/${p(packageName)}/oneTimeProducts/${p(productId)}/purchaseOptions/${p(purchaseOptionId)}/offers`
1931
2014
  );
1932
2015
  return data;
1933
2016
  },
1934
2017
  async getOffer(packageName, productId, purchaseOptionId, offerId) {
1935
2018
  const { data } = await http.get(
1936
- `/${packageName}/oneTimeProducts/${productId}/purchaseOptions/${purchaseOptionId}/offers/${offerId}`
2019
+ `/${p(packageName)}/oneTimeProducts/${p(productId)}/purchaseOptions/${p(purchaseOptionId)}/offers/${p(offerId)}`
1937
2020
  );
1938
2021
  return data;
1939
2022
  },
@@ -1942,7 +2025,7 @@ function createApiClient(options) {
1942
2025
  "regionsVersion.version": regionsVersion || DEFAULT_REGIONS_VERSION
1943
2026
  });
1944
2027
  const { data } = await http.post(
1945
- `/${packageName}/oneTimeProducts/${productId}/purchaseOptions/${purchaseOptionId}/offers?${params.toString()}`,
2028
+ `/${p(packageName)}/oneTimeProducts/${p(productId)}/purchaseOptions/${p(purchaseOptionId)}/offers?${params.toString()}`,
1946
2029
  body
1947
2030
  );
1948
2031
  return data;
@@ -1952,44 +2035,44 @@ function createApiClient(options) {
1952
2035
  if (updateMask) params["updateMask"] = updateMask;
1953
2036
  params["regionsVersion.version"] = regionsVersion || DEFAULT_REGIONS_VERSION;
1954
2037
  applyMutationOptions(params, options2);
1955
- const path = `/${packageName}/oneTimeProducts/${productId}/purchaseOptions/${purchaseOptionId}/offers/${offerId}?${new URLSearchParams(params).toString()}`;
2038
+ const path = `/${p(packageName)}/oneTimeProducts/${p(productId)}/purchaseOptions/${p(purchaseOptionId)}/offers/${p(offerId)}?${new URLSearchParams(params).toString()}`;
1956
2039
  const { data } = await http.patch(path, body);
1957
2040
  return data;
1958
2041
  },
1959
2042
  async deleteOffer(packageName, productId, purchaseOptionId, offerId) {
1960
2043
  await http.delete(
1961
- `/${packageName}/oneTimeProducts/${productId}/purchaseOptions/${purchaseOptionId}/offers/${offerId}`
2044
+ `/${p(packageName)}/oneTimeProducts/${p(productId)}/purchaseOptions/${p(purchaseOptionId)}/offers/${p(offerId)}`
1962
2045
  );
1963
2046
  },
1964
2047
  async batchGet(packageName, productIds) {
1965
2048
  const params = productIds.map((id) => `productIds=${encodeURIComponent(id)}`).join("&");
1966
2049
  const { data } = await http.get(
1967
- `/${packageName}/oneTimeProducts:batchGet?${params}`
2050
+ `/${p(packageName)}/oneTimeProducts:batchGet?${params}`
1968
2051
  );
1969
2052
  return data.oneTimeProducts || [];
1970
2053
  },
1971
2054
  async batchUpdate(packageName, requests) {
1972
2055
  const { data } = await http.post(
1973
- `/${packageName}/oneTimeProducts:batchUpdate`,
2056
+ `/${p(packageName)}/oneTimeProducts:batchUpdate`,
1974
2057
  requests
1975
2058
  );
1976
2059
  return data;
1977
2060
  },
1978
2061
  async batchDelete(packageName, productIds) {
1979
- await http.post(`/${packageName}/oneTimeProducts:batchDelete`, {
2062
+ await http.post(`/${p(packageName)}/oneTimeProducts:batchDelete`, {
1980
2063
  requests: productIds.map((id) => ({ productId: id }))
1981
2064
  });
1982
2065
  },
1983
2066
  // Purchase option batch operations
1984
2067
  async batchDeletePurchaseOptions(packageName, productId, requests) {
1985
2068
  await http.post(
1986
- `/${packageName}/oneTimeProducts/${productId}/purchaseOptions:batchDelete`,
2069
+ `/${p(packageName)}/oneTimeProducts/${p(productId)}/purchaseOptions:batchDelete`,
1987
2070
  { requests }
1988
2071
  );
1989
2072
  },
1990
2073
  async batchUpdatePurchaseOptionStates(packageName, productId, requests) {
1991
2074
  const { data } = await http.post(
1992
- `/${packageName}/oneTimeProducts/${productId}/purchaseOptions:batchUpdateStates`,
2075
+ `/${p(packageName)}/oneTimeProducts/${p(productId)}/purchaseOptions:batchUpdateStates`,
1993
2076
  { requests }
1994
2077
  );
1995
2078
  return data;
@@ -1997,13 +2080,13 @@ function createApiClient(options) {
1997
2080
  // Offer lifecycle
1998
2081
  async activateOffer(packageName, productId, purchaseOptionId, offerId) {
1999
2082
  const { data } = await http.post(
2000
- `/${packageName}/oneTimeProducts/${productId}/purchaseOptions/${purchaseOptionId}/offers/${offerId}:activate`
2083
+ `/${p(packageName)}/oneTimeProducts/${p(productId)}/purchaseOptions/${p(purchaseOptionId)}/offers/${p(offerId)}:activate`
2001
2084
  );
2002
2085
  return data;
2003
2086
  },
2004
2087
  async deactivateOffer(packageName, productId, purchaseOptionId, offerId) {
2005
2088
  const { data } = await http.post(
2006
- `/${packageName}/oneTimeProducts/${productId}/purchaseOptions/${purchaseOptionId}/offers/${offerId}:deactivate`
2089
+ `/${p(packageName)}/oneTimeProducts/${p(productId)}/purchaseOptions/${p(purchaseOptionId)}/offers/${p(offerId)}:deactivate`
2007
2090
  );
2008
2091
  return data;
2009
2092
  },
@@ -2011,35 +2094,35 @@ function createApiClient(options) {
2011
2094
  async cancelOffer(packageName, productId, purchaseOptionId, offerId, latencyTolerance) {
2012
2095
  const body = latencyTolerance ? { latencyTolerance } : {};
2013
2096
  const { data } = await http.post(
2014
- `/${packageName}/oneTimeProducts/${productId}/purchaseOptions/${purchaseOptionId}/offers/${offerId}:cancel`,
2097
+ `/${p(packageName)}/oneTimeProducts/${p(productId)}/purchaseOptions/${p(purchaseOptionId)}/offers/${p(offerId)}:cancel`,
2015
2098
  body
2016
2099
  );
2017
2100
  return data;
2018
2101
  },
2019
2102
  async batchGetOffers(packageName, productId, purchaseOptionId, requests) {
2020
2103
  const { data } = await http.post(
2021
- `/${packageName}/oneTimeProducts/${productId}/purchaseOptions/${purchaseOptionId}/offers:batchGet`,
2104
+ `/${p(packageName)}/oneTimeProducts/${p(productId)}/purchaseOptions/${p(purchaseOptionId)}/offers:batchGet`,
2022
2105
  { requests }
2023
2106
  );
2024
2107
  return data;
2025
2108
  },
2026
2109
  async batchUpdateOffers(packageName, productId, purchaseOptionId, requests) {
2027
2110
  const { data } = await http.post(
2028
- `/${packageName}/oneTimeProducts/${productId}/purchaseOptions/${purchaseOptionId}/offers:batchUpdate`,
2111
+ `/${p(packageName)}/oneTimeProducts/${p(productId)}/purchaseOptions/${p(purchaseOptionId)}/offers:batchUpdate`,
2029
2112
  { requests }
2030
2113
  );
2031
2114
  return data;
2032
2115
  },
2033
2116
  async batchUpdateOfferStates(packageName, productId, purchaseOptionId, requests) {
2034
2117
  const { data } = await http.post(
2035
- `/${packageName}/oneTimeProducts/${productId}/purchaseOptions/${purchaseOptionId}/offers:batchUpdateStates`,
2118
+ `/${p(packageName)}/oneTimeProducts/${p(productId)}/purchaseOptions/${p(purchaseOptionId)}/offers:batchUpdateStates`,
2036
2119
  { requests }
2037
2120
  );
2038
2121
  return data;
2039
2122
  },
2040
2123
  async batchDeleteOffers(packageName, productId, purchaseOptionId, requests) {
2041
2124
  await http.post(
2042
- `/${packageName}/oneTimeProducts/${productId}/purchaseOptions/${purchaseOptionId}/offers:batchDelete`,
2125
+ `/${p(packageName)}/oneTimeProducts/${p(productId)}/purchaseOptions/${p(purchaseOptionId)}/offers:batchDelete`,
2043
2126
  { requests }
2044
2127
  );
2045
2128
  }
@@ -2047,7 +2130,7 @@ function createApiClient(options) {
2047
2130
  internalAppSharing: {
2048
2131
  async uploadBundle(packageName, bundlePath) {
2049
2132
  const { data } = await http.uploadInternal(
2050
- `/${packageName}/artifacts/bundle`,
2133
+ `/${p(packageName)}/artifacts/bundle`,
2051
2134
  bundlePath,
2052
2135
  "application/octet-stream"
2053
2136
  );
@@ -2055,7 +2138,7 @@ function createApiClient(options) {
2055
2138
  },
2056
2139
  async uploadApk(packageName, apkPath) {
2057
2140
  const { data } = await http.uploadInternal(
2058
- `/${packageName}/artifacts/apk`,
2141
+ `/${p(packageName)}/artifacts/apk`,
2059
2142
  apkPath,
2060
2143
  "application/vnd.android.package-archive"
2061
2144
  );
@@ -2065,37 +2148,39 @@ function createApiClient(options) {
2065
2148
  generatedApks: {
2066
2149
  async list(packageName, versionCode) {
2067
2150
  const { data } = await http.get(
2068
- `/${packageName}/generatedApks/${versionCode}`
2151
+ `/${p(packageName)}/generatedApks/${p(String(versionCode))}`
2069
2152
  );
2070
2153
  return data.generatedApks || [];
2071
2154
  },
2072
2155
  async download(packageName, versionCode, id) {
2073
- return http.download(`/${packageName}/generatedApks/${versionCode}/download/${id}`);
2156
+ return http.download(
2157
+ `/${p(packageName)}/generatedApks/${p(String(versionCode))}/download/${p(id)}`
2158
+ );
2074
2159
  }
2075
2160
  },
2076
2161
  systemApks: {
2077
2162
  async create(packageName, versionCode, variant) {
2078
2163
  const { data } = await http.post(
2079
- `/${packageName}/systemApks/${versionCode}/variants`,
2164
+ `/${p(packageName)}/systemApks/${p(String(versionCode))}/variants`,
2080
2165
  variant
2081
2166
  );
2082
2167
  return data;
2083
2168
  },
2084
2169
  async list(packageName, versionCode) {
2085
2170
  const { data } = await http.get(
2086
- `/${packageName}/systemApks/${versionCode}/variants`
2171
+ `/${p(packageName)}/systemApks/${p(String(versionCode))}/variants`
2087
2172
  );
2088
2173
  return data;
2089
2174
  },
2090
2175
  async get(packageName, versionCode, variantId) {
2091
2176
  const { data } = await http.get(
2092
- `/${packageName}/systemApks/${versionCode}/variants/${variantId}`
2177
+ `/${p(packageName)}/systemApks/${p(String(versionCode))}/variants/${p(String(variantId))}`
2093
2178
  );
2094
2179
  return data;
2095
2180
  },
2096
2181
  async download(packageName, versionCode, variantId) {
2097
2182
  return http.download(
2098
- `/${packageName}/systemApks/${versionCode}/variants/${variantId}:download`
2183
+ `/${p(packageName)}/systemApks/${p(String(versionCode))}/variants/${p(String(variantId))}:download`
2099
2184
  );
2100
2185
  }
2101
2186
  }
@@ -2119,9 +2204,7 @@ function createReportingClient(options) {
2119
2204
  },
2120
2205
  async getMetricSetFreshness(packageName, metricSet) {
2121
2206
  await limiter.acquire("reporting");
2122
- const { data } = await http.get(
2123
- `/apps/${packageName}/${metricSet}`
2124
- );
2207
+ const { data } = await http.get(`/apps/${packageName}/${metricSet}`);
2125
2208
  return data;
2126
2209
  },
2127
2210
  async getAnomalies(packageName) {
@@ -2141,13 +2224,23 @@ function createReportingClient(options) {
2141
2224
  );
2142
2225
  return data;
2143
2226
  },
2144
- async searchErrorReports(packageName, issueName, pageSize, pageToken) {
2227
+ async searchErrorReports(packageName, issueId, pageSize, pageToken) {
2228
+ if (!/^\d+$/.test(issueId)) {
2229
+ throw new PlayApiError(
2230
+ "Invalid error issue ID: must be numeric.",
2231
+ "API_INVALID_INPUT",
2232
+ void 0,
2233
+ "Provide a valid numeric error issue ID from errorIssues.search."
2234
+ );
2235
+ }
2145
2236
  await limiter.acquire("reporting");
2146
- const params = {};
2237
+ const params = {
2238
+ filter: `errorIssueId = ${issueId}`
2239
+ };
2147
2240
  if (pageSize) params["pageSize"] = String(pageSize);
2148
2241
  if (pageToken) params["pageToken"] = pageToken;
2149
2242
  const { data } = await http.get(
2150
- `/apps/${packageName}/errorIssues/${issueName}/reports`,
2243
+ `/apps/${packageName}/errorReports:search`,
2151
2244
  params
2152
2245
  );
2153
2246
  return data;