@gpc-cli/api 1.0.25 → 1.0.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.d.ts +67 -1
- package/dist/index.js +167 -74
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/http.ts","../src/resumable-upload.ts","../src/client.ts","../src/rate-limiter.ts","../src/reporting-client.ts","../src/users-client.ts","../src/games-client.ts","../src/enterprise-client.ts","../src/paginate.ts"],"sourcesContent":["export class PlayApiError extends Error {\n public readonly exitCode = 4;\n constructor(\n message: string,\n public readonly code: string,\n public readonly statusCode?: number,\n public readonly suggestion?: string,\n ) {\n super(message);\n this.name = \"PlayApiError\";\n }\n toJSON() {\n return {\n success: false,\n error: {\n code: this.code,\n message: this.message,\n suggestion: this.suggestion,\n },\n };\n }\n}\n","import { readFile, stat } from \"node:fs/promises\";\nimport { resolve, isAbsolute } from \"node:path\";\nimport { PlayApiError } from \"./errors.js\";\nimport { resumableUpload, RESUMABLE_THRESHOLD } from \"./resumable-upload.js\";\nimport type { ApiClientOptions, ApiResponse, ResumableUploadOptions } from \"./types.js\";\n\n/** Strip HTML tags and collapse whitespace from a string. */\nfunction stripHtml(text: string): string {\n return text\n .replace(/<[^>]*>/g, \" \")\n .replace(/&[a-z]+;/gi, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\n/** Extract a short, safe error summary from API response body (no tokens/secrets). */\nfunction sanitizeErrorBody(body: string): string {\n try {\n const parsed = JSON.parse(body) as {\n error?: { message?: string; status?: string; code?: number };\n };\n if (parsed?.error?.message) {\n return `${parsed.error.code ?? \"?\"} ${parsed.error.status ?? \"\"}: ${parsed.error.message}`.trim();\n }\n } catch {\n // not JSON — may be HTML error page\n }\n // Strip HTML tags before truncating\n const cleaned = body.startsWith(\"<\") ? stripHtml(body) : body;\n return cleaned.length > 200 ? cleaned.slice(0, 200) + \"...\" : cleaned;\n}\n\n/** Validate upload file path to prevent path traversal. */\nfunction validateFilePath(filePath: string): string {\n const resolved = resolve(filePath);\n if (!isAbsolute(resolved)) {\n throw new PlayApiError(\n \"Invalid file path\",\n \"API_INVALID_PATH\",\n undefined,\n \"File path must resolve to an absolute path.\",\n );\n }\n // Block obvious traversal patterns in the original input\n if (filePath.includes(\"\\0\")) {\n throw new PlayApiError(\n \"Invalid file path: null bytes not allowed\",\n \"API_INVALID_PATH\",\n undefined,\n \"Provide a valid file path without null bytes.\",\n );\n }\n return resolved;\n}\n\nconst BASE_URL = \"https://androidpublisher.googleapis.com/androidpublisher/v3/applications\";\n\nconst UPLOAD_BASE_URL =\n \"https://androidpublisher.googleapis.com/upload/androidpublisher/v3/applications\";\n\nconst INTERNAL_SHARING_UPLOAD_BASE_URL =\n \"https://androidpublisher.googleapis.com/upload/internalappsharing/v3/applications\";\n\nexport interface HttpClient {\n get<T>(path: string, params?: Record<string, string>): Promise<ApiResponse<T>>;\n post<T>(path: string, body?: unknown): Promise<ApiResponse<T>>;\n put<T>(path: string, body?: unknown): Promise<ApiResponse<T>>;\n patch<T>(path: string, body?: unknown): Promise<ApiResponse<T>>;\n delete<T>(path: string): Promise<ApiResponse<T>>;\n upload<T>(path: string, filePath: string, contentType: string): Promise<ApiResponse<T>>;\n uploadResumable<T>(\n path: string,\n filePath: string,\n contentType: string,\n options?: ResumableUploadOptions,\n ): Promise<ApiResponse<T>>;\n uploadInternal<T>(path: string, filePath: string, contentType: string): Promise<ApiResponse<T>>;\n download(path: string): Promise<ArrayBuffer>;\n}\n\nfunction envInt(name: string): number | undefined {\n const val = process.env[name];\n if (val === undefined) return undefined;\n const n = Number(val);\n return Number.isFinite(n) ? n : undefined;\n}\n\nfunction resolveOption(explicit: number | undefined, envName: string, fallback: number): number {\n return explicit ?? envInt(envName) ?? fallback;\n}\n\ninterface ErrorMapping {\n code: string;\n message: string;\n suggestion: string;\n}\n\n/**\n * Pattern-match Google Play API error responses to return specific,\n * actionable error messages. Returns undefined if no pattern matches.\n */\nfunction enhanceApiError(status: number, body: string): ErrorMapping | undefined {\n let errorMsg = \"\";\n try {\n const parsed = JSON.parse(body) as { error?: { message?: string; status?: string } };\n errorMsg = parsed?.error?.message?.toLowerCase() ?? \"\";\n } catch {\n errorMsg = body.toLowerCase();\n }\n\n // — Duplicate version code (400/403)\n if ((status === 400 || status === 403) && errorMsg.includes(\"version code\") && errorMsg.includes(\"already been used\")) {\n const match = errorMsg.match(/version code (\\d+)/);\n const vc = match?.[1] ?? \"?\";\n return {\n code: \"API_DUPLICATE_VERSION_CODE\",\n message: `Version code ${vc} has already been uploaded to this app.`,\n suggestion: [\n `Increment versionCode in your build.gradle (or build.gradle.kts) and rebuild.`,\n `Check the current version with: gpc releases status --track production`,\n ].join(\"\\n\"),\n };\n }\n\n // — Version code too low (400/403)\n if (\n (status === 400 || status === 403) &&\n errorMsg.includes(\"version code\") &&\n (errorMsg.includes(\"lower\") || errorMsg.includes(\"not allowed\") || errorMsg.includes(\"not greater\"))\n ) {\n return {\n code: \"API_VERSION_CODE_TOO_LOW\",\n message: \"Version code is lower than the current version on the target track.\",\n suggestion: [\n \"Google Play requires version codes to increase with each upload.\",\n \"Check the current version with: gpc releases status --track <track>\",\n \"Then set a higher versionCode in your build.gradle and rebuild.\",\n ].join(\"\\n\"),\n };\n }\n\n // — Package name mismatch (400/403)\n if (\n (status === 400 || status === 403) &&\n (errorMsg.includes(\"package name\") || errorMsg.includes(\"applicationid\")) &&\n errorMsg.includes(\"does not match\")\n ) {\n return {\n code: \"API_PACKAGE_NAME_MISMATCH\",\n message: \"The package name in the uploaded bundle does not match the target app.\",\n suggestion: [\n \"Verify your applicationId in build.gradle matches the app you're uploading to.\",\n \"Check the configured package with: gpc config show\",\n \"Or specify explicitly with: --app com.example.yourapp\",\n ].join(\"\\n\"),\n };\n }\n\n // — App not found (404)\n if (\n status === 404 &&\n (errorMsg.includes(\"applicationnotfound\") ||\n errorMsg.includes(\"no application was found\") ||\n errorMsg.includes(\"application not found\"))\n ) {\n return {\n code: \"API_APP_NOT_FOUND\",\n message: \"This app was not found in your Google Play developer account.\",\n suggestion: [\n \"Verify the package name is correct.\",\n \"Ensure the app has been created in the Google Play Console.\",\n \"List available apps with: gpc apps list\",\n ].join(\"\\n\"),\n };\n }\n\n // — Insufficient permissions (403)\n if (\n status === 403 &&\n (errorMsg.includes(\"permission\") ||\n errorMsg.includes(\"insufficient\") ||\n errorMsg.includes(\"caller does not have\"))\n ) {\n return {\n code: \"API_INSUFFICIENT_PERMISSIONS\",\n message: \"The service account does not have permission for this operation.\",\n suggestion: [\n \"In Google Play Console → Users and permissions → find your service account email.\",\n \"Grant the required permissions (e.g., 'Release to production' for uploads).\",\n \"Run gpc doctor to verify your credentials and permissions.\",\n ].join(\"\\n\"),\n };\n }\n\n // — Edit conflict (409)\n if (status === 409) {\n return {\n code: \"API_EDIT_CONFLICT\",\n message: \"An edit conflict occurred — another edit session is open for this app.\",\n suggestion: [\n \"This usually means another process has an open edit (CI pipeline, Play Console, or another gpc instance).\",\n \"Wait a few minutes and retry — GPC will auto-retry once.\",\n \"Or discard the stale edit in the Google Play Console.\",\n ].join(\"\\n\"),\n };\n }\n\n // — Bundle too large (400/413)\n if (\n status === 413 ||\n ((status === 400 || status === 403) &&\n (errorMsg.includes(\"too large\") || (errorMsg.includes(\"exceeds\") && errorMsg.includes(\"size\"))))\n ) {\n return {\n code: \"API_BUNDLE_TOO_LARGE\",\n message: \"The uploaded file exceeds Google Play's size limit.\",\n suggestion: [\n \"AAB files must be under 2 GB, APK files under 1 GB.\",\n \"Use Android App Bundles (AAB) instead of APK for smaller file sizes.\",\n \"Run gpc preflight <file> to check bundle size before uploading.\",\n ].join(\"\\n\"),\n };\n }\n\n // — Invalid bundle/APK (400)\n if (\n status === 400 &&\n (errorMsg.includes(\"invalid bundle\") ||\n errorMsg.includes(\"invalid apk\") ||\n errorMsg.includes(\"unable to parse\") ||\n errorMsg.includes(\"malformed apk\") ||\n errorMsg.includes(\"malformed bundle\"))\n ) {\n return {\n code: \"API_INVALID_BUNDLE\",\n message: \"Google Play rejected the uploaded file as invalid or malformed.\",\n suggestion: [\n \"Ensure the file is a properly signed AAB or APK.\",\n \"Common causes: corrupted file, unsigned bundle, wrong file format.\",\n \"Run gpc preflight <file> for offline validation.\",\n \"Rebuild with: ./gradlew bundleRelease\",\n ].join(\"\\n\"),\n };\n }\n\n // — Track not found (404)\n if (status === 404 && errorMsg.includes(\"track\") && (errorMsg.includes(\"not found\") || errorMsg.includes(\"does not exist\"))) {\n return {\n code: \"API_TRACK_NOT_FOUND\",\n message: \"The specified track does not exist for this app.\",\n suggestion: [\n \"Built-in tracks: internal, alpha, beta, production.\",\n \"List custom tracks with: gpc tracks list\",\n \"Create a custom track with: gpc tracks create <name>\",\n ].join(\"\\n\"),\n };\n }\n\n // — Release notes too long (400)\n if (status === 400 && errorMsg.includes(\"release notes\") && (errorMsg.includes(\"too long\") || errorMsg.includes(\"character limit\"))) {\n return {\n code: \"API_RELEASE_NOTES_TOO_LONG\",\n message: \"Release notes exceed the 500-character limit.\",\n suggestion: [\n \"Shorten the release notes to 500 characters or fewer per language.\",\n \"Preview current notes with: gpc releases notes get --track <track>\",\n ].join(\"\\n\"),\n };\n }\n\n // — Rollout already completed (400)\n if (status === 400 && (errorMsg.includes(\"cannot change rollout\") || (errorMsg.includes(\"release\") && errorMsg.includes(\"already completed\")))) {\n return {\n code: \"API_ROLLOUT_ALREADY_COMPLETED\",\n message: \"The release is already at full rollout (100%) and cannot be changed.\",\n suggestion: [\n \"A completed release cannot have its rollout percentage modified.\",\n \"To deploy a new version: gpc releases upload --track <track>\",\n ].join(\"\\n\"),\n };\n }\n\n // — Edit expired (400 FAILED_PRECONDITION)\n if (status === 400 && errorMsg.includes(\"edit\") && (errorMsg.includes(\"expired\") || errorMsg.includes(\"failed_precondition\"))) {\n return {\n code: \"API_EDIT_EXPIRED\",\n message: \"The edit session has expired.\",\n suggestion: [\n \"Edit sessions last about 1 hour.\",\n \"Retry the operation — GPC will open a fresh edit automatically.\",\n ].join(\"\\n\"),\n };\n }\n\n return undefined;\n}\n\nfunction mapStatusToError(status: number, body: string): { code: string; message?: string; suggestion?: string } {\n // Try specific pattern matching first\n const enhanced = enhanceApiError(status, body);\n if (enhanced) return enhanced;\n\n // Fall back to generic status-based mapping\n switch (status) {\n case 400:\n return { code: \"API_HTTP_400\", suggestion: \"Check request parameters and try again.\" };\n case 401:\n return {\n code: \"API_UNAUTHORIZED\",\n suggestion: \"Check that your access token is valid and not expired. Run: gpc doctor\",\n };\n case 403:\n return {\n code: \"API_FORBIDDEN\",\n suggestion: \"Ensure the service account has the required permissions. Run: gpc doctor\",\n };\n case 404:\n return {\n code: \"API_NOT_FOUND\",\n suggestion: \"Verify the package name and resource IDs are correct. Run: gpc apps list\",\n };\n case 413:\n return {\n code: \"API_BUNDLE_TOO_LARGE\",\n suggestion: \"The uploaded file is too large. AAB limit: 2 GB, APK limit: 1 GB.\",\n };\n case 429:\n return {\n code: \"API_RATE_LIMITED\",\n suggestion: \"Too many requests. GPC will retry automatically.\",\n };\n default:\n if (status >= 500) {\n return {\n code: \"API_SERVER_ERROR\",\n suggestion: \"Google Play API server error. GPC will retry automatically.\",\n };\n }\n return { code: `API_HTTP_${status}` };\n }\n}\n\nfunction isRetryable(status: number): boolean {\n return status === 408 || status === 429 || status >= 500;\n}\n\nfunction jitteredDelay(base: number, attempt: number, max: number): number {\n const exponential = base * 2 ** attempt;\n const capped = Math.min(exponential, max);\n return capped * (0.5 + Math.random() * 0.5);\n}\n\nexport function createHttpClient(options: ApiClientOptions): HttpClient {\n const maxRetries = resolveOption(options.maxRetries, \"GPC_MAX_RETRIES\", 5);\n const timeout = resolveOption(options.timeout, \"GPC_TIMEOUT\", 30_000);\n const uploadTimeoutExplicit = options.uploadTimeout ?? envInt(\"GPC_UPLOAD_TIMEOUT\");\n const baseDelay = resolveOption(options.baseDelay, \"GPC_BASE_DELAY\", 1_000);\n const maxDelay = resolveOption(options.maxDelay, \"GPC_MAX_DELAY\", 60_000);\n const onRetry = options.onRetry;\n\n async function request<T>(\n method: string,\n path: string,\n body?: unknown,\n params?: Record<string, string>,\n ): Promise<ApiResponse<T>> {\n let url = `${options.baseUrl ?? BASE_URL}${path}`;\n if (params) {\n const search = new URLSearchParams(params);\n url += `?${search.toString()}`;\n }\n\n // Fetch token once before retries — the auth layer handles its own caching and mutex\n let token = await options.auth.getAccessToken();\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n if (attempt > 0) {\n const delay = jitteredDelay(baseDelay, attempt - 1, maxDelay);\n await new Promise((r) => setTimeout(r, delay));\n }\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeout);\n\n try {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n \"Accept-Encoding\": \"gzip, deflate\",\n Connection: \"keep-alive\",\n };\n\n const init: RequestInit = {\n method,\n headers,\n signal: controller.signal,\n keepalive: true,\n };\n\n if (body !== undefined) {\n init.body = JSON.stringify(body);\n }\n\n const response = await fetch(url, init);\n\n if (response.ok) {\n const text = await response.text();\n const data = text ? (JSON.parse(text) as T) : ({} as T);\n return { data, status: response.status };\n }\n\n const errorBody = await response.text();\n const mapped = mapStatusToError(response.status, errorBody);\n\n const err = new PlayApiError(\n mapped.message ?? `${method} ${path} failed with status ${response.status}: ${sanitizeErrorBody(errorBody)}`,\n mapped.code,\n response.status,\n mapped.suggestion,\n );\n\n if (isRetryable(response.status) && attempt < maxRetries) {\n lastError = err;\n const delay = jitteredDelay(baseDelay, attempt, maxDelay);\n onRetry?.({\n attempt: attempt + 1,\n method,\n path,\n status: response.status,\n error: err.message,\n delayMs: Math.round(delay),\n timestamp: new Date().toISOString(),\n });\n continue;\n }\n\n // On 401, refresh token once before giving up\n if (response.status === 401 && attempt < maxRetries) {\n token = await options.auth.getAccessToken();\n lastError = err;\n continue;\n }\n\n throw err;\n } catch (error) {\n if (error instanceof PlayApiError) {\n throw error;\n }\n\n if (error instanceof DOMException && error.name === \"AbortError\") {\n const timeoutErr = new PlayApiError(\n `${method} ${path} timed out after ${timeout}ms`,\n \"API_TIMEOUT\",\n undefined,\n \"The request exceeded the configured timeout. Consider increasing the timeout value.\",\n );\n if (attempt < maxRetries) {\n lastError = timeoutErr;\n onRetry?.({\n attempt: attempt + 1,\n method,\n path,\n error: timeoutErr.message,\n delayMs: Math.round(jitteredDelay(baseDelay, attempt, maxDelay)),\n timestamp: new Date().toISOString(),\n });\n continue;\n }\n throw timeoutErr;\n }\n\n const networkErr = new PlayApiError(\n `${method} ${path} failed: ${error instanceof Error ? error.message : String(error)}`,\n \"API_NETWORK_ERROR\",\n undefined,\n \"A network error occurred. Check your internet connection.\",\n );\n if (attempt < maxRetries) {\n lastError = networkErr;\n onRetry?.({\n attempt: attempt + 1,\n method,\n path,\n error: networkErr.message,\n delayMs: Math.round(jitteredDelay(baseDelay, attempt, maxDelay)),\n timestamp: new Date().toISOString(),\n });\n continue;\n }\n throw networkErr;\n } finally {\n clearTimeout(timer);\n }\n }\n\n // Should not reach here, but just in case\n throw (\n lastError ??\n new PlayApiError(\n \"Request failed\",\n \"API_NETWORK_ERROR\",\n undefined,\n \"Check your network connection and try again. Use --verbose for details.\",\n )\n );\n }\n\n /** Calculate upload timeout: explicit value, or auto-scale from file size (1 MB/s minimum throughput + 30s overhead). */\n function computeUploadTimeout(fileSizeBytes: number): number {\n if (uploadTimeoutExplicit !== undefined) return uploadTimeoutExplicit;\n // Base: 30s overhead + 1s per MB (assumes ~1 MB/s minimum upload speed)\n const sizeMb = fileSizeBytes / (1024 * 1024);\n return Math.max(timeout, 30_000 + Math.ceil(sizeMb) * 1_000);\n }\n\n async function uploadRequest<T>(\n path: string,\n filePath: string,\n contentType: string,\n baseUrl: string = UPLOAD_BASE_URL,\n ): Promise<ApiResponse<T>> {\n const url = `${baseUrl}${path}`;\n const safeFilePath = validateFilePath(filePath);\n const fileBuffer = await readFile(safeFilePath);\n const effectiveTimeout = computeUploadTimeout(fileBuffer.byteLength);\n\n // Fetch token once before retries\n let token = await options.auth.getAccessToken();\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n if (attempt > 0) {\n const delay = jitteredDelay(baseDelay, attempt - 1, maxDelay);\n await new Promise((r) => setTimeout(r, delay));\n }\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), effectiveTimeout);\n\n try {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": contentType,\n \"Accept-Encoding\": \"gzip, deflate\",\n Connection: \"keep-alive\",\n };\n\n const response = await fetch(url, {\n method: \"POST\",\n headers,\n body: fileBuffer,\n signal: controller.signal,\n keepalive: true,\n });\n\n if (response.ok) {\n const text = await response.text();\n const data = text ? (JSON.parse(text) as T) : ({} as T);\n return { data, status: response.status };\n }\n\n const errorBody = await response.text();\n const mapped = mapStatusToError(response.status, errorBody);\n\n const err = new PlayApiError(\n mapped.message ?? `Upload failed with status ${response.status}: ${sanitizeErrorBody(errorBody)}`,\n mapped.code,\n response.status,\n mapped.suggestion,\n );\n\n if (isRetryable(response.status) && attempt < maxRetries) {\n lastError = err;\n const delay = jitteredDelay(baseDelay, attempt, maxDelay);\n onRetry?.({\n attempt: attempt + 1,\n method: \"POST\",\n path: `upload ${path}`,\n status: response.status,\n error: err.message,\n delayMs: Math.round(delay),\n timestamp: new Date().toISOString(),\n });\n continue;\n }\n\n // On 401, refresh token once before giving up\n if (response.status === 401 && attempt < maxRetries) {\n token = await options.auth.getAccessToken();\n lastError = err;\n continue;\n }\n\n throw err;\n } catch (error) {\n if (error instanceof PlayApiError) {\n throw error;\n }\n\n if (error instanceof DOMException && error.name === \"AbortError\") {\n const sizeMb = Math.round(fileBuffer.byteLength / (1024 * 1024));\n const timeoutErr = new PlayApiError(\n `POST upload ${path} timed out after ${effectiveTimeout}ms (file: ${sizeMb} MB)`,\n \"API_TIMEOUT\",\n undefined,\n `Upload timed out. Set GPC_UPLOAD_TIMEOUT=${effectiveTimeout * 2} (ms) or use --timeout to increase.`,\n );\n if (attempt < maxRetries) {\n lastError = timeoutErr;\n onRetry?.({\n attempt: attempt + 1,\n method: \"POST\",\n path: `upload ${path}`,\n error: timeoutErr.message,\n delayMs: Math.round(jitteredDelay(baseDelay, attempt, maxDelay)),\n timestamp: new Date().toISOString(),\n });\n continue;\n }\n throw timeoutErr;\n }\n\n const networkErr = new PlayApiError(\n `POST upload ${path} failed: ${error instanceof Error ? error.message : String(error)}`,\n \"API_NETWORK_ERROR\",\n undefined,\n \"A network error occurred. Check your internet connection.\",\n );\n if (attempt < maxRetries) {\n lastError = networkErr;\n onRetry?.({\n attempt: attempt + 1,\n method: \"POST\",\n path: `upload ${path}`,\n error: networkErr.message,\n delayMs: Math.round(jitteredDelay(baseDelay, attempt, maxDelay)),\n timestamp: new Date().toISOString(),\n });\n continue;\n }\n throw networkErr;\n } finally {\n clearTimeout(timer);\n }\n }\n\n throw (\n lastError ??\n new PlayApiError(\n \"Upload request failed\",\n \"API_NETWORK_ERROR\",\n undefined,\n \"Check your network connection and try again. Use --verbose for details.\",\n )\n );\n }\n\n return {\n get<T>(path: string, params?: Record<string, string>) {\n return request<T>(\"GET\", path, undefined, params);\n },\n post<T>(path: string, body?: unknown) {\n return request<T>(\"POST\", path, body);\n },\n put<T>(path: string, body?: unknown) {\n return request<T>(\"PUT\", path, body);\n },\n patch<T>(path: string, body?: unknown) {\n return request<T>(\"PATCH\", path, body);\n },\n delete<T>(path: string) {\n return request<T>(\"DELETE\", path);\n },\n upload<T>(path: string, filePath: string, contentType: string) {\n return uploadRequest<T>(path, filePath, contentType);\n },\n async uploadResumable<T>(\n path: string,\n filePath: string,\n contentType: string,\n uploadOptions?: ResumableUploadOptions,\n ) {\n const safeFilePath = validateFilePath(filePath);\n const fileStats = await stat(safeFilePath);\n\n // For small files, fall back to simple upload (less overhead)\n const threshold = envInt(\"GPC_UPLOAD_RESUMABLE_THRESHOLD\") ?? RESUMABLE_THRESHOLD;\n if (fileStats.size < threshold && !uploadOptions?.resumeSessionUri) {\n // Fire progress callbacks for consistency\n uploadOptions?.onProgress?.({\n bytesUploaded: 0,\n totalBytes: fileStats.size,\n percent: 0,\n bytesPerSecond: 0,\n etaSeconds: 0,\n });\n const result = await uploadRequest<T>(path, safeFilePath, contentType);\n uploadOptions?.onProgress?.({\n bytesUploaded: fileStats.size,\n totalBytes: fileStats.size,\n percent: 100,\n bytesPerSecond: 0,\n etaSeconds: 0,\n });\n return result;\n }\n\n const uploadUrl = `${UPLOAD_BASE_URL}${path}`;\n return resumableUpload<T>(\n uploadUrl,\n safeFilePath,\n contentType,\n {\n getAccessToken: () => options.auth.getAccessToken(),\n maxRetries,\n baseDelay,\n maxDelay,\n onRetry,\n },\n uploadOptions,\n );\n },\n uploadInternal<T>(path: string, filePath: string, contentType: string) {\n return uploadRequest<T>(path, filePath, contentType, INTERNAL_SHARING_UPLOAD_BASE_URL);\n },\n async download(path: string): Promise<ArrayBuffer> {\n const url = `${options.baseUrl ?? BASE_URL}${path}`;\n const token = await options.auth.getAccessToken();\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Accept-Encoding\": \"gzip, deflate\",\n Connection: \"keep-alive\",\n },\n signal: controller.signal,\n keepalive: true,\n });\n\n if (!response.ok) {\n const errorBody = await response.text();\n const mapped = mapStatusToError(response.status, errorBody);\n throw new PlayApiError(\n mapped.message ?? `GET ${path} failed with status ${response.status}: ${sanitizeErrorBody(errorBody)}`,\n mapped.code,\n response.status,\n mapped.suggestion,\n );\n }\n\n return await response.arrayBuffer();\n } finally {\n clearTimeout(timer);\n }\n },\n };\n}\n","import { open, stat } from \"node:fs/promises\";\nimport type { FileHandle } from \"node:fs/promises\";\nimport { PlayApiError } from \"./errors.js\";\nimport type { ApiResponse, ResumableUploadOptions } from \"./types.js\";\n\n/** 256 KB — Google requires chunk sizes to be multiples of this. */\nconst CHUNK_ALIGNMENT = 256 * 1024;\n\n/**\n * Google's resumable upload protocol uses HTTP 308 for \"Resume Incomplete\",\n * but RFC 7238 standardized 308 as \"Permanent Redirect\". Node.js fetch\n * follows 308 redirects automatically, so GPC would never see the raw 308.\n *\n * Google's solution: send `X-GUploader-No-308: yes` on every request.\n * The server then replies with 200 OK and sets the response header\n * `X-Http-Status-Code-Override: 308` instead of using a real 308 status.\n *\n * This matches the behavior of Google's official Go client library.\n * See: google-api-go-client/internal/gensupport/resumable.go\n */\nconst GUPLOADER_NO_308_HEADER = \"X-GUploader-No-308\";\n\n/** Check if a 200 response is actually a \"308 Resume Incomplete\" in disguise. */\nfunction isResumeIncomplete(response: Response): boolean {\n return response.headers.get(\"X-Http-Status-Code-Override\") === \"308\";\n}\n\n/** 8 MB — default chunk size (multiple of 256 KB). */\nconst DEFAULT_CHUNK_SIZE = 8 * 1024 * 1024;\n\n/** Files below this threshold use simple upload instead. */\nexport const RESUMABLE_THRESHOLD = 5 * 1024 * 1024; // 5 MB\n\nfunction envInt(name: string): number | undefined {\n const val = process.env[name];\n if (val === undefined) return undefined;\n const n = Number(val);\n return Number.isFinite(n) ? n : undefined;\n}\n\nfunction resolveChunkSize(explicit?: number): number {\n const size = explicit ?? envInt(\"GPC_UPLOAD_CHUNK_SIZE\") ?? DEFAULT_CHUNK_SIZE;\n if (size < CHUNK_ALIGNMENT || size % CHUNK_ALIGNMENT !== 0) {\n throw new PlayApiError(\n `Chunk size must be a multiple of 256 KB (got ${size} bytes)`,\n \"UPLOAD_INVALID_CHUNK_SIZE\",\n undefined,\n `Use a multiple of 262144 (256 KB). Common values: 1048576 (1 MB), 8388608 (8 MB), 16777216 (16 MB).`,\n );\n }\n return size;\n}\n\nfunction jitteredDelay(base: number, attempt: number, max: number): number {\n const exponential = base * 2 ** attempt;\n const capped = Math.min(exponential, max);\n return capped * (0.5 + Math.random() * 0.5);\n}\n\ninterface ResumableUploadContext {\n getAccessToken: () => Promise<string>;\n maxRetries: number;\n baseDelay: number;\n maxDelay: number;\n onRetry?: (info: {\n attempt: number;\n method: string;\n path: string;\n error: string;\n delayMs: number;\n timestamp: string;\n }) => void;\n}\n\n/**\n * Google Play resumable upload protocol.\n *\n * 1. Initiate session → POST with X-Upload-Content-Type/Length, get Location header\n * 2. Stream chunks → PUT chunks with Content-Range to session URI\n * 3. Resume on failure → PUT with Content-Range: bytes * /total to query progress\n */\nexport async function resumableUpload<T>(\n uploadUrl: string,\n filePath: string,\n contentType: string,\n ctx: ResumableUploadContext,\n options?: ResumableUploadOptions,\n): Promise<ApiResponse<T>> {\n const chunkSize = resolveChunkSize(options?.chunkSize);\n const maxResumeAttempts = options?.maxResumeAttempts ?? 5;\n const onProgress = options?.onProgress;\n\n const fileStats = await stat(filePath);\n const totalBytes = fileStats.size;\n\n // Step 1: Initiate resumable session (or resume existing)\n let sessionUri = options?.resumeSessionUri;\n if (!sessionUri) {\n sessionUri = await initiateSession(uploadUrl, contentType, totalBytes, ctx);\n }\n\n // Step 2: Stream file in chunks\n const startTime = Date.now();\n let offset = 0;\n\n // If resuming, query the server for where we left off\n if (options?.resumeSessionUri) {\n offset = await queryProgress(sessionUri, totalBytes, ctx);\n }\n\n let fh: FileHandle | undefined;\n try {\n fh = await open(filePath, \"r\");\n const chunkBuffer = Buffer.alloc(chunkSize);\n\n while (offset < totalBytes) {\n const remaining = totalBytes - offset;\n const bytesToRead = Math.min(chunkSize, remaining);\n const { bytesRead } = await fh.read(chunkBuffer, 0, bytesToRead, offset);\n\n if (bytesRead === 0) break;\n\n // Always copy the chunk to avoid race conditions — fetch may read the body\n // asynchronously while we overwrite chunkBuffer on the next iteration\n const chunk = Buffer.from(chunkBuffer.buffer, chunkBuffer.byteOffset, bytesRead);\n const rangeEnd = offset + bytesRead - 1;\n const contentRange = `bytes ${offset}-${rangeEnd}/${totalBytes}`;\n\n let result: ChunkResult<T> | undefined;\n for (let attempt = 0; attempt <= maxResumeAttempts; attempt++) {\n if (attempt > 0) {\n const delay = jitteredDelay(1000, attempt - 1, 30_000);\n await new Promise((r) => setTimeout(r, delay));\n\n // Query server for actual progress before retrying\n try {\n const serverOffset = await queryProgress(sessionUri, totalBytes, ctx);\n if (serverOffset >= totalBytes) {\n // Upload is fully complete — server has all bytes\n // Fetch the completion response via one more query\n const completionResult = await fetchCompletionResponse<T>(sessionUri, totalBytes, ctx);\n if (completionResult) {\n result = completionResult;\n break;\n }\n // If we can't get the response, treat as complete without body\n result = { complete: true, response: { data: {} as T, status: 200 } };\n break;\n }\n if (serverOffset >= offset + bytesRead) {\n // Server already has this chunk but upload not finished, advance\n result = { complete: false };\n break;\n }\n if (serverOffset > offset) {\n // Partial — skip to where server is, but we'll need to re-read\n // For simplicity, just retry the whole chunk since it's small enough\n }\n } catch {\n // Query failed — just retry the PUT\n }\n\n ctx.onRetry?.({\n attempt,\n method: \"PUT\",\n path: sessionUri,\n error: `Chunk upload failed at offset ${offset}, retrying`,\n delayMs: Math.round(delay),\n timestamp: new Date().toISOString(),\n });\n }\n\n result = await sendChunk<T>(sessionUri, chunk, contentRange, ctx);\n if (result) break;\n }\n\n if (!result) {\n throw new PlayApiError(\n `Upload failed: chunk at offset ${offset} could not be sent after ${maxResumeAttempts + 1} attempts`,\n \"UPLOAD_CHUNK_FAILED\",\n undefined,\n `The upload session is still valid for up to 1 week. Resume with: --resume-uri \"${sessionUri}\"`,\n );\n }\n\n offset += bytesRead;\n\n // Fire progress callback\n if (onProgress) {\n const elapsed = (Date.now() - startTime) / 1000;\n const bytesPerSecond = elapsed > 0 ? offset / elapsed : 0;\n const remainingBytes = totalBytes - offset;\n const etaSeconds = bytesPerSecond > 0 ? remainingBytes / bytesPerSecond : 0;\n\n onProgress({\n bytesUploaded: offset,\n totalBytes,\n percent: Math.round((offset / totalBytes) * 100),\n bytesPerSecond: Math.round(bytesPerSecond),\n etaSeconds: Math.round(etaSeconds),\n });\n }\n\n // If the server returned a final response (200/201), we're done\n if (result.complete && result.response) {\n return result.response;\n }\n }\n\n // All bytes sent but no completion response captured — verify with server\n try {\n const serverOffset = await queryProgress(sessionUri, totalBytes, ctx);\n if (serverOffset >= totalBytes) {\n // Upload IS complete — server confirmed. Fetch the resource.\n const completionResult = await fetchCompletionResponse<T>(sessionUri, totalBytes, ctx);\n if (completionResult?.response) {\n return completionResult.response;\n }\n // Server confirmed complete but no parseable body — return empty\n return { data: {} as T, status: 200 };\n }\n } catch {\n // Query failed — fall through to error\n }\n\n throw new PlayApiError(\n \"Upload finished sending all bytes but did not receive a completion response\",\n \"UPLOAD_NO_COMPLETION\",\n undefined,\n `The upload session may still be valid. Resume with: --resume-uri \"${sessionUri}\"`,\n );\n } finally {\n await fh?.close();\n }\n}\n\ninterface ChunkResult<T> {\n complete: boolean;\n response?: ApiResponse<T>;\n}\n\nasync function initiateSession(\n uploadUrl: string,\n contentType: string,\n totalBytes: number,\n ctx: ResumableUploadContext,\n): Promise<string> {\n const token = await ctx.getAccessToken();\n const url = uploadUrl.includes(\"?\")\n ? `${uploadUrl}&uploadType=resumable`\n : `${uploadUrl}?uploadType=resumable`;\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), 60_000); // 60s timeout for session initiation\n let response: Response;\n try {\n response = await fetch(url, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"X-Upload-Content-Type\": contentType,\n \"X-Upload-Content-Length\": String(totalBytes),\n \"Content-Length\": \"0\",\n },\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n\n if (!response.ok) {\n const body = await response.text();\n throw new PlayApiError(\n `Failed to initiate resumable upload: ${response.status} ${body.slice(0, 200)}`,\n \"UPLOAD_INITIATE_FAILED\",\n response.status,\n \"Check that the package name, edit ID, and credentials are correct.\",\n );\n }\n\n const location = response.headers.get(\"Location\");\n if (!location) {\n throw new PlayApiError(\n \"Resumable upload initiation did not return a session URI (Location header missing)\",\n \"UPLOAD_NO_SESSION_URI\",\n response.status,\n \"This is a Google API issue. Try again.\",\n );\n }\n\n return location;\n}\n\nasync function sendChunk<T>(\n sessionUri: string,\n chunk: Buffer,\n contentRange: string,\n ctx: ResumableUploadContext,\n): Promise<ChunkResult<T> | undefined> {\n const token = await ctx.getAccessToken();\n\n // Timeout: 30s base + 1s per MB of chunk data\n const chunkTimeoutMs = 30_000 + Math.ceil(chunk.byteLength / (1024 * 1024)) * 1_000;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), chunkTimeoutMs);\n let response: Response;\n try {\n response = await fetch(sessionUri, {\n method: \"PUT\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Length\": String(chunk.byteLength),\n \"Content-Range\": contentRange,\n [GUPLOADER_NO_308_HEADER]: \"yes\",\n },\n body: chunk,\n signal: controller.signal,\n redirect: \"manual\", // Belt-and-suspenders: don't follow redirects even without the header\n });\n } catch {\n // Network error or timeout — caller will retry\n return undefined;\n } finally {\n clearTimeout(timer);\n }\n\n // With X-GUploader-No-308, Google sends 200 OK for both \"chunk accepted\"\n // and \"upload complete\". Distinguish via X-Http-Status-Code-Override header.\n if (response.status === 200 || response.status === 201) {\n // Check if this is really a \"308 Resume Incomplete\" disguised as 200\n if (isResumeIncomplete(response)) {\n await response.body?.cancel();\n return { complete: false };\n }\n\n // Genuine 200/201 — upload complete, parse the resource body\n const text = await response.text();\n let data: T;\n try {\n data = text ? (JSON.parse(text) as T) : ({} as T);\n } catch {\n data = {} as T;\n }\n return { complete: true, response: { data, status: response.status } };\n }\n\n // Real 308 (fallback if X-GUploader-No-308 not honored) — chunk accepted\n if (response.status === 308) {\n await response.body?.cancel();\n return { complete: false };\n }\n\n // 404 — session not found (expired or invalid)\n if (response.status === 404) {\n throw new PlayApiError(\n \"Upload session not found. The session may have expired.\",\n \"UPLOAD_SESSION_NOT_FOUND\",\n 404,\n \"Start a new upload. Resumable upload sessions are valid for up to 1 week.\",\n );\n }\n\n // 410 — session gone\n if (response.status === 410) {\n throw new PlayApiError(\n \"Upload session has expired.\",\n \"UPLOAD_SESSION_EXPIRED\",\n 410,\n \"Start a new upload from the beginning.\",\n );\n }\n\n // 401 — token expired, refresh and retry\n if (response.status === 401) {\n await response.body?.cancel();\n return undefined;\n }\n\n // 5xx or 429 — retryable\n if (response.status === 429 || response.status >= 500) {\n await response.body?.cancel();\n return undefined;\n }\n\n // Non-retryable error\n const body = await response.text();\n throw new PlayApiError(\n `Upload chunk failed with status ${response.status}: ${body.slice(0, 200)}`,\n `UPLOAD_HTTP_${response.status}`,\n response.status,\n \"The upload encountered an unexpected error.\",\n );\n}\n\n/**\n * When queryProgress confirms the upload is complete (200/201),\n * re-query the session to get the final resource response body.\n */\nasync function fetchCompletionResponse<T>(\n sessionUri: string,\n totalBytes: number,\n ctx: ResumableUploadContext,\n): Promise<ChunkResult<T> | undefined> {\n const token = await ctx.getAccessToken();\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), 30_000);\n try {\n const response = await fetch(sessionUri, {\n method: \"PUT\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Length\": \"0\",\n \"Content-Range\": `bytes */${totalBytes}`,\n [GUPLOADER_NO_308_HEADER]: \"yes\",\n },\n signal: controller.signal,\n redirect: \"manual\",\n });\n\n // Genuine 200/201 (not a disguised 308) — upload complete with resource body\n if ((response.status === 200 || response.status === 201) && !isResumeIncomplete(response)) {\n const text = await response.text();\n let data: T;\n try {\n data = text ? (JSON.parse(text) as T) : ({} as T);\n } catch {\n data = {} as T;\n }\n return { complete: true, response: { data, status: response.status } };\n }\n\n await response.body?.cancel();\n return undefined;\n } catch {\n return undefined;\n } finally {\n clearTimeout(timer);\n }\n}\n\nasync function queryProgress(\n sessionUri: string,\n totalBytes: number,\n ctx: ResumableUploadContext,\n): Promise<number> {\n const token = await ctx.getAccessToken();\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), 30_000);\n let response: Response;\n try {\n response = await fetch(sessionUri, {\n method: \"PUT\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Length\": \"0\",\n \"Content-Range\": `bytes */${totalBytes}`,\n [GUPLOADER_NO_308_HEADER]: \"yes\",\n },\n signal: controller.signal,\n redirect: \"manual\",\n });\n } finally {\n clearTimeout(timer);\n }\n\n // With X-GUploader-No-308, a \"308 Resume Incomplete\" arrives as 200\n // with X-Http-Status-Code-Override: 308. Check the Range header for progress.\n if (response.status === 308 || isResumeIncomplete(response)) {\n await response.body?.cancel();\n const range = response.headers.get(\"Range\");\n if (range) {\n const match = range.match(/bytes=0-(\\d+)/);\n if (match) {\n return Number(match[1]) + 1;\n }\n }\n return 0;\n }\n\n // Genuine 200/201 without override — upload is actually complete\n if (response.status === 200 || response.status === 201) {\n await response.body?.cancel();\n return totalBytes;\n }\n\n // 404/410 — session expired\n if (response.status === 404 || response.status === 410) {\n await response.body?.cancel();\n throw new PlayApiError(\n \"Upload session has expired while querying progress.\",\n \"UPLOAD_SESSION_EXPIRED\",\n response.status,\n \"Start a new upload from the beginning.\",\n );\n }\n\n await response.body?.cancel();\n return 0;\n}\n","import { PlayApiError } from \"./errors.js\";\nimport { createHttpClient } from \"./http.js\";\nimport type { RateLimiter } from \"./rate-limiter.js\";\nimport type {\n ApiClientOptions,\n AppDetails,\n AppEdit,\n AppRecoveriesListResponse,\n AppRecoveryAction,\n AppRecoveryTargeting,\n CreateAppRecoveryActionRequest,\n BasePlanMigratePricesRequest,\n Bundle,\n BundleListResponse,\n ConvertRegionPricesRequest,\n ConvertRegionPricesResponse,\n CountryAvailability,\n DataSafety,\n DeobfuscationFile,\n DeobfuscationUploadResponse,\n DeviceTierConfig,\n DeviceTierConfigsListResponse,\n ExternalTransaction,\n ExternalTransactionRefund,\n ExternallyHostedApk,\n ExternallyHostedApkResponse,\n Image,\n ImageType,\n ImageUploadResponse,\n ImagesDeleteAllResponse,\n ImagesListResponse,\n InAppProduct,\n InAppProductsListResponse,\n Listing,\n ListingsListResponse,\n OffersListResponse,\n ProductPurchase,\n Release,\n ReportsListResponse,\n ReportType,\n Review,\n ReviewReplyRequest,\n ReviewReplyResponse,\n ReviewsListOptions,\n ReviewsListResponse,\n Subscription,\n SubscriptionDeferRequest,\n SubscriptionDeferResponse,\n SubscriptionOffer,\n SubscriptionPurchase,\n SubscriptionPurchaseV2,\n SubscriptionsListResponse,\n Testers,\n Track,\n TrackListResponse,\n VoidedPurchasesListResponse,\n OneTimeProduct,\n OneTimeProductsListResponse,\n OneTimeOffer,\n OneTimeOffersListResponse,\n InternalAppSharingArtifact,\n GeneratedApk,\n GeneratedApksPerVersion,\n PurchaseOption,\n PurchaseOptionsListResponse,\n InAppProductsBatchUpdateRequest,\n InAppProductsBatchUpdateResponse,\n ResumableUploadOptions,\n Order,\n BatchGetOrdersResponse,\n ProductPurchaseV2,\n SubscriptionsV2CancelRequest,\n SubscriptionsV2DeferRequest,\n SubscriptionsV2DeferResponse,\n ReleaseSummary,\n ReleasesListResponse,\n SubscriptionsBatchGetResponse,\n SubscriptionsBatchUpdateRequest,\n SubscriptionsBatchUpdateResponse,\n InAppProductsBatchDeleteRequest,\n} from \"./types.js\";\n\nexport interface PlayApiClient {\n edits: {\n insert(packageName: string): Promise<AppEdit>;\n get(packageName: string, editId: string): Promise<AppEdit>;\n validate(packageName: string, editId: string): Promise<AppEdit>;\n commit(packageName: string, editId: string): Promise<AppEdit>;\n delete(packageName: string, editId: string): Promise<void>;\n };\n\n details: {\n get(packageName: string, editId: string): Promise<AppDetails>;\n update(packageName: string, editId: string, details: Partial<AppDetails>): Promise<AppDetails>;\n patch(packageName: string, editId: string, partial: Partial<AppDetails>): Promise<AppDetails>;\n };\n\n bundles: {\n list(packageName: string, editId: string): Promise<Bundle[]>;\n upload(\n packageName: string,\n editId: string,\n filePath: string,\n uploadOptions?: ResumableUploadOptions,\n ): Promise<Bundle>;\n };\n\n tracks: {\n list(packageName: string, editId: string): Promise<Track[]>;\n get(packageName: string, editId: string, track: string): Promise<Track>;\n create(packageName: string, editId: string, trackName: string): Promise<Track>;\n update(packageName: string, editId: string, track: string, release: Release): Promise<Track>;\n patch(packageName: string, editId: string, track: string, release: Release): Promise<Track>;\n };\n\n releases: {\n list(packageName: string, track: string): Promise<ReleaseSummary[]>;\n };\n\n apks: {\n addExternallyHosted(\n packageName: string,\n editId: string,\n data: ExternallyHostedApk,\n ): Promise<ExternallyHostedApkResponse>;\n };\n\n listings: {\n list(packageName: string, editId: string): Promise<Listing[]>;\n get(packageName: string, editId: string, language: string): Promise<Listing>;\n update(\n packageName: string,\n editId: string,\n language: string,\n listing: Omit<Listing, \"language\">,\n ): Promise<Listing>;\n patch(\n packageName: string,\n editId: string,\n language: string,\n partial: Partial<Omit<Listing, \"language\">>,\n ): Promise<Listing>;\n delete(packageName: string, editId: string, language: string): Promise<void>;\n deleteAll(packageName: string, editId: string): Promise<void>;\n };\n\n images: {\n list(\n packageName: string,\n editId: string,\n language: string,\n imageType: ImageType,\n ): Promise<Image[]>;\n upload(\n packageName: string,\n editId: string,\n language: string,\n imageType: ImageType,\n filePath: string,\n ): Promise<Image>;\n delete(\n packageName: string,\n editId: string,\n language: string,\n imageType: ImageType,\n imageId: string,\n ): Promise<void>;\n deleteAll(\n packageName: string,\n editId: string,\n language: string,\n imageType: ImageType,\n ): Promise<Image[]>;\n };\n\n countryAvailability: {\n get(packageName: string, editId: string, track: string): Promise<CountryAvailability>;\n };\n\n dataSafety: {\n get(packageName: string): Promise<DataSafety>;\n update(packageName: string, data: DataSafety): Promise<DataSafety>;\n };\n\n reviews: {\n list(packageName: string, options?: ReviewsListOptions): Promise<ReviewsListResponse>;\n get(packageName: string, reviewId: string, translationLanguage?: string): Promise<Review>;\n reply(packageName: string, reviewId: string, replyText: string): Promise<ReviewReplyResponse>;\n };\n\n subscriptions: {\n list(\n packageName: string,\n options?: { pageToken?: string; pageSize?: number },\n ): Promise<SubscriptionsListResponse>;\n get(packageName: string, productId: string): Promise<Subscription>;\n create(packageName: string, data: Subscription, productId?: string): Promise<Subscription>;\n update(\n packageName: string,\n productId: string,\n data: Subscription,\n updateMask?: string,\n regionsVersion?: string,\n ): Promise<Subscription>;\n delete(packageName: string, productId: string): Promise<void>;\n batchGet(packageName: string, productIds: string[]): Promise<Subscription[]>;\n batchUpdate(packageName: string, requests: SubscriptionsBatchUpdateRequest): Promise<SubscriptionsBatchUpdateResponse>;\n activateBasePlan(\n packageName: string,\n productId: string,\n basePlanId: string,\n ): Promise<Subscription>;\n deactivateBasePlan(\n packageName: string,\n productId: string,\n basePlanId: string,\n ): Promise<Subscription>;\n deleteBasePlan(packageName: string, productId: string, basePlanId: string): Promise<void>;\n migratePrices(\n packageName: string,\n productId: string,\n basePlanId: string,\n body: BasePlanMigratePricesRequest,\n ): Promise<Subscription>;\n listOffers(\n packageName: string,\n productId: string,\n basePlanId: string,\n ): Promise<OffersListResponse>;\n getOffer(\n packageName: string,\n productId: string,\n basePlanId: string,\n offerId: string,\n ): Promise<SubscriptionOffer>;\n createOffer(\n packageName: string,\n productId: string,\n basePlanId: string,\n data: SubscriptionOffer,\n offerId?: string,\n ): Promise<SubscriptionOffer>;\n updateOffer(\n packageName: string,\n productId: string,\n basePlanId: string,\n offerId: string,\n data: SubscriptionOffer,\n updateMask?: string,\n regionsVersion?: string,\n ): Promise<SubscriptionOffer>;\n deleteOffer(\n packageName: string,\n productId: string,\n basePlanId: string,\n offerId: string,\n ): Promise<void>;\n activateOffer(\n packageName: string,\n productId: string,\n basePlanId: string,\n offerId: string,\n ): Promise<SubscriptionOffer>;\n deactivateOffer(\n packageName: string,\n productId: string,\n basePlanId: string,\n offerId: string,\n ): Promise<SubscriptionOffer>;\n };\n\n inappproducts: {\n list(\n packageName: string,\n options?: { token?: string; maxResults?: number },\n ): Promise<InAppProductsListResponse>;\n get(packageName: string, sku: string): Promise<InAppProduct>;\n create(\n packageName: string,\n data: InAppProduct,\n options?: { autoConvertMissingPrices?: boolean },\n ): Promise<InAppProduct>;\n update(\n packageName: string,\n sku: string,\n data: InAppProduct,\n options?: { autoConvertMissingPrices?: boolean; allowMissing?: boolean },\n ): Promise<InAppProduct>;\n delete(packageName: string, sku: string): Promise<void>;\n batchUpdate(\n packageName: string,\n requests: InAppProductsBatchUpdateRequest,\n ): Promise<InAppProductsBatchUpdateResponse>;\n batchGet(packageName: string, skus: string[]): Promise<InAppProduct[]>;\n batchDelete(packageName: string, skus: string[]): Promise<void>;\n };\n\n purchases: {\n getProduct(packageName: string, productId: string, token: string): Promise<ProductPurchase>;\n acknowledgeProduct(\n packageName: string,\n productId: string,\n token: string,\n body?: { developerPayload?: string },\n ): Promise<void>;\n consumeProduct(packageName: string, productId: string, token: string): Promise<void>;\n getSubscriptionV2(packageName: string, token: string): Promise<SubscriptionPurchaseV2>;\n getSubscriptionV1(\n packageName: string,\n subscriptionId: string,\n token: string,\n ): Promise<SubscriptionPurchase>;\n cancelSubscription(packageName: string, subscriptionId: string, token: string): Promise<void>;\n deferSubscription(\n packageName: string,\n subscriptionId: string,\n token: string,\n body: SubscriptionDeferRequest,\n ): Promise<SubscriptionDeferResponse>;\n acknowledgeSubscription(\n packageName: string,\n subscriptionId: string,\n token: string,\n body?: { developerPayload?: string },\n ): Promise<void>;\n revokeSubscriptionV2(packageName: string, token: string): Promise<void>;\n refundSubscriptionV2(packageName: string, token: string): Promise<void>;\n /** V2 cancel with cancellationType support. (Sep 2025) */\n cancelSubscriptionV2(\n packageName: string,\n token: string,\n body?: SubscriptionsV2CancelRequest,\n ): Promise<void>;\n /** V2 defer for subscriptions with add-ons. (Jan 2026) */\n deferSubscriptionV2(\n packageName: string,\n token: string,\n body: SubscriptionsV2DeferRequest,\n ): Promise<SubscriptionsV2DeferResponse>;\n /** V2 product purchase details for multi-offer OTPs. (Jun 2025) */\n getProductV2(\n packageName: string,\n token: string,\n ): Promise<ProductPurchaseV2>;\n listVoided(\n packageName: string,\n options?: { startTime?: string; endTime?: string; maxResults?: number; token?: string },\n ): Promise<VoidedPurchasesListResponse>;\n };\n\n orders: {\n get(packageName: string, orderId: string): Promise<Order>;\n batchGet(packageName: string, orderIds: string[]): Promise<Order[]>;\n refund(\n packageName: string,\n orderId: string,\n body?: { fullRefund?: boolean; proratedRefund?: boolean; revoke?: boolean },\n ): Promise<void>;\n };\n\n monetization: {\n convertRegionPrices(\n packageName: string,\n price: ConvertRegionPricesRequest,\n ): Promise<ConvertRegionPricesResponse>;\n };\n\n reports: {\n list(\n packageName: string,\n reportType: ReportType,\n year: number,\n month: number,\n ): Promise<ReportsListResponse>;\n };\n\n testers: {\n get(packageName: string, editId: string, track: string): Promise<Testers>;\n update(packageName: string, editId: string, track: string, testers: Testers): Promise<Testers>;\n };\n\n deobfuscation: {\n upload(\n packageName: string,\n editId: string,\n versionCode: number,\n filePath: string,\n ): Promise<DeobfuscationFile>;\n };\n\n appRecovery: {\n list(packageName: string, versionCode?: number): Promise<AppRecoveryAction[]>;\n cancel(packageName: string, appRecoveryId: string): Promise<void>;\n deploy(packageName: string, appRecoveryId: string): Promise<void>;\n create(\n packageName: string,\n request: CreateAppRecoveryActionRequest,\n ): Promise<AppRecoveryAction>;\n addTargeting(\n packageName: string,\n appRecoveryId: string,\n targeting: AppRecoveryTargeting,\n ): Promise<AppRecoveryAction>;\n };\n\n externalTransactions: {\n create(packageName: string, data: ExternalTransaction): Promise<ExternalTransaction>;\n get(packageName: string, transactionId: string): Promise<ExternalTransaction>;\n refund(\n packageName: string,\n transactionId: string,\n refundData: ExternalTransactionRefund,\n ): Promise<ExternalTransaction>;\n };\n\n deviceTiers: {\n list(packageName: string): Promise<DeviceTierConfig[]>;\n get(packageName: string, configId: string): Promise<DeviceTierConfig>;\n create(packageName: string, config: DeviceTierConfig): Promise<DeviceTierConfig>;\n };\n\n oneTimeProducts: {\n list(packageName: string): Promise<OneTimeProductsListResponse>;\n get(packageName: string, productId: string): Promise<OneTimeProduct>;\n create(packageName: string, product: OneTimeProduct): Promise<OneTimeProduct>;\n update(\n packageName: string,\n productId: string,\n product: Partial<OneTimeProduct>,\n updateMask?: string,\n regionsVersion?: string,\n ): Promise<OneTimeProduct>;\n delete(packageName: string, productId: string): Promise<void>;\n listOffers(packageName: string, productId: string): Promise<OneTimeOffersListResponse>;\n getOffer(packageName: string, productId: string, offerId: string): Promise<OneTimeOffer>;\n createOffer(packageName: string, productId: string, offer: OneTimeOffer): Promise<OneTimeOffer>;\n updateOffer(\n packageName: string,\n productId: string,\n offerId: string,\n offer: Partial<OneTimeOffer>,\n updateMask?: string,\n regionsVersion?: string,\n ): Promise<OneTimeOffer>;\n deleteOffer(packageName: string, productId: string, offerId: string): Promise<void>;\n };\n\n purchaseOptions: {\n list(packageName: string): Promise<PurchaseOptionsListResponse>;\n get(packageName: string, purchaseOptionId: string): Promise<PurchaseOption>;\n create(packageName: string, data: PurchaseOption): Promise<PurchaseOption>;\n activate(packageName: string, purchaseOptionId: string): Promise<PurchaseOption>;\n deactivate(packageName: string, purchaseOptionId: string): Promise<PurchaseOption>;\n };\n\n internalAppSharing: {\n uploadBundle(packageName: string, bundlePath: string): Promise<InternalAppSharingArtifact>;\n uploadApk(packageName: string, apkPath: string): Promise<InternalAppSharingArtifact>;\n };\n\n generatedApks: {\n list(packageName: string, versionCode: number): Promise<GeneratedApk[]>;\n download(packageName: string, versionCode: number, id: string): Promise<ArrayBuffer>;\n };\n}\n\nasync function rateLimit(limiter: RateLimiter | undefined, bucket: string): Promise<void> {\n if (limiter) await limiter.acquire(bucket);\n}\n\nexport function createApiClient(options: ApiClientOptions): PlayApiClient {\n const http = createHttpClient(options);\n const limiter = options.rateLimiter || undefined;\n\n return {\n edits: {\n async insert(packageName) {\n const { data } = await http.post<AppEdit>(`/${packageName}/edits`);\n return data;\n },\n\n async get(packageName, editId) {\n const { data } = await http.get<AppEdit>(`/${packageName}/edits/${editId}`);\n return data;\n },\n\n async validate(packageName, editId) {\n const { data } = await http.post<AppEdit>(`/${packageName}/edits/${editId}:validate`);\n return data;\n },\n\n async commit(packageName, editId) {\n const { data } = await http.post<AppEdit>(`/${packageName}/edits/${editId}:commit`);\n return data;\n },\n\n async delete(packageName, editId) {\n await http.delete(`/${packageName}/edits/${editId}`);\n },\n },\n\n details: {\n async get(packageName, editId) {\n const { data } = await http.get<AppDetails>(`/${packageName}/edits/${editId}/details`);\n return data;\n },\n\n async update(packageName, editId, details) {\n const { data } = await http.put<AppDetails>(\n `/${packageName}/edits/${editId}/details`,\n details,\n );\n return data;\n },\n\n async patch(packageName, editId, partial) {\n const { data } = await http.patch<AppDetails>(\n `/${packageName}/edits/${editId}/details`,\n partial,\n );\n return data;\n },\n },\n\n bundles: {\n async list(packageName, editId) {\n const { data } = await http.get<BundleListResponse>(\n `/${packageName}/edits/${editId}/bundles`,\n );\n return data.bundles;\n },\n\n async upload(packageName, editId, filePath, uploadOptions) {\n const { data } = await http.uploadResumable<Bundle>(\n `/${packageName}/edits/${editId}/bundles`,\n filePath,\n \"application/octet-stream\",\n uploadOptions,\n );\n if (!data || !data.versionCode) {\n throw new PlayApiError(\n \"Upload succeeded but no bundle data returned\",\n \"API_EMPTY_RESPONSE\",\n 200,\n \"This is unexpected. Retry the upload or contact Google Play support if the issue persists.\",\n );\n }\n return data;\n },\n },\n\n tracks: {\n async list(packageName, editId) {\n const { data } = await http.get<TrackListResponse>(\n `/${packageName}/edits/${editId}/tracks`,\n );\n return data.tracks;\n },\n\n async get(packageName, editId, track) {\n const { data } = await http.get<Track>(`/${packageName}/edits/${editId}/tracks/${track}`);\n return data;\n },\n\n async create(packageName, editId, trackName) {\n const { data } = await http.post<Track>(`/${packageName}/edits/${editId}/tracks`, {\n track: trackName,\n });\n return data;\n },\n\n async update(packageName, editId, track, release) {\n const { data } = await http.put<Track>(`/${packageName}/edits/${editId}/tracks/${track}`, {\n track,\n releases: [release],\n });\n return data;\n },\n\n async patch(packageName, editId, track, release) {\n const { data } = await http.patch<Track>(`/${packageName}/edits/${editId}/tracks/${track}`, {\n track,\n releases: [release],\n });\n return data;\n },\n },\n\n releases: {\n async list(packageName, track) {\n const { data } = await http.get<ReleasesListResponse>(\n `/${packageName}/tracks/${track}/releases`,\n );\n return data.releases ?? [];\n },\n },\n\n apks: {\n async addExternallyHosted(packageName, editId, apkData) {\n const { data } = await http.post<ExternallyHostedApkResponse>(\n `/${packageName}/edits/${editId}/apks/externallyHosted`,\n { externallyHostedApk: apkData },\n );\n return data;\n },\n },\n\n listings: {\n async list(packageName, editId) {\n const { data } = await http.get<ListingsListResponse>(\n `/${packageName}/edits/${editId}/listings`,\n );\n return data.listings || [];\n },\n\n async get(packageName, editId, language) {\n const { data } = await http.get<Listing>(\n `/${packageName}/edits/${editId}/listings/${language}`,\n );\n return data;\n },\n\n async update(packageName, editId, language, listing) {\n const { data } = await http.put<Listing>(\n `/${packageName}/edits/${editId}/listings/${language}`,\n listing,\n );\n return data;\n },\n\n async patch(packageName, editId, language, partial) {\n const { data } = await http.patch<Listing>(\n `/${packageName}/edits/${editId}/listings/${language}`,\n partial,\n );\n return data;\n },\n\n async delete(packageName, editId, language) {\n await http.delete(`/${packageName}/edits/${editId}/listings/${language}`);\n },\n\n async deleteAll(packageName, editId) {\n await http.delete(`/${packageName}/edits/${editId}/listings`);\n },\n },\n\n images: {\n async list(packageName, editId, language, imageType) {\n const { data } = await http.get<ImagesListResponse>(\n `/${packageName}/edits/${editId}/listings/${language}/${imageType}`,\n );\n return data.images || [];\n },\n\n async upload(packageName, editId, language, imageType, filePath) {\n const { data } = await http.upload<ImageUploadResponse>(\n `/${packageName}/edits/${editId}/listings/${language}/${imageType}`,\n filePath,\n filePath.endsWith(\".png\") ? \"image/png\" : \"image/jpeg\",\n );\n if (!data.image) {\n throw new PlayApiError(\n \"Upload succeeded but no image data returned\",\n \"API_EMPTY_RESPONSE\",\n 200,\n \"This is unexpected. Retry the upload or contact Google Play support if the issue persists.\",\n );\n }\n return data.image;\n },\n\n async delete(packageName, editId, language, imageType, imageId) {\n await http.delete(\n `/${packageName}/edits/${editId}/listings/${language}/${imageType}/${imageId}`,\n );\n },\n\n async deleteAll(packageName, editId, language, imageType) {\n const { data } = await http.delete<ImagesDeleteAllResponse>(\n `/${packageName}/edits/${editId}/listings/${language}/${imageType}`,\n );\n return data.deleted || [];\n },\n },\n\n countryAvailability: {\n async get(packageName, editId, track) {\n const { data } = await http.get<CountryAvailability>(\n `/${packageName}/edits/${editId}/countryAvailability/${track}`,\n );\n return data;\n },\n },\n\n dataSafety: {\n async get(packageName) {\n const { data } = await http.get<DataSafety>(`/${packageName}/dataSafety`);\n return data;\n },\n\n async update(packageName, body) {\n const { data } = await http.put<DataSafety>(`/${packageName}/dataSafety`, body);\n return data;\n },\n },\n\n reviews: {\n async list(packageName, options?) {\n await rateLimit(limiter, \"reviewsGet\");\n const params: Record<string, string> = {};\n if (options?.token) params[\"token\"] = options.token;\n if (options?.maxResults) params[\"maxResults\"] = String(options.maxResults);\n if (options?.translationLanguage)\n params[\"translationLanguage\"] = options.translationLanguage;\n const hasParams = Object.keys(params).length > 0;\n const { data } = await http.get<ReviewsListResponse>(\n `/${packageName}/reviews`,\n hasParams ? params : undefined,\n );\n return data;\n },\n\n async get(packageName, reviewId, translationLanguage?) {\n await rateLimit(limiter, \"reviewsGet\");\n const params: Record<string, string> = {};\n if (translationLanguage) params[\"translationLanguage\"] = translationLanguage;\n const hasParams = Object.keys(params).length > 0;\n const { data } = await http.get<Review>(\n `/${packageName}/reviews/${reviewId}`,\n hasParams ? params : undefined,\n );\n return data;\n },\n\n async reply(packageName, reviewId, replyText) {\n await rateLimit(limiter, \"reviewsPost\");\n const body: ReviewReplyRequest = { replyText };\n const { data } = await http.post<ReviewReplyResponse>(\n `/${packageName}/reviews/${reviewId}:reply`,\n body,\n );\n return data;\n },\n },\n\n subscriptions: {\n async list(packageName, options?) {\n const params: Record<string, string> = {};\n if (options?.pageToken) params[\"pageToken\"] = options.pageToken;\n if (options?.pageSize) params[\"pageSize\"] = String(options.pageSize);\n const hasParams = Object.keys(params).length > 0;\n const { data } = await http.get<SubscriptionsListResponse>(\n `/${packageName}/subscriptions`,\n hasParams ? params : undefined,\n );\n return data;\n },\n\n async get(packageName, productId) {\n const { data } = await http.get<Subscription>(`/${packageName}/subscriptions/${productId}`);\n return data;\n },\n\n async create(packageName, body, productId?) {\n const params: Record<string, string> = {};\n if (productId) params[\"productId\"] = productId;\n params[\"regionsVersion.version\"] = \"2022/02\";\n const path = `/${packageName}/subscriptions?${new URLSearchParams(params).toString()}`;\n const { data } = await http.post<Subscription>(path, body);\n return data;\n },\n\n async update(packageName, productId, body, updateMask?, regionsVersion?) {\n const params: Record<string, string> = {};\n if (updateMask) params[\"updateMask\"] = updateMask;\n params[\"regionsVersion.version\"] = regionsVersion || \"2022/02\";\n const path = `/${packageName}/subscriptions/${productId}?${new URLSearchParams(params).toString()}`;\n const { data } = await http.patch<Subscription>(path, body);\n return data;\n },\n\n async delete(packageName, productId) {\n await http.delete(`/${packageName}/subscriptions/${productId}`);\n },\n\n async batchGet(packageName, productIds) {\n const params = productIds.map((id) => `productIds=${encodeURIComponent(id)}`).join(\"&\");\n const { data } = await http.get<SubscriptionsBatchGetResponse>(\n `/${packageName}/subscriptions:batchGet?${params}`,\n );\n return data.subscriptions ?? [];\n },\n\n async batchUpdate(packageName, requests) {\n const { data } = await http.post<SubscriptionsBatchUpdateResponse>(\n `/${packageName}/subscriptions:batchUpdate`,\n requests,\n );\n return data;\n },\n\n async activateBasePlan(packageName, productId, basePlanId) {\n const { data } = await http.post<Subscription>(\n `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}:activate`,\n );\n return data;\n },\n\n async deactivateBasePlan(packageName, productId, basePlanId) {\n const { data } = await http.post<Subscription>(\n `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}:deactivate`,\n );\n return data;\n },\n\n async deleteBasePlan(packageName, productId, basePlanId) {\n await http.delete(`/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}`);\n },\n\n async migratePrices(packageName, productId, basePlanId, body) {\n const { data } = await http.post<Subscription>(\n `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}:migratePrices`,\n body,\n );\n return data;\n },\n\n async listOffers(packageName, productId, basePlanId) {\n const { data } = await http.get<OffersListResponse>(\n `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}/offers`,\n );\n return data;\n },\n\n async getOffer(packageName, productId, basePlanId, offerId) {\n const { data } = await http.get<SubscriptionOffer>(\n `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}/offers/${offerId}`,\n );\n return data;\n },\n\n async createOffer(packageName, productId, basePlanId, body, offerId?) {\n const params: Record<string, string> = {};\n if (offerId) params[\"offerId\"] = offerId;\n params[\"regionsVersion.version\"] = \"2022/02\";\n const path = `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}/offers?${new URLSearchParams(params).toString()}`;\n const { data } = await http.post<SubscriptionOffer>(path, body);\n return data;\n },\n\n async updateOffer(\n packageName,\n productId,\n basePlanId,\n offerId,\n body,\n updateMask?,\n regionsVersion?,\n ) {\n const params: Record<string, string> = {};\n if (updateMask) params[\"updateMask\"] = updateMask;\n params[\"regionsVersion.version\"] = regionsVersion || \"2022/02\";\n const path = `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}/offers/${offerId}?${new URLSearchParams(params).toString()}`;\n const { data } = await http.patch<SubscriptionOffer>(path, body);\n return data;\n },\n\n async deleteOffer(packageName, productId, basePlanId, offerId) {\n await http.delete(\n `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}/offers/${offerId}`,\n );\n },\n\n async activateOffer(packageName, productId, basePlanId, offerId) {\n const { data } = await http.post<SubscriptionOffer>(\n `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}/offers/${offerId}:activate`,\n );\n return data;\n },\n\n async deactivateOffer(packageName, productId, basePlanId, offerId) {\n const { data } = await http.post<SubscriptionOffer>(\n `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}/offers/${offerId}:deactivate`,\n );\n return data;\n },\n },\n\n inappproducts: {\n async list(packageName, options?) {\n // Note: maxResults and startIndex are deprecated and ignored by Google for inappproducts.list.\n // Server determines page size. Only token (pageToken) is supported for pagination.\n const params: Record<string, string> = {};\n if (options?.token) params[\"token\"] = options.token;\n const hasParams = Object.keys(params).length > 0;\n const { data } = await http.get<InAppProductsListResponse>(\n `/${packageName}/inappproducts`,\n hasParams ? params : undefined,\n );\n return data;\n },\n\n async get(packageName, sku) {\n const { data } = await http.get<InAppProduct>(`/${packageName}/inappproducts/${sku}`);\n return data;\n },\n\n async create(packageName, body, options?) {\n const params: Record<string, string> = {};\n if (options?.autoConvertMissingPrices) params[\"autoConvertMissingPrices\"] = \"true\";\n const hasParams = Object.keys(params).length > 0;\n const path = hasParams\n ? `/${packageName}/inappproducts?${new URLSearchParams(params).toString()}`\n : `/${packageName}/inappproducts`;\n const { data } = await http.post<InAppProduct>(path, body);\n return data;\n },\n\n async update(packageName, sku, body, options?) {\n const params: Record<string, string> = {};\n if (options?.autoConvertMissingPrices) params[\"autoConvertMissingPrices\"] = \"true\";\n if (options?.allowMissing) params[\"allowMissing\"] = \"true\";\n const hasParams = Object.keys(params).length > 0;\n const path = hasParams\n ? `/${packageName}/inappproducts/${sku}?${new URLSearchParams(params).toString()}`\n : `/${packageName}/inappproducts/${sku}`;\n const { data } = await http.put<InAppProduct>(path, body);\n return data;\n },\n\n async delete(packageName, sku) {\n await http.delete(`/${packageName}/inappproducts/${sku}`);\n },\n\n async batchUpdate(packageName, requests) {\n const { data } = await http.post<InAppProductsBatchUpdateResponse>(\n `/${packageName}/inappproducts:batchUpdate`,\n requests,\n );\n return data;\n },\n\n async batchGet(packageName, skus) {\n const params: Record<string, string> = {};\n if (skus.length > 0) {\n params[\"sku\"] = skus.join(\",\");\n }\n const { data } = await http.get<{ inappproduct: InAppProduct[] }>(\n `/${packageName}/inappproducts:batchGet`,\n Object.keys(params).length > 0 ? params : undefined,\n );\n return data.inappproduct || [];\n },\n\n async batchDelete(packageName, skus) {\n await http.post(\n `/${packageName}/inappproducts:batchDelete`,\n { requests: skus.map((sku) => ({ packageName, sku })) },\n );\n },\n },\n\n purchases: {\n async getProduct(packageName, productId, token) {\n const { data } = await http.get<ProductPurchase>(\n `/${packageName}/purchases/products/${productId}/tokens/${token}`,\n );\n return data;\n },\n\n async acknowledgeProduct(packageName, productId, token, body?) {\n await http.post(\n `/${packageName}/purchases/products/${productId}/tokens/${token}:acknowledge`,\n body,\n );\n },\n\n async consumeProduct(packageName, productId, token) {\n await http.post(`/${packageName}/purchases/products/${productId}/tokens/${token}:consume`);\n },\n\n async getSubscriptionV2(packageName, token) {\n const { data } = await http.get<SubscriptionPurchaseV2>(\n `/${packageName}/purchases/subscriptionsv2/tokens/${token}`,\n );\n return data;\n },\n\n async getSubscriptionV1(packageName, subscriptionId, token) {\n if (typeof process !== \"undefined\" && process.emitWarning) {\n process.emitWarning(\n \"purchases.subscriptions.get (v1) is deprecated by Google (shutdown Aug 2027). Use getSubscriptionV2() instead.\",\n \"DeprecationWarning\",\n );\n }\n const { data } = await http.get<SubscriptionPurchase>(\n `/${packageName}/purchases/subscriptions/${subscriptionId}/tokens/${token}`,\n );\n return data;\n },\n\n async cancelSubscription(packageName, subscriptionId, token) {\n await http.post(\n `/${packageName}/purchases/subscriptions/${subscriptionId}/tokens/${token}:cancel`,\n );\n },\n\n async deferSubscription(packageName, subscriptionId, token, body) {\n const { data } = await http.post<SubscriptionDeferResponse>(\n `/${packageName}/purchases/subscriptions/${subscriptionId}/tokens/${token}:defer`,\n body,\n );\n return data;\n },\n\n async acknowledgeSubscription(packageName, subscriptionId, token, body?) {\n await http.post(\n `/${packageName}/purchases/subscriptions/${subscriptionId}/tokens/${token}:acknowledge`,\n body ?? {},\n );\n },\n\n async revokeSubscriptionV2(packageName, token) {\n await http.post(`/${packageName}/purchases/subscriptionsv2/tokens/${token}:revoke`);\n },\n\n async refundSubscriptionV2(packageName, token) {\n await http.post(`/${packageName}/purchases/subscriptionsv2/tokens/${token}:refund`);\n },\n\n async cancelSubscriptionV2(packageName, token, body?) {\n await http.post(\n `/${packageName}/purchases/subscriptionsv2/tokens/${token}:cancel`,\n body,\n );\n },\n\n async deferSubscriptionV2(packageName, token, body) {\n const { data } = await http.post<SubscriptionsV2DeferResponse>(\n `/${packageName}/purchases/subscriptionsv2/tokens/${token}:defer`,\n body,\n );\n return data;\n },\n\n async getProductV2(packageName, token) {\n const { data } = await http.get<ProductPurchaseV2>(\n `/${packageName}/purchases/productsv2/tokens/${token}`,\n );\n return data;\n },\n\n async listVoided(packageName, options?) {\n await rateLimit(limiter, \"voidedBurst\");\n await rateLimit(limiter, \"voidedDaily\");\n const params: Record<string, string> = {};\n if (options?.startTime) params[\"startTime\"] = options.startTime;\n if (options?.endTime) params[\"endTime\"] = options.endTime;\n if (options?.maxResults) params[\"maxResults\"] = String(options.maxResults);\n if (options?.token) params[\"token\"] = options.token;\n const hasParams = Object.keys(params).length > 0;\n const { data } = await http.get<VoidedPurchasesListResponse>(\n `/${packageName}/purchases/voidedpurchases`,\n hasParams ? params : undefined,\n );\n return data;\n },\n },\n\n orders: {\n async get(packageName, orderId) {\n const { data } = await http.get<Order>(\n `/${packageName}/orders/${orderId}`,\n );\n return data;\n },\n\n async batchGet(packageName, orderIds) {\n const { data } = await http.post<BatchGetOrdersResponse>(\n `/${packageName}/orders:batchGet`,\n { orderIds },\n );\n return data.orders || [];\n },\n\n async refund(packageName, orderId, body?) {\n await http.post(`/${packageName}/orders/${orderId}:refund`, body);\n },\n },\n\n monetization: {\n async convertRegionPrices(packageName, price) {\n const { data } = await http.post<ConvertRegionPricesResponse>(\n `/${packageName}/pricing:convertRegionPrices`,\n price,\n );\n return data;\n },\n },\n\n reports: {\n async list(packageName, reportType, year, month) {\n const monthStr = String(month).padStart(2, \"0\");\n const { data } = await http.get<ReportsListResponse>(\n `/${packageName}/reports/${reportType}/${year}/${monthStr}`,\n );\n return data;\n },\n },\n\n testers: {\n async get(packageName, editId, track) {\n const { data } = await http.get<Testers>(\n `/${packageName}/edits/${editId}/testers/${track}`,\n );\n return data;\n },\n\n async update(packageName, editId, track, testersData) {\n const { data } = await http.put<Testers>(\n `/${packageName}/edits/${editId}/testers/${track}`,\n testersData,\n );\n return data;\n },\n },\n\n deobfuscation: {\n async upload(packageName, editId, versionCode, filePath) {\n const { data } = await http.upload<DeobfuscationUploadResponse>(\n `/${packageName}/edits/${editId}/apks/${versionCode}/deobfuscationFiles/proguard`,\n filePath,\n \"application/octet-stream\",\n );\n if (!data.deobfuscationFile) {\n throw new PlayApiError(\n \"Upload succeeded but no deobfuscation file data returned\",\n \"API_EMPTY_RESPONSE\",\n 200,\n \"This is unexpected. Retry the upload or contact Google Play support if the issue persists.\",\n );\n }\n return data.deobfuscationFile;\n },\n },\n\n appRecovery: {\n async list(packageName, versionCode?) {\n const params: Record<string, string> = {};\n if (versionCode !== undefined) params[\"versionCode\"] = String(versionCode);\n const hasParams = Object.keys(params).length > 0;\n const { data } = await http.get<AppRecoveriesListResponse>(\n `/${packageName}/appRecoveries`,\n hasParams ? params : undefined,\n );\n return data.recoveryActions || [];\n },\n\n async cancel(packageName, appRecoveryId) {\n await http.post(`/${packageName}/appRecovery/${appRecoveryId}:cancel`);\n },\n\n async deploy(packageName, appRecoveryId) {\n await http.post(`/${packageName}/appRecovery/${appRecoveryId}:deploy`);\n },\n\n async create(packageName, request) {\n const { data } = await http.post<AppRecoveryAction>(\n `/${packageName}/appRecoveries`,\n request,\n );\n return data;\n },\n\n async addTargeting(packageName, appRecoveryId, targeting) {\n const { data } = await http.post<AppRecoveryAction>(\n `/${packageName}/appRecoveries/${appRecoveryId}:addTargeting`,\n targeting,\n );\n return data;\n },\n },\n\n externalTransactions: {\n async create(packageName, body) {\n const { data } = await http.post<ExternalTransaction>(\n `/${packageName}/externalTransactions`,\n body,\n );\n return data;\n },\n\n async get(packageName, transactionId) {\n const { data } = await http.get<ExternalTransaction>(\n `/${packageName}/externalTransactions/${transactionId}`,\n );\n return data;\n },\n\n async refund(packageName, transactionId, refundData) {\n const { data } = await http.post<ExternalTransaction>(\n `/${packageName}/externalTransactions/${transactionId}:refund`,\n refundData,\n );\n return data;\n },\n },\n\n deviceTiers: {\n async list(packageName) {\n const { data } = await http.get<DeviceTierConfigsListResponse>(\n `/${packageName}/deviceTierConfigs`,\n );\n return data.deviceTierConfigs || [];\n },\n\n async get(packageName, configId) {\n const { data } = await http.get<DeviceTierConfig>(\n `/${packageName}/deviceTierConfigs/${configId}`,\n );\n return data;\n },\n\n async create(packageName, config) {\n const { data } = await http.post<DeviceTierConfig>(\n `/${packageName}/deviceTierConfigs`,\n config,\n );\n return data;\n },\n },\n\n oneTimeProducts: {\n async list(packageName) {\n const { data } = await http.get<OneTimeProductsListResponse>(\n `/${packageName}/oneTimeProducts`,\n );\n return data;\n },\n\n async get(packageName, productId) {\n const { data } = await http.get<OneTimeProduct>(\n `/${packageName}/oneTimeProducts/${productId}`,\n );\n return data;\n },\n\n async create(packageName, body) {\n const params = new URLSearchParams({ \"regionsVersion.version\": \"2022/02\" });\n const { data } = await http.post<OneTimeProduct>(\n `/${packageName}/oneTimeProducts?${params.toString()}`,\n body,\n );\n return data;\n },\n\n async update(packageName, productId, body, updateMask?, regionsVersion?) {\n const params: Record<string, string> = {};\n if (updateMask) params[\"updateMask\"] = updateMask;\n params[\"regionsVersion.version\"] = regionsVersion || \"2022/02\";\n const path = `/${packageName}/oneTimeProducts/${productId}?${new URLSearchParams(params).toString()}`;\n const { data } = await http.patch<OneTimeProduct>(path, body);\n return data;\n },\n\n async delete(packageName, productId) {\n await http.delete(`/${packageName}/oneTimeProducts/${productId}`);\n },\n\n async listOffers(packageName, productId) {\n const { data } = await http.get<OneTimeOffersListResponse>(\n `/${packageName}/oneTimeProducts/${productId}/offers`,\n );\n return data;\n },\n\n async getOffer(packageName, productId, offerId) {\n const { data } = await http.get<OneTimeOffer>(\n `/${packageName}/oneTimeProducts/${productId}/offers/${offerId}`,\n );\n return data;\n },\n\n async createOffer(packageName, productId, body) {\n const { data } = await http.post<OneTimeOffer>(\n `/${packageName}/oneTimeProducts/${productId}/offers`,\n body,\n );\n return data;\n },\n\n async updateOffer(packageName, productId, offerId, body, updateMask?, regionsVersion?) {\n const params: Record<string, string> = {};\n if (updateMask) params[\"updateMask\"] = updateMask;\n params[\"regionsVersion.version\"] = regionsVersion || \"2022/02\";\n const path = `/${packageName}/oneTimeProducts/${productId}/offers/${offerId}?${new URLSearchParams(params).toString()}`;\n const { data } = await http.patch<OneTimeOffer>(path, body);\n return data;\n },\n\n async deleteOffer(packageName, productId, offerId) {\n await http.delete(`/${packageName}/oneTimeProducts/${productId}/offers/${offerId}`);\n },\n },\n\n purchaseOptions: {\n async list(packageName) {\n const { data } = await http.get<PurchaseOptionsListResponse>(\n `/${packageName}/purchaseOptions`,\n );\n return data;\n },\n\n async get(packageName, purchaseOptionId) {\n const { data } = await http.get<PurchaseOption>(\n `/${packageName}/purchaseOptions/${purchaseOptionId}`,\n );\n return data;\n },\n\n async create(packageName, body) {\n const { data } = await http.post<PurchaseOption>(`/${packageName}/purchaseOptions`, body);\n return data;\n },\n\n async activate(packageName, purchaseOptionId) {\n const { data } = await http.post<PurchaseOption>(\n `/${packageName}/purchaseOptions/${purchaseOptionId}:activate`,\n );\n return data;\n },\n\n async deactivate(packageName, purchaseOptionId) {\n const { data } = await http.post<PurchaseOption>(\n `/${packageName}/purchaseOptions/${purchaseOptionId}:deactivate`,\n );\n return data;\n },\n },\n\n internalAppSharing: {\n async uploadBundle(packageName, bundlePath) {\n const { data } = await http.uploadInternal<InternalAppSharingArtifact>(\n `/${packageName}/artifacts/bundle`,\n bundlePath,\n \"application/octet-stream\",\n );\n return data;\n },\n\n async uploadApk(packageName, apkPath) {\n const { data } = await http.uploadInternal<InternalAppSharingArtifact>(\n `/${packageName}/artifacts/apk`,\n apkPath,\n \"application/vnd.android.package-archive\",\n );\n return data;\n },\n },\n\n generatedApks: {\n async list(packageName, versionCode) {\n const { data } = await http.get<GeneratedApksPerVersion>(\n `/${packageName}/generatedApks/${versionCode}`,\n );\n return data.generatedApks || [];\n },\n\n async download(packageName, versionCode, id) {\n return http.download(`/${packageName}/generatedApks/${versionCode}/download/${id}`);\n },\n },\n };\n}\n","export interface RateLimitBucket {\n name: string;\n maxTokens: number;\n refillRate: number;\n refillIntervalMs: number;\n}\n\nexport interface RateLimiter {\n acquire(bucket: string): Promise<void>;\n}\n\ninterface BucketState {\n tokens: number;\n lastRefillTime: number;\n config: RateLimitBucket;\n}\n\nexport const RATE_LIMIT_BUCKETS: Record<string, RateLimitBucket> = {\n default: { name: \"default\", maxTokens: 200, refillRate: 200, refillIntervalMs: 1_000 },\n reviewsGet: { name: \"reviewsGet\", maxTokens: 200, refillRate: 200, refillIntervalMs: 3_600_000 },\n reviewsPost: {\n name: \"reviewsPost\",\n maxTokens: 2_000,\n refillRate: 2_000,\n refillIntervalMs: 86_400_000,\n },\n voidedBurst: { name: \"voidedBurst\", maxTokens: 30, refillRate: 30, refillIntervalMs: 30_000 },\n voidedDaily: {\n name: \"voidedDaily\",\n maxTokens: 6_000,\n refillRate: 6_000,\n refillIntervalMs: 86_400_000,\n },\n reporting: { name: \"reporting\", maxTokens: 10, refillRate: 10, refillIntervalMs: 1_000 },\n};\n\nexport function createRateLimiter(buckets?: RateLimitBucket[]): RateLimiter {\n const states = new Map<string, BucketState>();\n\n if (buckets) {\n for (const bucket of buckets) {\n states.set(bucket.name, {\n tokens: bucket.maxTokens,\n lastRefillTime: Date.now(),\n config: bucket,\n });\n }\n }\n\n return {\n async acquire(bucket: string): Promise<void> {\n const state = states.get(bucket);\n if (!state) return;\n\n const now = Date.now();\n const elapsed = now - state.lastRefillTime;\n const refill = Math.floor(\n (elapsed / state.config.refillIntervalMs) * state.config.refillRate,\n );\n\n if (refill > 0) {\n state.tokens = Math.min(state.config.maxTokens, state.tokens + refill);\n state.lastRefillTime = now;\n }\n\n if (state.tokens > 0) {\n state.tokens--;\n return;\n }\n\n const tokensNeeded = 1;\n const waitMs = Math.ceil(\n (tokensNeeded / state.config.refillRate) * state.config.refillIntervalMs,\n );\n await new Promise((r) => setTimeout(r, waitMs));\n\n // Recalculate refill based on actual elapsed time since last refill\n const afterWait = Date.now();\n const totalElapsed = afterWait - state.lastRefillTime;\n const newTokens = Math.floor(\n (totalElapsed / state.config.refillIntervalMs) * state.config.refillRate,\n );\n state.tokens = Math.min(state.config.maxTokens, newTokens) - 1;\n state.lastRefillTime = afterWait;\n },\n };\n}\n","import { createHttpClient } from \"./http.js\";\nimport { createRateLimiter, RATE_LIMIT_BUCKETS } from \"./rate-limiter.js\";\nimport type {\n AnomalyDetectionResponse,\n ApiClientOptions,\n ErrorIssuesResponse,\n ErrorReportsResponse,\n MetricSetQuery,\n MetricSetResponse,\n VitalsMetricSet,\n} from \"./types.js\";\n\nconst REPORTING_BASE_URL = \"https://playdeveloperreporting.googleapis.com/v1beta1\";\n\nexport interface ReportingApiClient {\n queryMetricSet(\n packageName: string,\n metricSet: VitalsMetricSet,\n query: MetricSetQuery,\n ): Promise<MetricSetResponse>;\n\n getAnomalies(packageName: string): Promise<AnomalyDetectionResponse>;\n\n searchErrorIssues(\n packageName: string,\n filter?: string,\n pageSize?: number,\n pageToken?: string,\n ): Promise<ErrorIssuesResponse>;\n\n searchErrorReports(\n packageName: string,\n issueName: string,\n pageSize?: number,\n pageToken?: string,\n ): Promise<ErrorReportsResponse>;\n}\n\nexport function createReportingClient(options: ApiClientOptions): ReportingApiClient {\n const http = createHttpClient({ ...options, baseUrl: REPORTING_BASE_URL });\n const reportingBucket = RATE_LIMIT_BUCKETS[\"reporting\"];\n const limiter =\n options.rateLimiter ?? createRateLimiter(reportingBucket ? [reportingBucket] : []);\n\n return {\n async queryMetricSet(packageName, metricSet, query) {\n await limiter.acquire(\"reporting\");\n const { data } = await http.post<MetricSetResponse>(\n `/apps/${packageName}/${metricSet}:query`,\n query,\n );\n return data;\n },\n\n async getAnomalies(packageName) {\n await limiter.acquire(\"reporting\");\n const { data } = await http.get<AnomalyDetectionResponse>(`/apps/${packageName}/anomalies`);\n return data;\n },\n\n async searchErrorIssues(packageName, filter?, pageSize?, pageToken?) {\n await limiter.acquire(\"reporting\");\n const params: Record<string, string> = {};\n if (filter) params[\"filter\"] = filter;\n if (pageSize) params[\"pageSize\"] = String(pageSize);\n if (pageToken) params[\"pageToken\"] = pageToken;\n const { data } = await http.get<ErrorIssuesResponse>(\n `/apps/${packageName}/errorIssues:search`,\n params,\n );\n return data;\n },\n\n async searchErrorReports(packageName, issueName, pageSize?, pageToken?) {\n await limiter.acquire(\"reporting\");\n const params: Record<string, string> = {};\n if (pageSize) params[\"pageSize\"] = String(pageSize);\n if (pageToken) params[\"pageToken\"] = pageToken;\n const { data } = await http.get<ErrorReportsResponse>(\n `/apps/${packageName}/errorIssues/${issueName}/reports`,\n params,\n );\n return data;\n },\n };\n}\n","import { createHttpClient } from \"./http.js\";\nimport type { ApiClientOptions, User, UsersListResponse, Grant } from \"./types.js\";\n\nconst USERS_BASE_URL = \"https://androidpublisher.googleapis.com/androidpublisher/v3/developers\";\n\nexport interface GrantsListResponse {\n grants: Grant[];\n nextPageToken?: string;\n}\n\nexport interface UsersApiClient {\n list(\n developerId: string,\n options?: { pageToken?: string; pageSize?: number },\n ): Promise<UsersListResponse>;\n\n get(developerId: string, userId: string): Promise<User>;\n\n create(developerId: string, user: Partial<User>): Promise<User>;\n\n update(\n developerId: string,\n userId: string,\n user: Partial<User>,\n updateMask?: string,\n ): Promise<User>;\n\n delete(developerId: string, userId: string): Promise<void>;\n\n grants: {\n list(developerId: string, email: string): Promise<GrantsListResponse>;\n create(developerId: string, email: string, grant: Partial<Grant>): Promise<Grant>;\n patch(\n developerId: string,\n email: string,\n packageName: string,\n grant: Partial<Grant>,\n updateMask?: string,\n ): Promise<Grant>;\n delete(developerId: string, email: string, packageName: string): Promise<void>;\n };\n}\n\nexport function createUsersClient(options: ApiClientOptions): UsersApiClient {\n const http = createHttpClient({ ...options, baseUrl: USERS_BASE_URL });\n\n return {\n async list(developerId, listOptions?) {\n const params: Record<string, string> = {};\n if (listOptions?.pageToken) params[\"pageToken\"] = listOptions.pageToken;\n if (listOptions?.pageSize) params[\"pageSize\"] = String(listOptions.pageSize);\n const hasParams = Object.keys(params).length > 0;\n const { data } = await http.get<UsersListResponse>(\n `/${developerId}/users`,\n hasParams ? params : undefined,\n );\n return data;\n },\n\n async get(developerId, userId) {\n const { data } = await http.get<User>(`/${developerId}/users/${userId}`);\n return data;\n },\n\n async create(developerId, user) {\n const { data } = await http.post<User>(`/${developerId}/users`, user);\n return data;\n },\n\n async update(developerId, userId, user, updateMask?) {\n let path = `/${developerId}/users/${userId}`;\n if (updateMask) {\n path += `?updateMask=${encodeURIComponent(updateMask).replace(/%2C/gi, \",\")}`;\n }\n const { data } = await http.patch<User>(path, user);\n return data;\n },\n\n async delete(developerId, userId) {\n await http.delete(`/${developerId}/users/${userId}`);\n },\n\n grants: {\n async list(developerId, email) {\n const { data } = await http.get<GrantsListResponse>(\n `/${developerId}/users/${encodeURIComponent(email)}/grants`,\n );\n return data;\n },\n\n async create(developerId, email, grant) {\n const { data } = await http.post<Grant>(\n `/${developerId}/users/${encodeURIComponent(email)}/grants`,\n grant,\n );\n return data;\n },\n\n async patch(developerId, email, packageName, grant, updateMask?) {\n let path = `/${developerId}/users/${encodeURIComponent(email)}/grants/${encodeURIComponent(packageName)}`;\n if (updateMask) {\n path += `?updateMask=${encodeURIComponent(updateMask)}`;\n }\n const { data } = await http.patch<Grant>(path, grant);\n return data;\n },\n\n async delete(developerId, email, packageName) {\n await http.delete(\n `/${developerId}/users/${encodeURIComponent(email)}/grants/${encodeURIComponent(packageName)}`,\n );\n },\n },\n };\n}\n","import { createHttpClient } from \"./http.js\";\nimport type { ApiClientOptions } from \"./types.js\";\n\nconst GAMES_BASE_URL = \"https://games.googleapis.com/games/v1\";\n\nexport interface Leaderboard {\n id: string;\n name: string;\n order: string;\n iconUrl?: string;\n}\n\nexport interface LeaderboardScore {\n leaderboardId: string;\n scoreValue: string;\n formattedScore: string;\n writeTimestamp?: string;\n tag?: string;\n}\n\nexport interface Achievement {\n id: string;\n name: string;\n description: string;\n state: \"REVEALED\" | \"HIDDEN\" | \"UNLOCKED\";\n currentSteps?: number;\n totalSteps?: number;\n experiencePoints?: number;\n formattedCurrentStepsString?: string;\n}\n\nexport interface GameEvent {\n definitionId: string;\n numEvents: string;\n formattedNumEvents: string;\n kind?: string;\n}\n\nexport interface GamesApiClient {\n leaderboards: {\n list(packageName: string): Promise<{ items?: Leaderboard[]; nextPageToken?: string }>;\n get(packageName: string, leaderboardId: string): Promise<Leaderboard>;\n getScores(\n packageName: string,\n leaderboardId: string,\n collection: string,\n timeSpan: string,\n ): Promise<{ items?: LeaderboardScore[] }>;\n };\n achievements: {\n list(packageName: string): Promise<{ items?: Achievement[]; nextPageToken?: string }>;\n reveal(packageName: string, achievementId: string): Promise<{ currentState: string }>;\n };\n events: {\n list(packageName: string): Promise<{ items?: GameEvent[]; nextPageToken?: string }>;\n };\n}\n\nexport function createGamesClient(options: ApiClientOptions): GamesApiClient {\n const http = createHttpClient({ ...options, baseUrl: GAMES_BASE_URL });\n\n function qs(params: Record<string, string>): string {\n return new URLSearchParams(params).toString();\n }\n\n return {\n leaderboards: {\n async list(packageName) {\n const { data } = await http.get<{ items?: Leaderboard[]; nextPageToken?: string }>(\n `/leaderboards?${qs({ applicationId: packageName })}`,\n );\n return data;\n },\n async get(packageName, leaderboardId) {\n const { data } = await http.get<Leaderboard>(\n `/leaderboards/${encodeURIComponent(leaderboardId)}?${qs({ applicationId: packageName })}`,\n );\n return data;\n },\n async getScores(packageName, leaderboardId, collection, timeSpan) {\n const { data } = await http.get<{ items?: LeaderboardScore[] }>(\n `/leaderboards/${encodeURIComponent(leaderboardId)}/scores/${encodeURIComponent(collection)}?${qs({ timeSpan, applicationId: packageName })}`,\n );\n return data;\n },\n },\n achievements: {\n async list(packageName) {\n const { data } = await http.get<{ items?: Achievement[]; nextPageToken?: string }>(\n `/achievements?${qs({ applicationId: packageName })}`,\n );\n return data;\n },\n async reveal(packageName, achievementId) {\n const { data } = await http.post<{ currentState: string }>(\n `/achievements/${encodeURIComponent(achievementId)}/reveal?${qs({ applicationId: packageName })}`,\n {},\n );\n return data;\n },\n },\n events: {\n async list(packageName) {\n const { data } = await http.get<{ items?: GameEvent[]; nextPageToken?: string }>(\n `/events?${qs({ applicationId: packageName })}`,\n );\n return data;\n },\n },\n };\n}\n","import { createHttpClient } from \"./http.js\";\nimport type { ApiClientOptions } from \"./types.js\";\n\nconst ENTERPRISE_BASE_URL = \"https://playcustomapp.googleapis.com/playcustomapp/v1/organizations\";\n\nexport interface CustomApp {\n packageName?: string;\n title: string;\n languageCode?: string;\n organizations?: Array<{ organizationId: string; organizationName?: string }>;\n}\n\nexport interface CustomAppsListResponse {\n customApps?: CustomApp[];\n nextPageToken?: string;\n}\n\nexport interface EnterpriseApiClient {\n apps: {\n create(organizationId: string, app: Partial<CustomApp>): Promise<CustomApp>;\n list(organizationId: string): Promise<CustomAppsListResponse>;\n };\n}\n\nexport function createEnterpriseClient(options: ApiClientOptions): EnterpriseApiClient {\n const http = createHttpClient({ ...options, baseUrl: ENTERPRISE_BASE_URL });\n\n return {\n apps: {\n async create(organizationId, app) {\n const { data } = await http.post<CustomApp>(`/${organizationId}/apps`, app);\n return data;\n },\n async list(organizationId) {\n const { data } = await http.get<CustomAppsListResponse>(`/${organizationId}/apps`);\n return data;\n },\n },\n };\n}\n","export interface PaginateOptions {\n limit?: number;\n startPageToken?: string;\n}\n\nexport async function* paginate<TItem>(\n fetchPage: (pageToken?: string) => Promise<{ items: TItem[]; nextPageToken?: string }>,\n options?: PaginateOptions,\n): AsyncGenerator<TItem[], void, unknown> {\n let pageToken = options?.startPageToken;\n let collected = 0;\n const limit = options?.limit;\n\n for (;;) {\n if (limit !== undefined && collected >= limit) break;\n\n const page = await fetchPage(pageToken);\n const items = page.items;\n\n if (items.length === 0) break;\n\n if (limit !== undefined) {\n const remaining = limit - collected;\n if (items.length > remaining) {\n yield items.slice(0, remaining);\n return;\n }\n }\n\n yield items;\n collected += items.length;\n pageToken = page.nextPageToken;\n\n if (!pageToken) break;\n }\n}\n\nexport async function paginateAll<TItem>(\n fetchPage: (pageToken?: string) => Promise<{ items: TItem[]; nextPageToken?: string }>,\n options?: PaginateOptions,\n): Promise<{ items: TItem[]; nextPageToken?: string }> {\n const allItems: TItem[] = [];\n let lastPageToken: string | undefined;\n const limit = options?.limit;\n\n for await (const items of paginate(fetchPage, options)) {\n allItems.push(...items);\n if (limit !== undefined && allItems.length >= limit) break;\n }\n\n // If we stopped due to limit, try to get the next page token for resumption\n if (limit !== undefined && allItems.length >= limit) {\n lastPageToken = undefined; // Already truncated by paginate\n }\n\n return { items: allItems, nextPageToken: lastPageToken };\n}\n\n/**\n * Fetch multiple known pages in parallel.\n * Useful when page tokens are predictable or when pre-fetching subsequent pages\n * after an initial sequential fetch reveals the token pattern.\n *\n * @param fetchPage - Function that fetches a page given a token\n * @param pageTokens - Array of page tokens to fetch concurrently\n * @param concurrency - Max concurrent requests (default: 4)\n */\nexport async function paginateParallel<TItem>(\n fetchPage: (pageToken?: string) => Promise<{ items: TItem[]; nextPageToken?: string }>,\n pageTokens: string[],\n concurrency = 4,\n): Promise<{ items: TItem[]; nextPageToken?: string }> {\n const allItems: TItem[] = [];\n let lastNextPageToken: string | undefined;\n\n // Process in batches of `concurrency`\n for (let i = 0; i < pageTokens.length; i += concurrency) {\n const batch = pageTokens.slice(i, i + concurrency);\n const results = await Promise.all(batch.map((token) => fetchPage(token)));\n\n for (const result of results) {\n allItems.push(...result.items);\n if (result.nextPageToken) {\n lastNextPageToken = result.nextPageToken;\n }\n }\n }\n\n return { items: allItems, nextPageToken: lastNextPageToken };\n}\n"],"mappings":"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","names":["stat","envInt","jitteredDelay","stat","options"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/http.ts","../src/resumable-upload.ts","../src/rate-limiter.ts","../src/client.ts","../src/reporting-client.ts","../src/users-client.ts","../src/games-client.ts","../src/enterprise-client.ts","../src/paginate.ts"],"sourcesContent":["export class PlayApiError extends Error {\n public readonly exitCode = 4;\n constructor(\n message: string,\n public readonly code: string,\n public readonly statusCode?: number,\n public readonly suggestion?: string,\n ) {\n super(message);\n this.name = \"PlayApiError\";\n }\n toJSON() {\n return {\n success: false,\n error: {\n code: this.code,\n message: this.message,\n suggestion: this.suggestion,\n },\n };\n }\n}\n","import { readFile, stat } from \"node:fs/promises\";\nimport { resolve, isAbsolute } from \"node:path\";\nimport { PlayApiError } from \"./errors.js\";\nimport { resumableUpload, RESUMABLE_THRESHOLD } from \"./resumable-upload.js\";\nimport type { ApiClientOptions, ApiResponse, ResumableUploadOptions } from \"./types.js\";\n\n/** Strip HTML tags and collapse whitespace from a string. */\nfunction stripHtml(text: string): string {\n return text\n .replace(/<[^>]*>/g, \" \")\n .replace(/&[a-z]+;/gi, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\n/** Extract a short, safe error summary from API response body (no tokens/secrets). */\nfunction sanitizeErrorBody(body: string): string {\n try {\n const parsed = JSON.parse(body) as {\n error?: { message?: string; status?: string; code?: number };\n };\n if (parsed?.error?.message) {\n return `${parsed.error.code ?? \"?\"} ${parsed.error.status ?? \"\"}: ${parsed.error.message}`.trim();\n }\n } catch {\n // not JSON — may be HTML error page\n }\n // Strip HTML tags before truncating\n const cleaned = body.startsWith(\"<\") ? stripHtml(body) : body;\n return cleaned.length > 200 ? cleaned.slice(0, 200) + \"...\" : cleaned;\n}\n\n/** Validate upload file path to prevent path traversal. */\nfunction validateFilePath(filePath: string): string {\n const resolved = resolve(filePath);\n if (!isAbsolute(resolved)) {\n throw new PlayApiError(\n \"Invalid file path\",\n \"API_INVALID_PATH\",\n undefined,\n \"File path must resolve to an absolute path.\",\n );\n }\n // Block obvious traversal patterns in the original input\n if (filePath.includes(\"\\0\")) {\n throw new PlayApiError(\n \"Invalid file path: null bytes not allowed\",\n \"API_INVALID_PATH\",\n undefined,\n \"Provide a valid file path without null bytes.\",\n );\n }\n return resolved;\n}\n\nconst BASE_URL = \"https://androidpublisher.googleapis.com/androidpublisher/v3/applications\";\n\nconst UPLOAD_BASE_URL =\n \"https://androidpublisher.googleapis.com/upload/androidpublisher/v3/applications\";\n\nconst INTERNAL_SHARING_UPLOAD_BASE_URL =\n \"https://androidpublisher.googleapis.com/upload/internalappsharing/v3/applications\";\n\nexport interface HttpClient {\n get<T>(path: string, params?: Record<string, string>): Promise<ApiResponse<T>>;\n post<T>(path: string, body?: unknown): Promise<ApiResponse<T>>;\n put<T>(path: string, body?: unknown): Promise<ApiResponse<T>>;\n patch<T>(path: string, body?: unknown): Promise<ApiResponse<T>>;\n delete<T>(path: string): Promise<ApiResponse<T>>;\n upload<T>(path: string, filePath: string, contentType: string): Promise<ApiResponse<T>>;\n uploadResumable<T>(\n path: string,\n filePath: string,\n contentType: string,\n options?: ResumableUploadOptions,\n ): Promise<ApiResponse<T>>;\n uploadInternal<T>(path: string, filePath: string, contentType: string): Promise<ApiResponse<T>>;\n download(path: string): Promise<ArrayBuffer>;\n}\n\nfunction envInt(name: string): number | undefined {\n const val = process.env[name];\n if (val === undefined) return undefined;\n const n = Number(val);\n return Number.isFinite(n) ? n : undefined;\n}\n\nfunction resolveOption(explicit: number | undefined, envName: string, fallback: number): number {\n return explicit ?? envInt(envName) ?? fallback;\n}\n\ninterface ErrorMapping {\n code: string;\n message: string;\n suggestion: string;\n}\n\n/**\n * Pattern-match Google Play API error responses to return specific,\n * actionable error messages. Returns undefined if no pattern matches.\n */\nfunction enhanceApiError(status: number, body: string): ErrorMapping | undefined {\n let errorMsg = \"\";\n try {\n const parsed = JSON.parse(body) as { error?: { message?: string; status?: string } };\n errorMsg = parsed?.error?.message?.toLowerCase() ?? \"\";\n } catch {\n errorMsg = body.toLowerCase();\n }\n\n // — Duplicate version code (400/403)\n if ((status === 400 || status === 403) && errorMsg.includes(\"version code\") && errorMsg.includes(\"already been used\")) {\n const match = errorMsg.match(/version code (\\d+)/);\n const vc = match?.[1] ?? \"?\";\n return {\n code: \"API_DUPLICATE_VERSION_CODE\",\n message: `Version code ${vc} has already been uploaded to this app.`,\n suggestion: [\n `Increment versionCode in your build.gradle (or build.gradle.kts) and rebuild.`,\n `Check the current version with: gpc releases status --track production`,\n ].join(\"\\n\"),\n };\n }\n\n // — Version code too low (400/403)\n if (\n (status === 400 || status === 403) &&\n errorMsg.includes(\"version code\") &&\n (errorMsg.includes(\"lower\") || errorMsg.includes(\"not allowed\") || errorMsg.includes(\"not greater\"))\n ) {\n return {\n code: \"API_VERSION_CODE_TOO_LOW\",\n message: \"Version code is lower than the current version on the target track.\",\n suggestion: [\n \"Google Play requires version codes to increase with each upload.\",\n \"Check the current version with: gpc releases status --track <track>\",\n \"Then set a higher versionCode in your build.gradle and rebuild.\",\n ].join(\"\\n\"),\n };\n }\n\n // — Package name mismatch (400/403)\n if (\n (status === 400 || status === 403) &&\n (errorMsg.includes(\"package name\") || errorMsg.includes(\"applicationid\")) &&\n errorMsg.includes(\"does not match\")\n ) {\n return {\n code: \"API_PACKAGE_NAME_MISMATCH\",\n message: \"The package name in the uploaded bundle does not match the target app.\",\n suggestion: [\n \"Verify your applicationId in build.gradle matches the app you're uploading to.\",\n \"Check the configured package with: gpc config show\",\n \"Or specify explicitly with: --app com.example.yourapp\",\n ].join(\"\\n\"),\n };\n }\n\n // — App not found (404)\n if (\n status === 404 &&\n (errorMsg.includes(\"applicationnotfound\") ||\n errorMsg.includes(\"no application was found\") ||\n errorMsg.includes(\"application not found\"))\n ) {\n return {\n code: \"API_APP_NOT_FOUND\",\n message: \"This app was not found in your Google Play developer account.\",\n suggestion: [\n \"Verify the package name is correct.\",\n \"Ensure the app has been created in the Google Play Console.\",\n \"List available apps with: gpc apps list\",\n ].join(\"\\n\"),\n };\n }\n\n // — Insufficient permissions (403)\n if (\n status === 403 &&\n (errorMsg.includes(\"permission\") ||\n errorMsg.includes(\"insufficient\") ||\n errorMsg.includes(\"caller does not have\"))\n ) {\n return {\n code: \"API_INSUFFICIENT_PERMISSIONS\",\n message: \"The service account does not have permission for this operation.\",\n suggestion: [\n \"In Google Play Console → Users and permissions → find your service account email.\",\n \"Grant the required permissions (e.g., 'Release to production' for uploads).\",\n \"Run gpc doctor to verify your credentials and permissions.\",\n ].join(\"\\n\"),\n };\n }\n\n // — Edit conflict (409)\n if (status === 409) {\n return {\n code: \"API_EDIT_CONFLICT\",\n message: \"An edit conflict occurred — another edit session is open for this app.\",\n suggestion: [\n \"This usually means another process has an open edit (CI pipeline, Play Console, or another gpc instance).\",\n \"Wait a few minutes and retry — GPC will auto-retry once.\",\n \"Or discard the stale edit in the Google Play Console.\",\n ].join(\"\\n\"),\n };\n }\n\n // — Bundle too large (400/413)\n if (\n status === 413 ||\n ((status === 400 || status === 403) &&\n (errorMsg.includes(\"too large\") || (errorMsg.includes(\"exceeds\") && errorMsg.includes(\"size\"))))\n ) {\n return {\n code: \"API_BUNDLE_TOO_LARGE\",\n message: \"The uploaded file exceeds Google Play's size limit.\",\n suggestion: [\n \"AAB files must be under 2 GB, APK files under 1 GB.\",\n \"Use Android App Bundles (AAB) instead of APK for smaller file sizes.\",\n \"Run gpc preflight <file> to check bundle size before uploading.\",\n ].join(\"\\n\"),\n };\n }\n\n // — Invalid bundle/APK (400)\n if (\n status === 400 &&\n (errorMsg.includes(\"invalid bundle\") ||\n errorMsg.includes(\"invalid apk\") ||\n errorMsg.includes(\"unable to parse\") ||\n errorMsg.includes(\"malformed apk\") ||\n errorMsg.includes(\"malformed bundle\"))\n ) {\n return {\n code: \"API_INVALID_BUNDLE\",\n message: \"Google Play rejected the uploaded file as invalid or malformed.\",\n suggestion: [\n \"Ensure the file is a properly signed AAB or APK.\",\n \"Common causes: corrupted file, unsigned bundle, wrong file format.\",\n \"Run gpc preflight <file> for offline validation.\",\n \"Rebuild with: ./gradlew bundleRelease\",\n ].join(\"\\n\"),\n };\n }\n\n // — Track not found (404)\n if (status === 404 && errorMsg.includes(\"track\") && (errorMsg.includes(\"not found\") || errorMsg.includes(\"does not exist\"))) {\n return {\n code: \"API_TRACK_NOT_FOUND\",\n message: \"The specified track does not exist for this app.\",\n suggestion: [\n \"Built-in tracks: internal, alpha, beta, production.\",\n \"List custom tracks with: gpc tracks list\",\n \"Create a custom track with: gpc tracks create <name>\",\n ].join(\"\\n\"),\n };\n }\n\n // — Release notes too long (400)\n if (status === 400 && errorMsg.includes(\"release notes\") && (errorMsg.includes(\"too long\") || errorMsg.includes(\"character limit\"))) {\n return {\n code: \"API_RELEASE_NOTES_TOO_LONG\",\n message: \"Release notes exceed the 500-character limit.\",\n suggestion: [\n \"Shorten the release notes to 500 characters or fewer per language.\",\n \"Preview current notes with: gpc releases notes get --track <track>\",\n ].join(\"\\n\"),\n };\n }\n\n // — Rollout already completed (400)\n if (status === 400 && (errorMsg.includes(\"cannot change rollout\") || (errorMsg.includes(\"release\") && errorMsg.includes(\"already completed\")))) {\n return {\n code: \"API_ROLLOUT_ALREADY_COMPLETED\",\n message: \"The release is already at full rollout (100%) and cannot be changed.\",\n suggestion: [\n \"A completed release cannot have its rollout percentage modified.\",\n \"To deploy a new version: gpc releases upload --track <track>\",\n ].join(\"\\n\"),\n };\n }\n\n // — Edit expired (400 FAILED_PRECONDITION)\n if (status === 400 && errorMsg.includes(\"edit\") && (errorMsg.includes(\"expired\") || errorMsg.includes(\"failed_precondition\"))) {\n return {\n code: \"API_EDIT_EXPIRED\",\n message: \"The edit session has expired.\",\n suggestion: [\n \"Edit sessions last about 1 hour.\",\n \"Retry the operation — GPC will open a fresh edit automatically.\",\n ].join(\"\\n\"),\n };\n }\n\n return undefined;\n}\n\nfunction mapStatusToError(status: number, body: string): { code: string; message?: string; suggestion?: string } {\n // Try specific pattern matching first\n const enhanced = enhanceApiError(status, body);\n if (enhanced) return enhanced;\n\n // Fall back to generic status-based mapping\n switch (status) {\n case 400:\n return { code: \"API_HTTP_400\", suggestion: \"Check request parameters and try again.\" };\n case 401:\n return {\n code: \"API_UNAUTHORIZED\",\n suggestion: \"Check that your access token is valid and not expired. Run: gpc doctor\",\n };\n case 403:\n return {\n code: \"API_FORBIDDEN\",\n suggestion: \"Ensure the service account has the required permissions. Run: gpc doctor\",\n };\n case 404:\n return {\n code: \"API_NOT_FOUND\",\n suggestion: \"Verify the package name and resource IDs are correct. Run: gpc apps list\",\n };\n case 413:\n return {\n code: \"API_BUNDLE_TOO_LARGE\",\n suggestion: \"The uploaded file is too large. AAB limit: 2 GB, APK limit: 1 GB.\",\n };\n case 429:\n return {\n code: \"API_RATE_LIMITED\",\n suggestion: \"Too many requests. GPC will retry automatically.\",\n };\n default:\n if (status >= 500) {\n return {\n code: \"API_SERVER_ERROR\",\n suggestion: \"Google Play API server error. GPC will retry automatically.\",\n };\n }\n return { code: `API_HTTP_${status}` };\n }\n}\n\nfunction isRetryable(status: number): boolean {\n return status === 408 || status === 429 || status >= 500;\n}\n\nfunction jitteredDelay(base: number, attempt: number, max: number): number {\n const exponential = base * 2 ** attempt;\n const capped = Math.min(exponential, max);\n return capped * (0.5 + Math.random() * 0.5);\n}\n\nexport function createHttpClient(options: ApiClientOptions): HttpClient {\n const maxRetries = resolveOption(options.maxRetries, \"GPC_MAX_RETRIES\", 5);\n const timeout = resolveOption(options.timeout, \"GPC_TIMEOUT\", 30_000);\n const uploadTimeoutExplicit = options.uploadTimeout ?? envInt(\"GPC_UPLOAD_TIMEOUT\");\n const baseDelay = resolveOption(options.baseDelay, \"GPC_BASE_DELAY\", 1_000);\n const maxDelay = resolveOption(options.maxDelay, \"GPC_MAX_DELAY\", 60_000);\n const onRetry = options.onRetry;\n\n async function request<T>(\n method: string,\n path: string,\n body?: unknown,\n params?: Record<string, string>,\n ): Promise<ApiResponse<T>> {\n let url = `${options.baseUrl ?? BASE_URL}${path}`;\n if (params) {\n const search = new URLSearchParams(params);\n url += `?${search.toString()}`;\n }\n\n // Fetch token once before retries — the auth layer handles its own caching and mutex\n let token = await options.auth.getAccessToken();\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n if (attempt > 0) {\n const delay = jitteredDelay(baseDelay, attempt - 1, maxDelay);\n await new Promise((r) => setTimeout(r, delay));\n }\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeout);\n\n try {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n \"Accept-Encoding\": \"gzip, deflate\",\n Connection: \"keep-alive\",\n };\n\n const init: RequestInit = {\n method,\n headers,\n signal: controller.signal,\n keepalive: true,\n };\n\n if (body !== undefined) {\n init.body = JSON.stringify(body);\n }\n\n const response = await fetch(url, init);\n\n if (response.ok) {\n const text = await response.text();\n const data = text ? (JSON.parse(text) as T) : ({} as T);\n return { data, status: response.status };\n }\n\n const errorBody = await response.text();\n const mapped = mapStatusToError(response.status, errorBody);\n\n const err = new PlayApiError(\n mapped.message ?? `${method} ${path} failed with status ${response.status}: ${sanitizeErrorBody(errorBody)}`,\n mapped.code,\n response.status,\n mapped.suggestion,\n );\n\n if (isRetryable(response.status) && attempt < maxRetries) {\n lastError = err;\n const delay = jitteredDelay(baseDelay, attempt, maxDelay);\n onRetry?.({\n attempt: attempt + 1,\n method,\n path,\n status: response.status,\n error: err.message,\n delayMs: Math.round(delay),\n timestamp: new Date().toISOString(),\n });\n continue;\n }\n\n // On 401, refresh token once before giving up\n if (response.status === 401 && attempt < maxRetries) {\n token = await options.auth.getAccessToken();\n lastError = err;\n continue;\n }\n\n throw err;\n } catch (error) {\n if (error instanceof PlayApiError) {\n throw error;\n }\n\n if (error instanceof DOMException && error.name === \"AbortError\") {\n const timeoutErr = new PlayApiError(\n `${method} ${path} timed out after ${timeout}ms`,\n \"API_TIMEOUT\",\n undefined,\n \"The request exceeded the configured timeout. Consider increasing the timeout value.\",\n );\n if (attempt < maxRetries) {\n lastError = timeoutErr;\n onRetry?.({\n attempt: attempt + 1,\n method,\n path,\n error: timeoutErr.message,\n delayMs: Math.round(jitteredDelay(baseDelay, attempt, maxDelay)),\n timestamp: new Date().toISOString(),\n });\n continue;\n }\n throw timeoutErr;\n }\n\n const networkErr = new PlayApiError(\n `${method} ${path} failed: ${error instanceof Error ? error.message : String(error)}`,\n \"API_NETWORK_ERROR\",\n undefined,\n \"A network error occurred. Check your internet connection.\",\n );\n if (attempt < maxRetries) {\n lastError = networkErr;\n onRetry?.({\n attempt: attempt + 1,\n method,\n path,\n error: networkErr.message,\n delayMs: Math.round(jitteredDelay(baseDelay, attempt, maxDelay)),\n timestamp: new Date().toISOString(),\n });\n continue;\n }\n throw networkErr;\n } finally {\n clearTimeout(timer);\n }\n }\n\n // Should not reach here, but just in case\n throw (\n lastError ??\n new PlayApiError(\n \"Request failed\",\n \"API_NETWORK_ERROR\",\n undefined,\n \"Check your network connection and try again. Use --verbose for details.\",\n )\n );\n }\n\n /** Calculate upload timeout: explicit value, or auto-scale from file size (1 MB/s minimum throughput + 30s overhead). */\n function computeUploadTimeout(fileSizeBytes: number): number {\n if (uploadTimeoutExplicit !== undefined) return uploadTimeoutExplicit;\n // Base: 30s overhead + 1s per MB (assumes ~1 MB/s minimum upload speed)\n const sizeMb = fileSizeBytes / (1024 * 1024);\n return Math.max(timeout, 30_000 + Math.ceil(sizeMb) * 1_000);\n }\n\n async function uploadRequest<T>(\n path: string,\n filePath: string,\n contentType: string,\n baseUrl: string = UPLOAD_BASE_URL,\n ): Promise<ApiResponse<T>> {\n const separator = path.includes(\"?\") ? \"&\" : \"?\";\n const url = `${baseUrl}${path}${separator}uploadType=media`;\n const safeFilePath = validateFilePath(filePath);\n const fileBuffer = await readFile(safeFilePath);\n const effectiveTimeout = computeUploadTimeout(fileBuffer.byteLength);\n\n // Fetch token once before retries\n let token = await options.auth.getAccessToken();\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n if (attempt > 0) {\n const delay = jitteredDelay(baseDelay, attempt - 1, maxDelay);\n await new Promise((r) => setTimeout(r, delay));\n }\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), effectiveTimeout);\n\n try {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": contentType,\n \"Accept-Encoding\": \"gzip, deflate\",\n Connection: \"keep-alive\",\n };\n\n const response = await fetch(url, {\n method: \"POST\",\n headers,\n body: fileBuffer,\n signal: controller.signal,\n keepalive: true,\n });\n\n if (response.ok) {\n const text = await response.text();\n const data = text ? (JSON.parse(text) as T) : ({} as T);\n return { data, status: response.status };\n }\n\n const errorBody = await response.text();\n const mapped = mapStatusToError(response.status, errorBody);\n\n const err = new PlayApiError(\n mapped.message ?? `Upload failed with status ${response.status}: ${sanitizeErrorBody(errorBody)}`,\n mapped.code,\n response.status,\n mapped.suggestion,\n );\n\n if (isRetryable(response.status) && attempt < maxRetries) {\n lastError = err;\n const delay = jitteredDelay(baseDelay, attempt, maxDelay);\n onRetry?.({\n attempt: attempt + 1,\n method: \"POST\",\n path: `upload ${path}`,\n status: response.status,\n error: err.message,\n delayMs: Math.round(delay),\n timestamp: new Date().toISOString(),\n });\n continue;\n }\n\n // On 401, refresh token once before giving up\n if (response.status === 401 && attempt < maxRetries) {\n token = await options.auth.getAccessToken();\n lastError = err;\n continue;\n }\n\n throw err;\n } catch (error) {\n if (error instanceof PlayApiError) {\n throw error;\n }\n\n if (error instanceof DOMException && error.name === \"AbortError\") {\n const sizeMb = Math.round(fileBuffer.byteLength / (1024 * 1024));\n const timeoutErr = new PlayApiError(\n `POST upload ${path} timed out after ${effectiveTimeout}ms (file: ${sizeMb} MB)`,\n \"API_TIMEOUT\",\n undefined,\n `Upload timed out. Set GPC_UPLOAD_TIMEOUT=${effectiveTimeout * 2} (ms) or use --timeout to increase.`,\n );\n if (attempt < maxRetries) {\n lastError = timeoutErr;\n onRetry?.({\n attempt: attempt + 1,\n method: \"POST\",\n path: `upload ${path}`,\n error: timeoutErr.message,\n delayMs: Math.round(jitteredDelay(baseDelay, attempt, maxDelay)),\n timestamp: new Date().toISOString(),\n });\n continue;\n }\n throw timeoutErr;\n }\n\n const networkErr = new PlayApiError(\n `POST upload ${path} failed: ${error instanceof Error ? error.message : String(error)}`,\n \"API_NETWORK_ERROR\",\n undefined,\n \"A network error occurred. Check your internet connection.\",\n );\n if (attempt < maxRetries) {\n lastError = networkErr;\n onRetry?.({\n attempt: attempt + 1,\n method: \"POST\",\n path: `upload ${path}`,\n error: networkErr.message,\n delayMs: Math.round(jitteredDelay(baseDelay, attempt, maxDelay)),\n timestamp: new Date().toISOString(),\n });\n continue;\n }\n throw networkErr;\n } finally {\n clearTimeout(timer);\n }\n }\n\n throw (\n lastError ??\n new PlayApiError(\n \"Upload request failed\",\n \"API_NETWORK_ERROR\",\n undefined,\n \"Check your network connection and try again. Use --verbose for details.\",\n )\n );\n }\n\n return {\n get<T>(path: string, params?: Record<string, string>) {\n return request<T>(\"GET\", path, undefined, params);\n },\n post<T>(path: string, body?: unknown) {\n return request<T>(\"POST\", path, body);\n },\n put<T>(path: string, body?: unknown) {\n return request<T>(\"PUT\", path, body);\n },\n patch<T>(path: string, body?: unknown) {\n return request<T>(\"PATCH\", path, body);\n },\n delete<T>(path: string) {\n return request<T>(\"DELETE\", path);\n },\n upload<T>(path: string, filePath: string, contentType: string) {\n return uploadRequest<T>(path, filePath, contentType);\n },\n async uploadResumable<T>(\n path: string,\n filePath: string,\n contentType: string,\n uploadOptions?: ResumableUploadOptions,\n ) {\n const safeFilePath = validateFilePath(filePath);\n const fileStats = await stat(safeFilePath);\n\n // For small files, fall back to simple upload (less overhead)\n const threshold = envInt(\"GPC_UPLOAD_RESUMABLE_THRESHOLD\") ?? RESUMABLE_THRESHOLD;\n if (fileStats.size < threshold && !uploadOptions?.resumeSessionUri) {\n // Fire progress callbacks for consistency\n uploadOptions?.onProgress?.({\n bytesUploaded: 0,\n totalBytes: fileStats.size,\n percent: 0,\n bytesPerSecond: 0,\n etaSeconds: 0,\n });\n const result = await uploadRequest<T>(path, safeFilePath, contentType);\n uploadOptions?.onProgress?.({\n bytesUploaded: fileStats.size,\n totalBytes: fileStats.size,\n percent: 100,\n bytesPerSecond: 0,\n etaSeconds: 0,\n });\n return result;\n }\n\n const uploadUrl = `${UPLOAD_BASE_URL}${path}`;\n return resumableUpload<T>(\n uploadUrl,\n safeFilePath,\n contentType,\n {\n getAccessToken: () => options.auth.getAccessToken(),\n maxRetries,\n baseDelay,\n maxDelay,\n onRetry,\n },\n uploadOptions,\n );\n },\n uploadInternal<T>(path: string, filePath: string, contentType: string) {\n return uploadRequest<T>(path, filePath, contentType, INTERNAL_SHARING_UPLOAD_BASE_URL);\n },\n async download(path: string): Promise<ArrayBuffer> {\n const url = `${options.baseUrl ?? BASE_URL}${path}`;\n const token = await options.auth.getAccessToken();\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Accept-Encoding\": \"gzip, deflate\",\n Connection: \"keep-alive\",\n },\n signal: controller.signal,\n keepalive: true,\n });\n\n if (!response.ok) {\n const errorBody = await response.text();\n const mapped = mapStatusToError(response.status, errorBody);\n throw new PlayApiError(\n mapped.message ?? `GET ${path} failed with status ${response.status}: ${sanitizeErrorBody(errorBody)}`,\n mapped.code,\n response.status,\n mapped.suggestion,\n );\n }\n\n return await response.arrayBuffer();\n } finally {\n clearTimeout(timer);\n }\n },\n };\n}\n","import { open, stat } from \"node:fs/promises\";\nimport type { FileHandle } from \"node:fs/promises\";\nimport { PlayApiError } from \"./errors.js\";\nimport type { ApiResponse, ResumableUploadOptions } from \"./types.js\";\n\n/** 256 KB — Google requires chunk sizes to be multiples of this. */\nconst CHUNK_ALIGNMENT = 256 * 1024;\n\n/**\n * Google's resumable upload protocol uses HTTP 308 for \"Resume Incomplete\",\n * but RFC 7238 standardized 308 as \"Permanent Redirect\". Node.js fetch\n * follows 308 redirects automatically, so GPC would never see the raw 308.\n *\n * Google's solution: send `X-GUploader-No-308: yes` on every request.\n * The server then replies with 200 OK and sets the response header\n * `X-Http-Status-Code-Override: 308` instead of using a real 308 status.\n *\n * This matches the behavior of Google's official Go client library.\n * See: google-api-go-client/internal/gensupport/resumable.go\n */\nconst GUPLOADER_NO_308_HEADER = \"X-GUploader-No-308\";\n\n/** Check if a 200 response is actually a \"308 Resume Incomplete\" in disguise. */\nfunction isResumeIncomplete(response: Response): boolean {\n return response.headers.get(\"X-Http-Status-Code-Override\") === \"308\";\n}\n\n/** 8 MB — default chunk size (multiple of 256 KB). */\nconst DEFAULT_CHUNK_SIZE = 8 * 1024 * 1024;\n\n/** Files below this threshold use simple upload instead. */\nexport const RESUMABLE_THRESHOLD = 5 * 1024 * 1024; // 5 MB\n\nfunction envInt(name: string): number | undefined {\n const val = process.env[name];\n if (val === undefined) return undefined;\n const n = Number(val);\n return Number.isFinite(n) ? n : undefined;\n}\n\nfunction resolveChunkSize(explicit?: number): number {\n const size = explicit ?? envInt(\"GPC_UPLOAD_CHUNK_SIZE\") ?? DEFAULT_CHUNK_SIZE;\n if (size < CHUNK_ALIGNMENT || size % CHUNK_ALIGNMENT !== 0) {\n throw new PlayApiError(\n `Chunk size must be a multiple of 256 KB (got ${size} bytes)`,\n \"UPLOAD_INVALID_CHUNK_SIZE\",\n undefined,\n `Use a multiple of 262144 (256 KB). Common values: 1048576 (1 MB), 8388608 (8 MB), 16777216 (16 MB).`,\n );\n }\n return size;\n}\n\nfunction jitteredDelay(base: number, attempt: number, max: number): number {\n const exponential = base * 2 ** attempt;\n const capped = Math.min(exponential, max);\n return capped * (0.5 + Math.random() * 0.5);\n}\n\ninterface ResumableUploadContext {\n getAccessToken: () => Promise<string>;\n maxRetries: number;\n baseDelay: number;\n maxDelay: number;\n onRetry?: (info: {\n attempt: number;\n method: string;\n path: string;\n error: string;\n delayMs: number;\n timestamp: string;\n }) => void;\n}\n\n/**\n * Google Play resumable upload protocol.\n *\n * 1. Initiate session → POST with X-Upload-Content-Type/Length, get Location header\n * 2. Stream chunks → PUT chunks with Content-Range to session URI\n * 3. Resume on failure → PUT with Content-Range: bytes * /total to query progress\n */\nexport async function resumableUpload<T>(\n uploadUrl: string,\n filePath: string,\n contentType: string,\n ctx: ResumableUploadContext,\n options?: ResumableUploadOptions,\n): Promise<ApiResponse<T>> {\n const chunkSize = resolveChunkSize(options?.chunkSize);\n const maxResumeAttempts = options?.maxResumeAttempts ?? 5;\n const onProgress = options?.onProgress;\n\n const fileStats = await stat(filePath);\n const totalBytes = fileStats.size;\n\n // Step 1: Initiate resumable session (or resume existing)\n let sessionUri = options?.resumeSessionUri;\n if (!sessionUri) {\n sessionUri = await initiateSession(uploadUrl, contentType, totalBytes, ctx);\n }\n\n // Step 2: Stream file in chunks\n const startTime = Date.now();\n let offset = 0;\n\n // If resuming, query the server for where we left off\n if (options?.resumeSessionUri) {\n offset = await queryProgress(sessionUri, totalBytes, ctx);\n }\n\n let fh: FileHandle | undefined;\n try {\n fh = await open(filePath, \"r\");\n const chunkBuffer = Buffer.alloc(chunkSize);\n\n while (offset < totalBytes) {\n const remaining = totalBytes - offset;\n const bytesToRead = Math.min(chunkSize, remaining);\n const { bytesRead } = await fh.read(chunkBuffer, 0, bytesToRead, offset);\n\n if (bytesRead === 0) break;\n\n // Always copy the chunk to avoid race conditions — fetch may read the body\n // asynchronously while we overwrite chunkBuffer on the next iteration\n const chunk = Buffer.from(chunkBuffer.buffer, chunkBuffer.byteOffset, bytesRead);\n const rangeEnd = offset + bytesRead - 1;\n const contentRange = `bytes ${offset}-${rangeEnd}/${totalBytes}`;\n\n let result: ChunkResult<T> | undefined;\n for (let attempt = 0; attempt <= maxResumeAttempts; attempt++) {\n if (attempt > 0) {\n const delay = jitteredDelay(1000, attempt - 1, 30_000);\n await new Promise((r) => setTimeout(r, delay));\n\n // Query server for actual progress before retrying\n try {\n const serverOffset = await queryProgress(sessionUri, totalBytes, ctx);\n if (serverOffset >= totalBytes) {\n // Upload is fully complete — server has all bytes\n // Fetch the completion response via one more query\n const completionResult = await fetchCompletionResponse<T>(sessionUri, totalBytes, ctx);\n if (completionResult) {\n result = completionResult;\n break;\n }\n // If we can't get the response, treat as complete without body\n result = { complete: true, response: { data: {} as T, status: 200 } };\n break;\n }\n if (serverOffset >= offset + bytesRead) {\n // Server already has this chunk but upload not finished, advance\n result = { complete: false };\n break;\n }\n if (serverOffset > offset) {\n // Partial — skip to where server is, but we'll need to re-read\n // For simplicity, just retry the whole chunk since it's small enough\n }\n } catch {\n // Query failed — just retry the PUT\n }\n\n ctx.onRetry?.({\n attempt,\n method: \"PUT\",\n path: sessionUri,\n error: `Chunk upload failed at offset ${offset}, retrying`,\n delayMs: Math.round(delay),\n timestamp: new Date().toISOString(),\n });\n }\n\n result = await sendChunk<T>(sessionUri, chunk, contentRange, ctx);\n if (result) break;\n }\n\n if (!result) {\n throw new PlayApiError(\n `Upload failed: chunk at offset ${offset} could not be sent after ${maxResumeAttempts + 1} attempts`,\n \"UPLOAD_CHUNK_FAILED\",\n undefined,\n `The upload session is still valid for up to 1 week. Resume with: --resume-uri \"${sessionUri}\"`,\n );\n }\n\n offset += bytesRead;\n\n // Fire progress callback\n if (onProgress) {\n const elapsed = (Date.now() - startTime) / 1000;\n const bytesPerSecond = elapsed > 0 ? offset / elapsed : 0;\n const remainingBytes = totalBytes - offset;\n const etaSeconds = bytesPerSecond > 0 ? remainingBytes / bytesPerSecond : 0;\n\n onProgress({\n bytesUploaded: offset,\n totalBytes,\n percent: Math.round((offset / totalBytes) * 100),\n bytesPerSecond: Math.round(bytesPerSecond),\n etaSeconds: Math.round(etaSeconds),\n });\n }\n\n // If the server returned a final response (200/201), we're done\n if (result.complete && result.response) {\n return result.response;\n }\n }\n\n // All bytes sent but no completion response captured — verify with server\n try {\n const serverOffset = await queryProgress(sessionUri, totalBytes, ctx);\n if (serverOffset >= totalBytes) {\n // Upload IS complete — server confirmed. Fetch the resource.\n const completionResult = await fetchCompletionResponse<T>(sessionUri, totalBytes, ctx);\n if (completionResult?.response) {\n return completionResult.response;\n }\n // Server confirmed complete but no parseable body — return empty\n return { data: {} as T, status: 200 };\n }\n } catch {\n // Query failed — fall through to error\n }\n\n throw new PlayApiError(\n \"Upload finished sending all bytes but did not receive a completion response\",\n \"UPLOAD_NO_COMPLETION\",\n undefined,\n `The upload session may still be valid. Resume with: --resume-uri \"${sessionUri}\"`,\n );\n } finally {\n await fh?.close();\n }\n}\n\ninterface ChunkResult<T> {\n complete: boolean;\n response?: ApiResponse<T>;\n}\n\nasync function initiateSession(\n uploadUrl: string,\n contentType: string,\n totalBytes: number,\n ctx: ResumableUploadContext,\n): Promise<string> {\n const token = await ctx.getAccessToken();\n const url = uploadUrl.includes(\"?\")\n ? `${uploadUrl}&uploadType=resumable`\n : `${uploadUrl}?uploadType=resumable`;\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), 60_000); // 60s timeout for session initiation\n let response: Response;\n try {\n response = await fetch(url, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"X-Upload-Content-Type\": contentType,\n \"X-Upload-Content-Length\": String(totalBytes),\n \"Content-Length\": \"0\",\n },\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n\n if (!response.ok) {\n const body = await response.text();\n throw new PlayApiError(\n `Failed to initiate resumable upload: ${response.status} ${body.slice(0, 200)}`,\n \"UPLOAD_INITIATE_FAILED\",\n response.status,\n \"Check that the package name, edit ID, and credentials are correct.\",\n );\n }\n\n const location = response.headers.get(\"Location\");\n if (!location) {\n throw new PlayApiError(\n \"Resumable upload initiation did not return a session URI (Location header missing)\",\n \"UPLOAD_NO_SESSION_URI\",\n response.status,\n \"This is a Google API issue. Try again.\",\n );\n }\n\n return location;\n}\n\nasync function sendChunk<T>(\n sessionUri: string,\n chunk: Buffer,\n contentRange: string,\n ctx: ResumableUploadContext,\n): Promise<ChunkResult<T> | undefined> {\n const token = await ctx.getAccessToken();\n\n // Timeout: 30s base + 1s per MB of chunk data\n const chunkTimeoutMs = 30_000 + Math.ceil(chunk.byteLength / (1024 * 1024)) * 1_000;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), chunkTimeoutMs);\n let response: Response;\n try {\n response = await fetch(sessionUri, {\n method: \"PUT\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Length\": String(chunk.byteLength),\n \"Content-Range\": contentRange,\n [GUPLOADER_NO_308_HEADER]: \"yes\",\n },\n body: chunk,\n signal: controller.signal,\n redirect: \"manual\", // Belt-and-suspenders: don't follow redirects even without the header\n });\n } catch {\n // Network error or timeout — caller will retry\n return undefined;\n } finally {\n clearTimeout(timer);\n }\n\n // With X-GUploader-No-308, Google sends 200 OK for both \"chunk accepted\"\n // and \"upload complete\". Distinguish via X-Http-Status-Code-Override header.\n if (response.status === 200 || response.status === 201) {\n // Check if this is really a \"308 Resume Incomplete\" disguised as 200\n if (isResumeIncomplete(response)) {\n await response.body?.cancel();\n return { complete: false };\n }\n\n // Genuine 200/201 — upload complete, parse the resource body\n const text = await response.text();\n let data: T;\n try {\n data = text ? (JSON.parse(text) as T) : ({} as T);\n } catch {\n data = {} as T;\n }\n return { complete: true, response: { data, status: response.status } };\n }\n\n // Real 308 (fallback if X-GUploader-No-308 not honored) — chunk accepted\n if (response.status === 308) {\n await response.body?.cancel();\n return { complete: false };\n }\n\n // 404 — session not found (expired or invalid)\n if (response.status === 404) {\n throw new PlayApiError(\n \"Upload session not found. The session may have expired.\",\n \"UPLOAD_SESSION_NOT_FOUND\",\n 404,\n \"Start a new upload. Resumable upload sessions are valid for up to 1 week.\",\n );\n }\n\n // 410 — session gone\n if (response.status === 410) {\n throw new PlayApiError(\n \"Upload session has expired.\",\n \"UPLOAD_SESSION_EXPIRED\",\n 410,\n \"Start a new upload from the beginning.\",\n );\n }\n\n // 401 — token expired, refresh and retry\n if (response.status === 401) {\n await response.body?.cancel();\n return undefined;\n }\n\n // 5xx or 429 — retryable\n if (response.status === 429 || response.status >= 500) {\n await response.body?.cancel();\n return undefined;\n }\n\n // Non-retryable error\n const body = await response.text();\n throw new PlayApiError(\n `Upload chunk failed with status ${response.status}: ${body.slice(0, 200)}`,\n `UPLOAD_HTTP_${response.status}`,\n response.status,\n \"The upload encountered an unexpected error.\",\n );\n}\n\n/**\n * When queryProgress confirms the upload is complete (200/201),\n * re-query the session to get the final resource response body.\n */\nasync function fetchCompletionResponse<T>(\n sessionUri: string,\n totalBytes: number,\n ctx: ResumableUploadContext,\n): Promise<ChunkResult<T> | undefined> {\n const token = await ctx.getAccessToken();\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), 30_000);\n try {\n const response = await fetch(sessionUri, {\n method: \"PUT\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Length\": \"0\",\n \"Content-Range\": `bytes */${totalBytes}`,\n [GUPLOADER_NO_308_HEADER]: \"yes\",\n },\n signal: controller.signal,\n redirect: \"manual\",\n });\n\n // Genuine 200/201 (not a disguised 308) — upload complete with resource body\n if ((response.status === 200 || response.status === 201) && !isResumeIncomplete(response)) {\n const text = await response.text();\n let data: T;\n try {\n data = text ? (JSON.parse(text) as T) : ({} as T);\n } catch {\n data = {} as T;\n }\n return { complete: true, response: { data, status: response.status } };\n }\n\n await response.body?.cancel();\n return undefined;\n } catch {\n return undefined;\n } finally {\n clearTimeout(timer);\n }\n}\n\nasync function queryProgress(\n sessionUri: string,\n totalBytes: number,\n ctx: ResumableUploadContext,\n): Promise<number> {\n const token = await ctx.getAccessToken();\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), 30_000);\n let response: Response;\n try {\n response = await fetch(sessionUri, {\n method: \"PUT\",\n headers: {\n Authorization: `Bearer ${token}`,\n \"Content-Length\": \"0\",\n \"Content-Range\": `bytes */${totalBytes}`,\n [GUPLOADER_NO_308_HEADER]: \"yes\",\n },\n signal: controller.signal,\n redirect: \"manual\",\n });\n } finally {\n clearTimeout(timer);\n }\n\n // With X-GUploader-No-308, a \"308 Resume Incomplete\" arrives as 200\n // with X-Http-Status-Code-Override: 308. Check the Range header for progress.\n if (response.status === 308 || isResumeIncomplete(response)) {\n await response.body?.cancel();\n const range = response.headers.get(\"Range\");\n if (range) {\n const match = range.match(/bytes=0-(\\d+)/);\n if (match) {\n return Number(match[1]) + 1;\n }\n }\n return 0;\n }\n\n // Genuine 200/201 without override — upload is actually complete\n if (response.status === 200 || response.status === 201) {\n await response.body?.cancel();\n return totalBytes;\n }\n\n // 404/410 — session expired\n if (response.status === 404 || response.status === 410) {\n await response.body?.cancel();\n throw new PlayApiError(\n \"Upload session has expired while querying progress.\",\n \"UPLOAD_SESSION_EXPIRED\",\n response.status,\n \"Start a new upload from the beginning.\",\n );\n }\n\n await response.body?.cancel();\n return 0;\n}\n","export interface RateLimitBucket {\n name: string;\n maxTokens: number;\n refillRate: number;\n refillIntervalMs: number;\n}\n\nexport interface RateLimiter {\n acquire(bucket: string): Promise<void>;\n}\n\ninterface BucketState {\n tokens: number;\n lastRefillTime: number;\n config: RateLimitBucket;\n}\n\n/**\n * Google Play Developer API quota model (as of 2025):\n * - 200,000 queries per day total\n * - 6 independent per-minute buckets at 3,000 queries/min each\n *\n * The per-minute buckets are the practical constraint for CLI usage.\n * Daily limits are tracked locally via gpc quota.\n */\nexport const RATE_LIMIT_BUCKETS: Record<string, RateLimitBucket> = {\n edits: { name: \"edits\", maxTokens: 3_000, refillRate: 3_000, refillIntervalMs: 60_000 },\n purchases: { name: \"purchases\", maxTokens: 3_000, refillRate: 3_000, refillIntervalMs: 60_000 },\n reviews: { name: \"reviews\", maxTokens: 3_000, refillRate: 3_000, refillIntervalMs: 60_000 },\n reporting: { name: \"reporting\", maxTokens: 3_000, refillRate: 3_000, refillIntervalMs: 60_000 },\n monetization: { name: \"monetization\", maxTokens: 3_000, refillRate: 3_000, refillIntervalMs: 60_000 },\n default: { name: \"default\", maxTokens: 3_000, refillRate: 3_000, refillIntervalMs: 60_000 },\n};\n\n/**\n * Map an API path to the appropriate rate-limit bucket.\n * Google's quota is structured by resource type.\n */\nexport function resolveBucket(path: string): string {\n // Order matters: more specific patterns first\n if (path.includes(\"/edits/\") || path.includes(\"/edits:\")) return \"edits\";\n if (path.includes(\"/purchases/\") || path.includes(\"/orders\")) return \"purchases\";\n if (path.includes(\"/reviews\")) return \"reviews\";\n if (path.includes(\"playdeveloperreporting\") || path.includes(\"MetricSet\") || path.includes(\"anomalies\")) return \"reporting\";\n if (path.includes(\"/inappproducts\") || path.includes(\"/oneTimeProducts\") || path.includes(\"/subscriptions\") || path.includes(\"/monetization\")) return \"monetization\";\n return \"default\";\n}\n\nexport function createRateLimiter(buckets?: RateLimitBucket[]): RateLimiter {\n const states = new Map<string, BucketState>();\n\n // Initialize from provided buckets or defaults\n const effectiveBuckets = buckets ?? Object.values(RATE_LIMIT_BUCKETS);\n for (const bucket of effectiveBuckets) {\n states.set(bucket.name, {\n tokens: bucket.maxTokens,\n lastRefillTime: Date.now(),\n config: bucket,\n });\n }\n\n return {\n async acquire(bucket: string): Promise<void> {\n const state = states.get(bucket);\n if (!state) return;\n\n const now = Date.now();\n const elapsed = now - state.lastRefillTime;\n const refill = Math.floor(\n (elapsed / state.config.refillIntervalMs) * state.config.refillRate,\n );\n\n if (refill > 0) {\n state.tokens = Math.min(state.config.maxTokens, state.tokens + refill);\n state.lastRefillTime = now;\n }\n\n if (state.tokens > 0) {\n state.tokens--;\n return;\n }\n\n const tokensNeeded = 1;\n const waitMs = Math.ceil(\n (tokensNeeded / state.config.refillRate) * state.config.refillIntervalMs,\n );\n await new Promise((r) => setTimeout(r, waitMs));\n\n // Recalculate refill based on actual elapsed time since last refill\n const afterWait = Date.now();\n const totalElapsed = afterWait - state.lastRefillTime;\n const newTokens = Math.floor(\n (totalElapsed / state.config.refillIntervalMs) * state.config.refillRate,\n );\n state.tokens = Math.max(0, Math.min(state.config.maxTokens, newTokens) - 1);\n state.lastRefillTime = afterWait;\n },\n };\n}\n","import { PlayApiError } from \"./errors.js\";\nimport { createHttpClient } from \"./http.js\";\nimport type { RateLimiter } from \"./rate-limiter.js\";\nimport { resolveBucket, createRateLimiter } from \"./rate-limiter.js\";\nimport type {\n ApiClientOptions,\n ApkInfo,\n ApksListResponse,\n AppDetails,\n AppEdit,\n AppRecoveriesListResponse,\n AppRecoveryAction,\n AppRecoveryTargeting,\n CreateAppRecoveryActionRequest,\n BasePlanMigratePricesRequest,\n Bundle,\n BundleListResponse,\n ConvertRegionPricesRequest,\n ConvertRegionPricesResponse,\n CountryAvailability,\n DataSafety,\n DeobfuscationFile,\n DeobfuscationUploadResponse,\n DeviceTierConfig,\n DeviceTierConfigsListResponse,\n ExternalTransaction,\n ExternalTransactionRefund,\n ExternallyHostedApk,\n ExternallyHostedApkResponse,\n Image,\n ImageType,\n ImageUploadResponse,\n ImagesDeleteAllResponse,\n ImagesListResponse,\n InAppProduct,\n InAppProductsListResponse,\n Listing,\n ListingsListResponse,\n OffersListResponse,\n ProductPurchase,\n Release,\n ReportsListResponse,\n ReportType,\n Review,\n ReviewReplyRequest,\n ReviewReplyResponse,\n ReviewsListOptions,\n ReviewsListResponse,\n Subscription,\n SubscriptionDeferRequest,\n SubscriptionDeferResponse,\n SubscriptionOffer,\n SubscriptionPurchase,\n SubscriptionPurchaseV2,\n SubscriptionsListResponse,\n Testers,\n Track,\n TrackListResponse,\n VoidedPurchasesListResponse,\n OneTimeProduct,\n OneTimeProductsListResponse,\n OneTimeOffer,\n OneTimeOffersListResponse,\n InternalAppSharingArtifact,\n GeneratedApk,\n GeneratedApksPerVersion,\n PurchaseOption,\n PurchaseOptionsListResponse,\n InAppProductsBatchUpdateRequest,\n InAppProductsBatchUpdateResponse,\n ResumableUploadOptions,\n Order,\n BatchGetOrdersResponse,\n ProductPurchaseV2,\n SubscriptionsV2CancelRequest,\n SubscriptionsV2DeferRequest,\n SubscriptionsV2DeferResponse,\n ReleaseSummary,\n ReleasesListResponse,\n SubscriptionsBatchGetResponse,\n SubscriptionsBatchUpdateRequest,\n SubscriptionsBatchUpdateResponse,\n InAppProductsBatchDeleteRequest,\n} from \"./types.js\";\n\nexport interface PlayApiClient {\n edits: {\n insert(packageName: string): Promise<AppEdit>;\n get(packageName: string, editId: string): Promise<AppEdit>;\n validate(packageName: string, editId: string): Promise<AppEdit>;\n commit(packageName: string, editId: string): Promise<AppEdit>;\n delete(packageName: string, editId: string): Promise<void>;\n };\n\n details: {\n get(packageName: string, editId: string): Promise<AppDetails>;\n update(packageName: string, editId: string, details: Partial<AppDetails>): Promise<AppDetails>;\n patch(packageName: string, editId: string, partial: Partial<AppDetails>): Promise<AppDetails>;\n };\n\n bundles: {\n list(packageName: string, editId: string): Promise<Bundle[]>;\n upload(\n packageName: string,\n editId: string,\n filePath: string,\n uploadOptions?: ResumableUploadOptions,\n ): Promise<Bundle>;\n };\n\n tracks: {\n list(packageName: string, editId: string): Promise<Track[]>;\n get(packageName: string, editId: string, track: string): Promise<Track>;\n create(packageName: string, editId: string, trackName: string): Promise<Track>;\n update(packageName: string, editId: string, track: string, release: Release): Promise<Track>;\n patch(packageName: string, editId: string, track: string, release: Release): Promise<Track>;\n };\n\n releases: {\n list(packageName: string, track: string): Promise<ReleaseSummary[]>;\n };\n\n apks: {\n list(packageName: string, editId: string): Promise<ApkInfo[]>;\n upload(\n packageName: string,\n editId: string,\n filePath: string,\n uploadOptions?: ResumableUploadOptions,\n ): Promise<ApkInfo>;\n addExternallyHosted(\n packageName: string,\n editId: string,\n data: ExternallyHostedApk,\n ): Promise<ExternallyHostedApkResponse>;\n };\n\n listings: {\n list(packageName: string, editId: string): Promise<Listing[]>;\n get(packageName: string, editId: string, language: string): Promise<Listing>;\n update(\n packageName: string,\n editId: string,\n language: string,\n listing: Omit<Listing, \"language\">,\n ): Promise<Listing>;\n patch(\n packageName: string,\n editId: string,\n language: string,\n partial: Partial<Omit<Listing, \"language\">>,\n ): Promise<Listing>;\n delete(packageName: string, editId: string, language: string): Promise<void>;\n deleteAll(packageName: string, editId: string): Promise<void>;\n };\n\n images: {\n list(\n packageName: string,\n editId: string,\n language: string,\n imageType: ImageType,\n ): Promise<Image[]>;\n upload(\n packageName: string,\n editId: string,\n language: string,\n imageType: ImageType,\n filePath: string,\n ): Promise<Image>;\n delete(\n packageName: string,\n editId: string,\n language: string,\n imageType: ImageType,\n imageId: string,\n ): Promise<void>;\n deleteAll(\n packageName: string,\n editId: string,\n language: string,\n imageType: ImageType,\n ): Promise<Image[]>;\n };\n\n countryAvailability: {\n get(packageName: string, editId: string, track: string): Promise<CountryAvailability>;\n };\n\n dataSafety: {\n get(packageName: string): Promise<DataSafety>;\n update(packageName: string, data: DataSafety): Promise<DataSafety>;\n };\n\n reviews: {\n list(packageName: string, options?: ReviewsListOptions): Promise<ReviewsListResponse>;\n get(packageName: string, reviewId: string, translationLanguage?: string): Promise<Review>;\n reply(packageName: string, reviewId: string, replyText: string): Promise<ReviewReplyResponse>;\n };\n\n subscriptions: {\n list(\n packageName: string,\n options?: { pageToken?: string; pageSize?: number },\n ): Promise<SubscriptionsListResponse>;\n get(packageName: string, productId: string): Promise<Subscription>;\n create(packageName: string, data: Subscription, productId?: string): Promise<Subscription>;\n update(\n packageName: string,\n productId: string,\n data: Subscription,\n updateMask?: string,\n regionsVersion?: string,\n ): Promise<Subscription>;\n delete(packageName: string, productId: string): Promise<void>;\n batchGet(packageName: string, productIds: string[]): Promise<Subscription[]>;\n batchUpdate(packageName: string, requests: SubscriptionsBatchUpdateRequest): Promise<SubscriptionsBatchUpdateResponse>;\n activateBasePlan(\n packageName: string,\n productId: string,\n basePlanId: string,\n ): Promise<Subscription>;\n deactivateBasePlan(\n packageName: string,\n productId: string,\n basePlanId: string,\n ): Promise<Subscription>;\n deleteBasePlan(packageName: string, productId: string, basePlanId: string): Promise<void>;\n migratePrices(\n packageName: string,\n productId: string,\n basePlanId: string,\n body: BasePlanMigratePricesRequest,\n ): Promise<Subscription>;\n listOffers(\n packageName: string,\n productId: string,\n basePlanId: string,\n ): Promise<OffersListResponse>;\n getOffer(\n packageName: string,\n productId: string,\n basePlanId: string,\n offerId: string,\n ): Promise<SubscriptionOffer>;\n createOffer(\n packageName: string,\n productId: string,\n basePlanId: string,\n data: SubscriptionOffer,\n offerId?: string,\n ): Promise<SubscriptionOffer>;\n updateOffer(\n packageName: string,\n productId: string,\n basePlanId: string,\n offerId: string,\n data: SubscriptionOffer,\n updateMask?: string,\n regionsVersion?: string,\n ): Promise<SubscriptionOffer>;\n deleteOffer(\n packageName: string,\n productId: string,\n basePlanId: string,\n offerId: string,\n ): Promise<void>;\n activateOffer(\n packageName: string,\n productId: string,\n basePlanId: string,\n offerId: string,\n ): Promise<SubscriptionOffer>;\n deactivateOffer(\n packageName: string,\n productId: string,\n basePlanId: string,\n offerId: string,\n ): Promise<SubscriptionOffer>;\n batchUpdateBasePlanStates(\n packageName: string,\n productId: string,\n requests: { requests: Array<{ activateBasePlanRequest?: { basePlanId: string }; deactivateBasePlanRequest?: { basePlanId: string } }> },\n ): Promise<Subscription>;\n batchGetOffers(\n packageName: string,\n productId: string,\n basePlanId: string,\n offerIds: string[],\n ): Promise<{ subscriptionOffers: SubscriptionOffer[] }>;\n batchUpdateOffers(\n packageName: string,\n productId: string,\n basePlanId: string,\n requests: { requests: Array<{ subscriptionOffer: Partial<SubscriptionOffer>; updateMask?: string; regionsVersion?: string }> },\n ): Promise<{ subscriptionOffers: SubscriptionOffer[] }>;\n batchUpdateOfferStates(\n packageName: string,\n productId: string,\n basePlanId: string,\n requests: { requests: Array<{ activateSubscriptionOfferRequest?: { offerId: string }; deactivateSubscriptionOfferRequest?: { offerId: string } }> },\n ): Promise<Subscription>;\n };\n\n inappproducts: {\n list(\n packageName: string,\n options?: { token?: string; maxResults?: number },\n ): Promise<InAppProductsListResponse>;\n get(packageName: string, sku: string): Promise<InAppProduct>;\n create(\n packageName: string,\n data: InAppProduct,\n options?: { autoConvertMissingPrices?: boolean },\n ): Promise<InAppProduct>;\n update(\n packageName: string,\n sku: string,\n data: InAppProduct,\n options?: { autoConvertMissingPrices?: boolean; allowMissing?: boolean },\n ): Promise<InAppProduct>;\n delete(packageName: string, sku: string): Promise<void>;\n batchUpdate(\n packageName: string,\n requests: InAppProductsBatchUpdateRequest,\n ): Promise<InAppProductsBatchUpdateResponse>;\n batchGet(packageName: string, skus: string[]): Promise<InAppProduct[]>;\n batchDelete(packageName: string, skus: string[]): Promise<void>;\n };\n\n purchases: {\n getProduct(packageName: string, productId: string, token: string): Promise<ProductPurchase>;\n acknowledgeProduct(\n packageName: string,\n productId: string,\n token: string,\n body?: { developerPayload?: string },\n ): Promise<void>;\n consumeProduct(packageName: string, productId: string, token: string): Promise<void>;\n getSubscriptionV2(packageName: string, token: string): Promise<SubscriptionPurchaseV2>;\n getSubscriptionV1(\n packageName: string,\n subscriptionId: string,\n token: string,\n ): Promise<SubscriptionPurchase>;\n cancelSubscription(packageName: string, subscriptionId: string, token: string): Promise<void>;\n deferSubscription(\n packageName: string,\n subscriptionId: string,\n token: string,\n body: SubscriptionDeferRequest,\n ): Promise<SubscriptionDeferResponse>;\n acknowledgeSubscription(\n packageName: string,\n subscriptionId: string,\n token: string,\n body?: { developerPayload?: string },\n ): Promise<void>;\n revokeSubscriptionV2(packageName: string, token: string): Promise<void>;\n refundSubscriptionV2(packageName: string, token: string): Promise<void>;\n /** V2 cancel with cancellationType support. (Sep 2025) */\n cancelSubscriptionV2(\n packageName: string,\n token: string,\n body?: SubscriptionsV2CancelRequest,\n ): Promise<void>;\n /** V2 defer for subscriptions with add-ons. (Jan 2026) */\n deferSubscriptionV2(\n packageName: string,\n token: string,\n body: SubscriptionsV2DeferRequest,\n ): Promise<SubscriptionsV2DeferResponse>;\n /** V2 product purchase details for multi-offer OTPs. (Jun 2025) */\n getProductV2(\n packageName: string,\n token: string,\n ): Promise<ProductPurchaseV2>;\n listVoided(\n packageName: string,\n options?: { startTime?: string; endTime?: string; type?: number; includeQuantityBasedPartialRefund?: boolean; maxResults?: number; token?: string },\n ): Promise<VoidedPurchasesListResponse>;\n };\n\n orders: {\n get(packageName: string, orderId: string): Promise<Order>;\n batchGet(packageName: string, orderIds: string[]): Promise<Order[]>;\n refund(\n packageName: string,\n orderId: string,\n body?: { fullRefund?: boolean; proratedRefund?: boolean; revoke?: boolean },\n ): Promise<void>;\n };\n\n monetization: {\n convertRegionPrices(\n packageName: string,\n price: ConvertRegionPricesRequest,\n ): Promise<ConvertRegionPricesResponse>;\n };\n\n reports: {\n list(\n packageName: string,\n reportType: ReportType,\n year: number,\n month: number,\n ): Promise<ReportsListResponse>;\n };\n\n testers: {\n get(packageName: string, editId: string, track: string): Promise<Testers>;\n update(packageName: string, editId: string, track: string, testers: Testers): Promise<Testers>;\n };\n\n deobfuscation: {\n upload(\n packageName: string,\n editId: string,\n versionCode: number,\n filePath: string,\n ): Promise<DeobfuscationFile>;\n };\n\n appRecovery: {\n list(packageName: string, versionCode?: number): Promise<AppRecoveryAction[]>;\n cancel(packageName: string, appRecoveryId: string): Promise<void>;\n deploy(packageName: string, appRecoveryId: string): Promise<void>;\n create(\n packageName: string,\n request: CreateAppRecoveryActionRequest,\n ): Promise<AppRecoveryAction>;\n addTargeting(\n packageName: string,\n appRecoveryId: string,\n targeting: AppRecoveryTargeting,\n ): Promise<AppRecoveryAction>;\n };\n\n externalTransactions: {\n create(packageName: string, data: ExternalTransaction): Promise<ExternalTransaction>;\n get(packageName: string, transactionId: string): Promise<ExternalTransaction>;\n refund(\n packageName: string,\n transactionId: string,\n refundData: ExternalTransactionRefund,\n ): Promise<ExternalTransaction>;\n };\n\n deviceTiers: {\n list(packageName: string): Promise<DeviceTierConfig[]>;\n get(packageName: string, configId: string): Promise<DeviceTierConfig>;\n create(packageName: string, config: DeviceTierConfig): Promise<DeviceTierConfig>;\n };\n\n oneTimeProducts: {\n list(packageName: string): Promise<OneTimeProductsListResponse>;\n get(packageName: string, productId: string): Promise<OneTimeProduct>;\n create(packageName: string, product: OneTimeProduct): Promise<OneTimeProduct>;\n update(\n packageName: string,\n productId: string,\n product: Partial<OneTimeProduct>,\n updateMask?: string,\n regionsVersion?: string,\n ): Promise<OneTimeProduct>;\n delete(packageName: string, productId: string): Promise<void>;\n listOffers(packageName: string, productId: string): Promise<OneTimeOffersListResponse>;\n getOffer(packageName: string, productId: string, offerId: string): Promise<OneTimeOffer>;\n createOffer(packageName: string, productId: string, offer: OneTimeOffer): Promise<OneTimeOffer>;\n updateOffer(\n packageName: string,\n productId: string,\n offerId: string,\n offer: Partial<OneTimeOffer>,\n updateMask?: string,\n regionsVersion?: string,\n ): Promise<OneTimeOffer>;\n deleteOffer(packageName: string, productId: string, offerId: string): Promise<void>;\n batchGet(packageName: string, productIds: string[]): Promise<OneTimeProduct[]>;\n batchUpdate(packageName: string, requests: { requests: Array<{ oneTimeProduct: Partial<OneTimeProduct>; updateMask?: string; regionsVersion?: string }> }): Promise<{ oneTimeProducts: OneTimeProduct[] }>;\n batchDelete(packageName: string, productIds: string[]): Promise<void>;\n };\n\n purchaseOptions: {\n list(packageName: string): Promise<PurchaseOptionsListResponse>;\n get(packageName: string, purchaseOptionId: string): Promise<PurchaseOption>;\n create(packageName: string, data: PurchaseOption): Promise<PurchaseOption>;\n activate(packageName: string, purchaseOptionId: string): Promise<PurchaseOption>;\n deactivate(packageName: string, purchaseOptionId: string): Promise<PurchaseOption>;\n };\n\n internalAppSharing: {\n uploadBundle(packageName: string, bundlePath: string): Promise<InternalAppSharingArtifact>;\n uploadApk(packageName: string, apkPath: string): Promise<InternalAppSharingArtifact>;\n };\n\n generatedApks: {\n list(packageName: string, versionCode: number): Promise<GeneratedApk[]>;\n download(packageName: string, versionCode: number, id: string): Promise<ArrayBuffer>;\n };\n}\n\nasync function rateLimit(limiter: RateLimiter | undefined, bucket: string): Promise<void> {\n if (limiter) await limiter.acquire(bucket);\n}\n\nasync function autoRateLimit(limiter: RateLimiter | undefined, path: string): Promise<void> {\n if (!limiter) return;\n const bucket = resolveBucket(path);\n await limiter.acquire(bucket);\n}\n\nexport function createApiClient(options: ApiClientOptions): PlayApiClient {\n const rawHttp = createHttpClient(options);\n const defaultLimiter = createRateLimiter();\n const limiter = options.rateLimiter || defaultLimiter;\n\n // Wrap HTTP methods with automatic rate limiting based on path\n const http = {\n ...rawHttp,\n async get<T>(path: string, params?: Record<string, string>) {\n await autoRateLimit(limiter, path);\n return rawHttp.get<T>(path, params);\n },\n async post<T>(path: string, body?: unknown) {\n await autoRateLimit(limiter, path);\n return rawHttp.post<T>(path, body);\n },\n async put<T>(path: string, body?: unknown) {\n await autoRateLimit(limiter, path);\n return rawHttp.put<T>(path, body);\n },\n async patch<T>(path: string, body?: unknown) {\n await autoRateLimit(limiter, path);\n return rawHttp.patch<T>(path, body);\n },\n async delete<T>(path: string) {\n await autoRateLimit(limiter, path);\n return rawHttp.delete<T>(path);\n },\n };\n\n return {\n edits: {\n async insert(packageName) {\n const { data } = await http.post<AppEdit>(`/${packageName}/edits`);\n return data;\n },\n\n async get(packageName, editId) {\n const { data } = await http.get<AppEdit>(`/${packageName}/edits/${editId}`);\n return data;\n },\n\n async validate(packageName, editId) {\n const { data } = await http.post<AppEdit>(`/${packageName}/edits/${editId}:validate`);\n return data;\n },\n\n async commit(packageName, editId) {\n const { data } = await http.post<AppEdit>(`/${packageName}/edits/${editId}:commit`);\n return data;\n },\n\n async delete(packageName, editId) {\n await http.delete(`/${packageName}/edits/${editId}`);\n },\n },\n\n details: {\n async get(packageName, editId) {\n const { data } = await http.get<AppDetails>(`/${packageName}/edits/${editId}/details`);\n return data;\n },\n\n async update(packageName, editId, details) {\n const { data } = await http.put<AppDetails>(\n `/${packageName}/edits/${editId}/details`,\n details,\n );\n return data;\n },\n\n async patch(packageName, editId, partial) {\n const { data } = await http.patch<AppDetails>(\n `/${packageName}/edits/${editId}/details`,\n partial,\n );\n return data;\n },\n },\n\n bundles: {\n async list(packageName, editId) {\n const { data } = await http.get<BundleListResponse>(\n `/${packageName}/edits/${editId}/bundles`,\n );\n return data.bundles;\n },\n\n async upload(packageName, editId, filePath, uploadOptions) {\n const { data } = await http.uploadResumable<Bundle>(\n `/${packageName}/edits/${editId}/bundles`,\n filePath,\n \"application/octet-stream\",\n uploadOptions,\n );\n if (!data || !data.versionCode) {\n throw new PlayApiError(\n \"Upload succeeded but no bundle data returned\",\n \"API_EMPTY_RESPONSE\",\n 200,\n \"This is unexpected. Retry the upload or contact Google Play support if the issue persists.\",\n );\n }\n return data;\n },\n },\n\n tracks: {\n async list(packageName, editId) {\n const { data } = await http.get<TrackListResponse>(\n `/${packageName}/edits/${editId}/tracks`,\n );\n return data.tracks;\n },\n\n async get(packageName, editId, track) {\n const { data } = await http.get<Track>(`/${packageName}/edits/${editId}/tracks/${track}`);\n return data;\n },\n\n async create(packageName, editId, trackName) {\n const { data } = await http.post<Track>(`/${packageName}/edits/${editId}/tracks`, {\n track: trackName,\n });\n return data;\n },\n\n async update(packageName, editId, track, release) {\n const { data } = await http.put<Track>(`/${packageName}/edits/${editId}/tracks/${track}`, {\n track,\n releases: [release],\n });\n return data;\n },\n\n async patch(packageName, editId, track, release) {\n const { data } = await http.patch<Track>(`/${packageName}/edits/${editId}/tracks/${track}`, {\n track,\n releases: [release],\n });\n return data;\n },\n },\n\n releases: {\n async list(packageName, track) {\n const { data } = await http.get<ReleasesListResponse>(\n `/${packageName}/tracks/${track}/releases`,\n );\n return data.releases ?? [];\n },\n },\n\n apks: {\n async list(packageName, editId) {\n const { data } = await http.get<ApksListResponse>(\n `/${packageName}/edits/${editId}/apks`,\n );\n return data.apks || [];\n },\n\n async upload(packageName, editId, filePath, uploadOptions) {\n const { data } = await http.uploadResumable<ApkInfo>(\n `/${packageName}/edits/${editId}/apks`,\n filePath,\n \"application/vnd.android.package-archive\",\n uploadOptions,\n );\n if (!data || !data.versionCode) {\n throw new PlayApiError(\n \"Upload succeeded but no APK data returned\",\n \"API_EMPTY_RESPONSE\",\n 200,\n \"This is unexpected. Retry the upload or contact Google Play support if the issue persists.\",\n );\n }\n return data;\n },\n\n async addExternallyHosted(packageName, editId, apkData) {\n const { data } = await http.post<ExternallyHostedApkResponse>(\n `/${packageName}/edits/${editId}/apks/externallyHosted`,\n { externallyHostedApk: apkData },\n );\n return data;\n },\n },\n\n listings: {\n async list(packageName, editId) {\n const { data } = await http.get<ListingsListResponse>(\n `/${packageName}/edits/${editId}/listings`,\n );\n return data.listings || [];\n },\n\n async get(packageName, editId, language) {\n const { data } = await http.get<Listing>(\n `/${packageName}/edits/${editId}/listings/${language}`,\n );\n return data;\n },\n\n async update(packageName, editId, language, listing) {\n const { data } = await http.put<Listing>(\n `/${packageName}/edits/${editId}/listings/${language}`,\n listing,\n );\n return data;\n },\n\n async patch(packageName, editId, language, partial) {\n const { data } = await http.patch<Listing>(\n `/${packageName}/edits/${editId}/listings/${language}`,\n partial,\n );\n return data;\n },\n\n async delete(packageName, editId, language) {\n await http.delete(`/${packageName}/edits/${editId}/listings/${language}`);\n },\n\n async deleteAll(packageName, editId) {\n await http.delete(`/${packageName}/edits/${editId}/listings`);\n },\n },\n\n images: {\n async list(packageName, editId, language, imageType) {\n const { data } = await http.get<ImagesListResponse>(\n `/${packageName}/edits/${editId}/listings/${language}/${imageType}`,\n );\n return data.images || [];\n },\n\n async upload(packageName, editId, language, imageType, filePath) {\n const { data } = await http.upload<ImageUploadResponse>(\n `/${packageName}/edits/${editId}/listings/${language}/${imageType}`,\n filePath,\n filePath.endsWith(\".png\") ? \"image/png\" : \"image/jpeg\",\n );\n if (!data.image) {\n throw new PlayApiError(\n \"Upload succeeded but no image data returned\",\n \"API_EMPTY_RESPONSE\",\n 200,\n \"This is unexpected. Retry the upload or contact Google Play support if the issue persists.\",\n );\n }\n return data.image;\n },\n\n async delete(packageName, editId, language, imageType, imageId) {\n await http.delete(\n `/${packageName}/edits/${editId}/listings/${language}/${imageType}/${imageId}`,\n );\n },\n\n async deleteAll(packageName, editId, language, imageType) {\n const { data } = await http.delete<ImagesDeleteAllResponse>(\n `/${packageName}/edits/${editId}/listings/${language}/${imageType}`,\n );\n return data.deleted || [];\n },\n },\n\n countryAvailability: {\n async get(packageName, editId, track) {\n const { data } = await http.get<CountryAvailability>(\n `/${packageName}/edits/${editId}/countryAvailability/${track}`,\n );\n return data;\n },\n },\n\n dataSafety: {\n async get(packageName) {\n const { data } = await http.get<DataSafety>(`/${packageName}/dataSafety`);\n return data;\n },\n\n async update(packageName, body) {\n const { data } = await http.put<DataSafety>(`/${packageName}/dataSafety`, body);\n return data;\n },\n },\n\n reviews: {\n async list(packageName, options?) {\n const params: Record<string, string> = {};\n if (options?.token) params[\"token\"] = options.token;\n if (options?.maxResults) params[\"maxResults\"] = String(options.maxResults);\n if (options?.translationLanguage)\n params[\"translationLanguage\"] = options.translationLanguage;\n const hasParams = Object.keys(params).length > 0;\n const { data } = await http.get<ReviewsListResponse>(\n `/${packageName}/reviews`,\n hasParams ? params : undefined,\n );\n return data;\n },\n\n async get(packageName, reviewId, translationLanguage?) {\n const params: Record<string, string> = {};\n if (translationLanguage) params[\"translationLanguage\"] = translationLanguage;\n const hasParams = Object.keys(params).length > 0;\n const { data } = await http.get<Review>(\n `/${packageName}/reviews/${reviewId}`,\n hasParams ? params : undefined,\n );\n return data;\n },\n\n async reply(packageName, reviewId, replyText) {\n const body: ReviewReplyRequest = { replyText };\n const { data } = await http.post<ReviewReplyResponse>(\n `/${packageName}/reviews/${reviewId}:reply`,\n body,\n );\n return data;\n },\n },\n\n subscriptions: {\n async list(packageName, options?) {\n const params: Record<string, string> = {};\n if (options?.pageToken) params[\"pageToken\"] = options.pageToken;\n if (options?.pageSize) params[\"pageSize\"] = String(options.pageSize);\n const hasParams = Object.keys(params).length > 0;\n const { data } = await http.get<SubscriptionsListResponse>(\n `/${packageName}/subscriptions`,\n hasParams ? params : undefined,\n );\n return data;\n },\n\n async get(packageName, productId) {\n const { data } = await http.get<Subscription>(`/${packageName}/subscriptions/${productId}`);\n return data;\n },\n\n async create(packageName, body, productId?) {\n const params: Record<string, string> = {};\n if (productId) params[\"productId\"] = productId;\n params[\"regionsVersion.version\"] = \"2022/02\";\n const path = `/${packageName}/subscriptions?${new URLSearchParams(params).toString()}`;\n const { data } = await http.post<Subscription>(path, body);\n return data;\n },\n\n async update(packageName, productId, body, updateMask?, regionsVersion?) {\n const params: Record<string, string> = {};\n if (updateMask) params[\"updateMask\"] = updateMask;\n params[\"regionsVersion.version\"] = regionsVersion || \"2022/02\";\n const path = `/${packageName}/subscriptions/${productId}?${new URLSearchParams(params).toString()}`;\n const { data } = await http.patch<Subscription>(path, body);\n return data;\n },\n\n async delete(packageName, productId) {\n await http.delete(`/${packageName}/subscriptions/${productId}`);\n },\n\n async batchGet(packageName, productIds) {\n const params = productIds.map((id) => `productIds=${encodeURIComponent(id)}`).join(\"&\");\n const { data } = await http.get<SubscriptionsBatchGetResponse>(\n `/${packageName}/subscriptions:batchGet?${params}`,\n );\n return data.subscriptions ?? [];\n },\n\n async batchUpdate(packageName, requests) {\n const { data } = await http.post<SubscriptionsBatchUpdateResponse>(\n `/${packageName}/subscriptions:batchUpdate`,\n requests,\n );\n return data;\n },\n\n async activateBasePlan(packageName, productId, basePlanId) {\n const { data } = await http.post<Subscription>(\n `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}:activate`,\n );\n return data;\n },\n\n async deactivateBasePlan(packageName, productId, basePlanId) {\n const { data } = await http.post<Subscription>(\n `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}:deactivate`,\n );\n return data;\n },\n\n async deleteBasePlan(packageName, productId, basePlanId) {\n await http.delete(`/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}`);\n },\n\n async migratePrices(packageName, productId, basePlanId, body) {\n const { data } = await http.post<Subscription>(\n `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}:migratePrices`,\n body,\n );\n return data;\n },\n\n async listOffers(packageName, productId, basePlanId) {\n const { data } = await http.get<OffersListResponse>(\n `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}/offers`,\n );\n return data;\n },\n\n async getOffer(packageName, productId, basePlanId, offerId) {\n const { data } = await http.get<SubscriptionOffer>(\n `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}/offers/${offerId}`,\n );\n return data;\n },\n\n async createOffer(packageName, productId, basePlanId, body, offerId?) {\n const params: Record<string, string> = {};\n if (offerId) params[\"offerId\"] = offerId;\n params[\"regionsVersion.version\"] = \"2022/02\";\n const path = `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}/offers?${new URLSearchParams(params).toString()}`;\n const { data } = await http.post<SubscriptionOffer>(path, body);\n return data;\n },\n\n async updateOffer(\n packageName,\n productId,\n basePlanId,\n offerId,\n body,\n updateMask?,\n regionsVersion?,\n ) {\n const params: Record<string, string> = {};\n if (updateMask) params[\"updateMask\"] = updateMask;\n params[\"regionsVersion.version\"] = regionsVersion || \"2022/02\";\n const path = `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}/offers/${offerId}?${new URLSearchParams(params).toString()}`;\n const { data } = await http.patch<SubscriptionOffer>(path, body);\n return data;\n },\n\n async deleteOffer(packageName, productId, basePlanId, offerId) {\n await http.delete(\n `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}/offers/${offerId}`,\n );\n },\n\n async activateOffer(packageName, productId, basePlanId, offerId) {\n const { data } = await http.post<SubscriptionOffer>(\n `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}/offers/${offerId}:activate`,\n );\n return data;\n },\n\n async deactivateOffer(packageName, productId, basePlanId, offerId) {\n const { data } = await http.post<SubscriptionOffer>(\n `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}/offers/${offerId}:deactivate`,\n );\n return data;\n },\n\n async batchUpdateBasePlanStates(packageName, productId, requests) {\n const { data } = await http.post<Subscription>(\n `/${packageName}/subscriptions/${productId}/basePlans:batchUpdateStates`,\n requests,\n );\n return data;\n },\n\n async batchGetOffers(packageName, productId, basePlanId, offerIds) {\n const { data } = await http.post<{ subscriptionOffers: SubscriptionOffer[] }>(\n `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}/offers:batchGet`,\n { requests: offerIds.map((id) => ({ offerId: id })) },\n );\n return data;\n },\n\n async batchUpdateOffers(packageName, productId, basePlanId, requests) {\n const { data } = await http.post<{ subscriptionOffers: SubscriptionOffer[] }>(\n `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}/offers:batchUpdate`,\n requests,\n );\n return data;\n },\n\n async batchUpdateOfferStates(packageName, productId, basePlanId, requests) {\n const { data } = await http.post<Subscription>(\n `/${packageName}/subscriptions/${productId}/basePlans/${basePlanId}/offers:batchUpdateStates`,\n requests,\n );\n return data;\n },\n },\n\n inappproducts: {\n async list(packageName, options?) {\n // Note: maxResults and startIndex are deprecated and ignored by Google for inappproducts.list.\n // Server determines page size. Only token (pageToken) is supported for pagination.\n const params: Record<string, string> = {};\n if (options?.token) params[\"token\"] = options.token;\n const hasParams = Object.keys(params).length > 0;\n const { data } = await http.get<InAppProductsListResponse>(\n `/${packageName}/inappproducts`,\n hasParams ? params : undefined,\n );\n return data;\n },\n\n async get(packageName, sku) {\n const { data } = await http.get<InAppProduct>(`/${packageName}/inappproducts/${sku}`);\n return data;\n },\n\n async create(packageName, body, options?) {\n const params: Record<string, string> = {};\n if (options?.autoConvertMissingPrices) params[\"autoConvertMissingPrices\"] = \"true\";\n const hasParams = Object.keys(params).length > 0;\n const path = hasParams\n ? `/${packageName}/inappproducts?${new URLSearchParams(params).toString()}`\n : `/${packageName}/inappproducts`;\n const { data } = await http.post<InAppProduct>(path, body);\n return data;\n },\n\n async update(packageName, sku, body, options?) {\n const params: Record<string, string> = {};\n if (options?.autoConvertMissingPrices) params[\"autoConvertMissingPrices\"] = \"true\";\n if (options?.allowMissing) params[\"allowMissing\"] = \"true\";\n const hasParams = Object.keys(params).length > 0;\n const path = hasParams\n ? `/${packageName}/inappproducts/${sku}?${new URLSearchParams(params).toString()}`\n : `/${packageName}/inappproducts/${sku}`;\n const { data } = await http.put<InAppProduct>(path, body);\n return data;\n },\n\n async delete(packageName, sku) {\n await http.delete(`/${packageName}/inappproducts/${sku}`);\n },\n\n async batchUpdate(packageName, requests) {\n const { data } = await http.post<InAppProductsBatchUpdateResponse>(\n `/${packageName}/inappproducts:batchUpdate`,\n requests,\n );\n return data;\n },\n\n async batchGet(packageName, skus) {\n const params: Record<string, string> = {};\n if (skus.length > 0) {\n params[\"sku\"] = skus.join(\",\");\n }\n const { data } = await http.get<{ inappproduct: InAppProduct[] }>(\n `/${packageName}/inappproducts:batchGet`,\n Object.keys(params).length > 0 ? params : undefined,\n );\n return data.inappproduct || [];\n },\n\n async batchDelete(packageName, skus) {\n await http.post(\n `/${packageName}/inappproducts:batchDelete`,\n { requests: skus.map((sku) => ({ packageName, sku })) },\n );\n },\n },\n\n purchases: {\n async getProduct(packageName, productId, token) {\n const { data } = await http.get<ProductPurchase>(\n `/${packageName}/purchases/products/${productId}/tokens/${token}`,\n );\n return data;\n },\n\n async acknowledgeProduct(packageName, productId, token, body?) {\n await http.post(\n `/${packageName}/purchases/products/${productId}/tokens/${token}:acknowledge`,\n body,\n );\n },\n\n async consumeProduct(packageName, productId, token) {\n await http.post(`/${packageName}/purchases/products/${productId}/tokens/${token}:consume`);\n },\n\n async getSubscriptionV2(packageName, token) {\n const { data } = await http.get<SubscriptionPurchaseV2>(\n `/${packageName}/purchases/subscriptionsv2/tokens/${token}`,\n );\n return data;\n },\n\n async getSubscriptionV1(packageName, subscriptionId, token) {\n if (typeof process !== \"undefined\" && process.emitWarning) {\n process.emitWarning(\n \"purchases.subscriptions.get (v1) is deprecated by Google (shutdown Aug 2027). Use getSubscriptionV2() instead.\",\n \"DeprecationWarning\",\n );\n }\n const { data } = await http.get<SubscriptionPurchase>(\n `/${packageName}/purchases/subscriptions/${subscriptionId}/tokens/${token}`,\n );\n return data;\n },\n\n async cancelSubscription(packageName, subscriptionId, token) {\n await http.post(\n `/${packageName}/purchases/subscriptions/${subscriptionId}/tokens/${token}:cancel`,\n );\n },\n\n async deferSubscription(packageName, subscriptionId, token, body) {\n const { data } = await http.post<SubscriptionDeferResponse>(\n `/${packageName}/purchases/subscriptions/${subscriptionId}/tokens/${token}:defer`,\n body,\n );\n return data;\n },\n\n async acknowledgeSubscription(packageName, subscriptionId, token, body?) {\n await http.post(\n `/${packageName}/purchases/subscriptions/${subscriptionId}/tokens/${token}:acknowledge`,\n body ?? {},\n );\n },\n\n async revokeSubscriptionV2(packageName, token) {\n await http.post(`/${packageName}/purchases/subscriptionsv2/tokens/${token}:revoke`);\n },\n\n async refundSubscriptionV2(packageName, token) {\n await http.post(`/${packageName}/purchases/subscriptionsv2/tokens/${token}:refund`);\n },\n\n async cancelSubscriptionV2(packageName, token, body?) {\n await http.post(\n `/${packageName}/purchases/subscriptionsv2/tokens/${token}:cancel`,\n body,\n );\n },\n\n async deferSubscriptionV2(packageName, token, body) {\n const { data } = await http.post<SubscriptionsV2DeferResponse>(\n `/${packageName}/purchases/subscriptionsv2/tokens/${token}:defer`,\n body,\n );\n return data;\n },\n\n async getProductV2(packageName, token) {\n const { data } = await http.get<ProductPurchaseV2>(\n `/${packageName}/purchases/productsv2/tokens/${token}`,\n );\n return data;\n },\n\n async listVoided(packageName, options?) {\n const params: Record<string, string> = {};\n if (options?.startTime) params[\"startTime\"] = options.startTime;\n if (options?.endTime) params[\"endTime\"] = options.endTime;\n if (options?.type !== undefined) params[\"type\"] = String(options.type);\n if (options?.includeQuantityBasedPartialRefund) params[\"includeQuantityBasedPartialRefund\"] = \"true\";\n if (options?.maxResults) params[\"maxResults\"] = String(options.maxResults);\n if (options?.token) params[\"token\"] = options.token;\n const hasParams = Object.keys(params).length > 0;\n const { data } = await http.get<VoidedPurchasesListResponse>(\n `/${packageName}/purchases/voidedpurchases`,\n hasParams ? params : undefined,\n );\n return data;\n },\n },\n\n orders: {\n async get(packageName, orderId) {\n const { data } = await http.get<Order>(\n `/${packageName}/orders/${orderId}`,\n );\n return data;\n },\n\n async batchGet(packageName, orderIds) {\n const { data } = await http.post<BatchGetOrdersResponse>(\n `/${packageName}/orders:batchGet`,\n { orderIds },\n );\n return data.orders || [];\n },\n\n async refund(packageName, orderId, body?) {\n await http.post(`/${packageName}/orders/${orderId}:refund`, body);\n },\n },\n\n monetization: {\n async convertRegionPrices(packageName, price) {\n const { data } = await http.post<ConvertRegionPricesResponse>(\n `/${packageName}/pricing:convertRegionPrices`,\n price,\n );\n return data;\n },\n },\n\n reports: {\n async list(packageName, reportType, year, month) {\n const monthStr = String(month).padStart(2, \"0\");\n const { data } = await http.get<ReportsListResponse>(\n `/${packageName}/reports/${reportType}/${year}/${monthStr}`,\n );\n return data;\n },\n },\n\n testers: {\n async get(packageName, editId, track) {\n const { data } = await http.get<Testers>(\n `/${packageName}/edits/${editId}/testers/${track}`,\n );\n return data;\n },\n\n async update(packageName, editId, track, testersData) {\n const { data } = await http.put<Testers>(\n `/${packageName}/edits/${editId}/testers/${track}`,\n testersData,\n );\n return data;\n },\n },\n\n deobfuscation: {\n async upload(packageName, editId, versionCode, filePath) {\n const { data } = await http.upload<DeobfuscationUploadResponse>(\n `/${packageName}/edits/${editId}/apks/${versionCode}/deobfuscationFiles/proguard`,\n filePath,\n \"application/octet-stream\",\n );\n if (!data.deobfuscationFile) {\n throw new PlayApiError(\n \"Upload succeeded but no deobfuscation file data returned\",\n \"API_EMPTY_RESPONSE\",\n 200,\n \"This is unexpected. Retry the upload or contact Google Play support if the issue persists.\",\n );\n }\n return data.deobfuscationFile;\n },\n },\n\n appRecovery: {\n async list(packageName, versionCode?) {\n const params: Record<string, string> = {};\n if (versionCode !== undefined) params[\"versionCode\"] = String(versionCode);\n const hasParams = Object.keys(params).length > 0;\n const { data } = await http.get<AppRecoveriesListResponse>(\n `/${packageName}/appRecoveries`,\n hasParams ? params : undefined,\n );\n return data.recoveryActions || [];\n },\n\n async cancel(packageName, appRecoveryId) {\n await http.post(`/${packageName}/appRecovery/${appRecoveryId}:cancel`);\n },\n\n async deploy(packageName, appRecoveryId) {\n await http.post(`/${packageName}/appRecovery/${appRecoveryId}:deploy`);\n },\n\n async create(packageName, request) {\n const { data } = await http.post<AppRecoveryAction>(\n `/${packageName}/appRecoveries`,\n request,\n );\n return data;\n },\n\n async addTargeting(packageName, appRecoveryId, targeting) {\n const { data } = await http.post<AppRecoveryAction>(\n `/${packageName}/appRecoveries/${appRecoveryId}:addTargeting`,\n targeting,\n );\n return data;\n },\n },\n\n externalTransactions: {\n async create(packageName, body) {\n const { data } = await http.post<ExternalTransaction>(\n `/${packageName}/externalTransactions`,\n body,\n );\n return data;\n },\n\n async get(packageName, transactionId) {\n const { data } = await http.get<ExternalTransaction>(\n `/${packageName}/externalTransactions/${transactionId}`,\n );\n return data;\n },\n\n async refund(packageName, transactionId, refundData) {\n const { data } = await http.post<ExternalTransaction>(\n `/${packageName}/externalTransactions/${transactionId}:refund`,\n refundData,\n );\n return data;\n },\n },\n\n deviceTiers: {\n async list(packageName) {\n const { data } = await http.get<DeviceTierConfigsListResponse>(\n `/${packageName}/deviceTierConfigs`,\n );\n return data.deviceTierConfigs || [];\n },\n\n async get(packageName, configId) {\n const { data } = await http.get<DeviceTierConfig>(\n `/${packageName}/deviceTierConfigs/${configId}`,\n );\n return data;\n },\n\n async create(packageName, config) {\n const { data } = await http.post<DeviceTierConfig>(\n `/${packageName}/deviceTierConfigs`,\n config,\n );\n return data;\n },\n },\n\n oneTimeProducts: {\n async list(packageName) {\n const { data } = await http.get<OneTimeProductsListResponse>(\n `/${packageName}/oneTimeProducts`,\n );\n return data;\n },\n\n async get(packageName, productId) {\n const { data } = await http.get<OneTimeProduct>(\n `/${packageName}/oneTimeProducts/${productId}`,\n );\n return data;\n },\n\n async create(packageName, body) {\n const params = new URLSearchParams({ \"regionsVersion.version\": \"2022/02\" });\n const { data } = await http.post<OneTimeProduct>(\n `/${packageName}/oneTimeProducts?${params.toString()}`,\n body,\n );\n return data;\n },\n\n async update(packageName, productId, body, updateMask?, regionsVersion?) {\n const params: Record<string, string> = {};\n if (updateMask) params[\"updateMask\"] = updateMask;\n params[\"regionsVersion.version\"] = regionsVersion || \"2022/02\";\n const path = `/${packageName}/oneTimeProducts/${productId}?${new URLSearchParams(params).toString()}`;\n const { data } = await http.patch<OneTimeProduct>(path, body);\n return data;\n },\n\n async delete(packageName, productId) {\n await http.delete(`/${packageName}/oneTimeProducts/${productId}`);\n },\n\n async listOffers(packageName, productId) {\n const { data } = await http.get<OneTimeOffersListResponse>(\n `/${packageName}/oneTimeProducts/${productId}/offers`,\n );\n return data;\n },\n\n async getOffer(packageName, productId, offerId) {\n const { data } = await http.get<OneTimeOffer>(\n `/${packageName}/oneTimeProducts/${productId}/offers/${offerId}`,\n );\n return data;\n },\n\n async createOffer(packageName, productId, body) {\n const { data } = await http.post<OneTimeOffer>(\n `/${packageName}/oneTimeProducts/${productId}/offers`,\n body,\n );\n return data;\n },\n\n async updateOffer(packageName, productId, offerId, body, updateMask?, regionsVersion?) {\n const params: Record<string, string> = {};\n if (updateMask) params[\"updateMask\"] = updateMask;\n params[\"regionsVersion.version\"] = regionsVersion || \"2022/02\";\n const path = `/${packageName}/oneTimeProducts/${productId}/offers/${offerId}?${new URLSearchParams(params).toString()}`;\n const { data } = await http.patch<OneTimeOffer>(path, body);\n return data;\n },\n\n async deleteOffer(packageName, productId, offerId) {\n await http.delete(`/${packageName}/oneTimeProducts/${productId}/offers/${offerId}`);\n },\n\n async batchGet(packageName, productIds) {\n const params = productIds.map((id) => `productIds=${encodeURIComponent(id)}`).join(\"&\");\n const { data } = await http.get<{ oneTimeProducts: OneTimeProduct[] }>(\n `/${packageName}/oneTimeProducts:batchGet?${params}`,\n );\n return data.oneTimeProducts || [];\n },\n\n async batchUpdate(packageName, requests) {\n const { data } = await http.post<{ oneTimeProducts: OneTimeProduct[] }>(\n `/${packageName}/oneTimeProducts:batchUpdate`,\n requests,\n );\n return data;\n },\n\n async batchDelete(packageName, productIds) {\n await http.post(\n `/${packageName}/oneTimeProducts:batchDelete`,\n { requests: productIds.map((id) => ({ productId: id })) },\n );\n },\n },\n\n purchaseOptions: {\n async list(packageName) {\n const { data } = await http.get<PurchaseOptionsListResponse>(\n `/${packageName}/purchaseOptions`,\n );\n return data;\n },\n\n async get(packageName, purchaseOptionId) {\n const { data } = await http.get<PurchaseOption>(\n `/${packageName}/purchaseOptions/${purchaseOptionId}`,\n );\n return data;\n },\n\n async create(packageName, body) {\n const { data } = await http.post<PurchaseOption>(`/${packageName}/purchaseOptions`, body);\n return data;\n },\n\n async activate(packageName, purchaseOptionId) {\n const { data } = await http.post<PurchaseOption>(\n `/${packageName}/purchaseOptions/${purchaseOptionId}:activate`,\n );\n return data;\n },\n\n async deactivate(packageName, purchaseOptionId) {\n const { data } = await http.post<PurchaseOption>(\n `/${packageName}/purchaseOptions/${purchaseOptionId}:deactivate`,\n );\n return data;\n },\n },\n\n internalAppSharing: {\n async uploadBundle(packageName, bundlePath) {\n const { data } = await http.uploadInternal<InternalAppSharingArtifact>(\n `/${packageName}/artifacts/bundle`,\n bundlePath,\n \"application/octet-stream\",\n );\n return data;\n },\n\n async uploadApk(packageName, apkPath) {\n const { data } = await http.uploadInternal<InternalAppSharingArtifact>(\n `/${packageName}/artifacts/apk`,\n apkPath,\n \"application/vnd.android.package-archive\",\n );\n return data;\n },\n },\n\n generatedApks: {\n async list(packageName, versionCode) {\n const { data } = await http.get<GeneratedApksPerVersion>(\n `/${packageName}/generatedApks/${versionCode}`,\n );\n return data.generatedApks || [];\n },\n\n async download(packageName, versionCode, id) {\n return http.download(`/${packageName}/generatedApks/${versionCode}/download/${id}`);\n },\n },\n };\n}\n","import { createHttpClient } from \"./http.js\";\nimport { createRateLimiter, RATE_LIMIT_BUCKETS } from \"./rate-limiter.js\";\nimport type {\n AnomalyDetectionResponse,\n ApiClientOptions,\n ErrorIssuesResponse,\n ErrorReportsResponse,\n MetricSetQuery,\n MetricSetResponse,\n VitalsMetricSet,\n} from \"./types.js\";\n\nconst REPORTING_BASE_URL = \"https://playdeveloperreporting.googleapis.com/v1beta1\";\n\nexport interface ReportingApiClient {\n queryMetricSet(\n packageName: string,\n metricSet: VitalsMetricSet,\n query: MetricSetQuery,\n ): Promise<MetricSetResponse>;\n\n getAnomalies(packageName: string): Promise<AnomalyDetectionResponse>;\n\n searchErrorIssues(\n packageName: string,\n filter?: string,\n pageSize?: number,\n pageToken?: string,\n ): Promise<ErrorIssuesResponse>;\n\n searchErrorReports(\n packageName: string,\n issueName: string,\n pageSize?: number,\n pageToken?: string,\n ): Promise<ErrorReportsResponse>;\n}\n\nexport function createReportingClient(options: ApiClientOptions): ReportingApiClient {\n const http = createHttpClient({ ...options, baseUrl: REPORTING_BASE_URL });\n const reportingBucket = RATE_LIMIT_BUCKETS[\"reporting\"];\n const limiter =\n options.rateLimiter ?? createRateLimiter(reportingBucket ? [reportingBucket] : []);\n\n return {\n async queryMetricSet(packageName, metricSet, query) {\n await limiter.acquire(\"reporting\");\n const { data } = await http.post<MetricSetResponse>(\n `/apps/${packageName}/${metricSet}:query`,\n query,\n );\n return data;\n },\n\n async getAnomalies(packageName) {\n await limiter.acquire(\"reporting\");\n const { data } = await http.get<AnomalyDetectionResponse>(`/apps/${packageName}/anomalies`);\n return data;\n },\n\n async searchErrorIssues(packageName, filter?, pageSize?, pageToken?) {\n await limiter.acquire(\"reporting\");\n const params: Record<string, string> = {};\n if (filter) params[\"filter\"] = filter;\n if (pageSize) params[\"pageSize\"] = String(pageSize);\n if (pageToken) params[\"pageToken\"] = pageToken;\n const { data } = await http.get<ErrorIssuesResponse>(\n `/apps/${packageName}/errorIssues:search`,\n params,\n );\n return data;\n },\n\n async searchErrorReports(packageName, issueName, pageSize?, pageToken?) {\n await limiter.acquire(\"reporting\");\n const params: Record<string, string> = {};\n if (pageSize) params[\"pageSize\"] = String(pageSize);\n if (pageToken) params[\"pageToken\"] = pageToken;\n const { data } = await http.get<ErrorReportsResponse>(\n `/apps/${packageName}/errorIssues/${issueName}/reports`,\n params,\n );\n return data;\n },\n };\n}\n","import { createHttpClient } from \"./http.js\";\nimport type { ApiClientOptions, User, UsersListResponse, Grant } from \"./types.js\";\n\nconst USERS_BASE_URL = \"https://androidpublisher.googleapis.com/androidpublisher/v3/developers\";\n\nexport interface GrantsListResponse {\n grants: Grant[];\n nextPageToken?: string;\n}\n\nexport interface UsersApiClient {\n list(\n developerId: string,\n options?: { pageToken?: string; pageSize?: number },\n ): Promise<UsersListResponse>;\n\n get(developerId: string, userId: string): Promise<User>;\n\n create(developerId: string, user: Partial<User>): Promise<User>;\n\n update(\n developerId: string,\n userId: string,\n user: Partial<User>,\n updateMask?: string,\n ): Promise<User>;\n\n delete(developerId: string, userId: string): Promise<void>;\n\n grants: {\n list(developerId: string, email: string): Promise<GrantsListResponse>;\n create(developerId: string, email: string, grant: Partial<Grant>): Promise<Grant>;\n patch(\n developerId: string,\n email: string,\n packageName: string,\n grant: Partial<Grant>,\n updateMask?: string,\n ): Promise<Grant>;\n delete(developerId: string, email: string, packageName: string): Promise<void>;\n };\n}\n\nexport function createUsersClient(options: ApiClientOptions): UsersApiClient {\n const http = createHttpClient({ ...options, baseUrl: USERS_BASE_URL });\n\n return {\n async list(developerId, listOptions?) {\n const params: Record<string, string> = {};\n if (listOptions?.pageToken) params[\"pageToken\"] = listOptions.pageToken;\n if (listOptions?.pageSize) params[\"pageSize\"] = String(listOptions.pageSize);\n const hasParams = Object.keys(params).length > 0;\n const { data } = await http.get<UsersListResponse>(\n `/${developerId}/users`,\n hasParams ? params : undefined,\n );\n return data;\n },\n\n async get(developerId, userId) {\n const { data } = await http.get<User>(`/${developerId}/users/${userId}`);\n return data;\n },\n\n async create(developerId, user) {\n const { data } = await http.post<User>(`/${developerId}/users`, user);\n return data;\n },\n\n async update(developerId, userId, user, updateMask?) {\n let path = `/${developerId}/users/${userId}`;\n if (updateMask) {\n path += `?updateMask=${encodeURIComponent(updateMask).replace(/%2C/gi, \",\")}`;\n }\n const { data } = await http.patch<User>(path, user);\n return data;\n },\n\n async delete(developerId, userId) {\n await http.delete(`/${developerId}/users/${userId}`);\n },\n\n grants: {\n async list(developerId, email) {\n const { data } = await http.get<GrantsListResponse>(\n `/${developerId}/users/${encodeURIComponent(email)}/grants`,\n );\n return data;\n },\n\n async create(developerId, email, grant) {\n const { data } = await http.post<Grant>(\n `/${developerId}/users/${encodeURIComponent(email)}/grants`,\n grant,\n );\n return data;\n },\n\n async patch(developerId, email, packageName, grant, updateMask?) {\n let path = `/${developerId}/users/${encodeURIComponent(email)}/grants/${encodeURIComponent(packageName)}`;\n if (updateMask) {\n path += `?updateMask=${encodeURIComponent(updateMask)}`;\n }\n const { data } = await http.patch<Grant>(path, grant);\n return data;\n },\n\n async delete(developerId, email, packageName) {\n await http.delete(\n `/${developerId}/users/${encodeURIComponent(email)}/grants/${encodeURIComponent(packageName)}`,\n );\n },\n },\n };\n}\n","import { createHttpClient } from \"./http.js\";\nimport type { ApiClientOptions } from \"./types.js\";\n\nconst GAMES_BASE_URL = \"https://games.googleapis.com/games/v1\";\n\nexport interface Leaderboard {\n id: string;\n name: string;\n order: string;\n iconUrl?: string;\n}\n\nexport interface LeaderboardScore {\n leaderboardId: string;\n scoreValue: string;\n formattedScore: string;\n writeTimestamp?: string;\n tag?: string;\n}\n\nexport interface Achievement {\n id: string;\n name: string;\n description: string;\n state: \"REVEALED\" | \"HIDDEN\" | \"UNLOCKED\";\n currentSteps?: number;\n totalSteps?: number;\n experiencePoints?: number;\n formattedCurrentStepsString?: string;\n}\n\nexport interface GameEvent {\n definitionId: string;\n numEvents: string;\n formattedNumEvents: string;\n kind?: string;\n}\n\nexport interface GamesApiClient {\n leaderboards: {\n list(packageName: string): Promise<{ items?: Leaderboard[]; nextPageToken?: string }>;\n get(packageName: string, leaderboardId: string): Promise<Leaderboard>;\n getScores(\n packageName: string,\n leaderboardId: string,\n collection: string,\n timeSpan: string,\n ): Promise<{ items?: LeaderboardScore[] }>;\n };\n achievements: {\n list(packageName: string): Promise<{ items?: Achievement[]; nextPageToken?: string }>;\n reveal(packageName: string, achievementId: string): Promise<{ currentState: string }>;\n };\n events: {\n list(packageName: string): Promise<{ items?: GameEvent[]; nextPageToken?: string }>;\n };\n}\n\nexport function createGamesClient(options: ApiClientOptions): GamesApiClient {\n const http = createHttpClient({ ...options, baseUrl: GAMES_BASE_URL });\n\n function qs(params: Record<string, string>): string {\n return new URLSearchParams(params).toString();\n }\n\n return {\n leaderboards: {\n async list(packageName) {\n const { data } = await http.get<{ items?: Leaderboard[]; nextPageToken?: string }>(\n `/leaderboards?${qs({ applicationId: packageName })}`,\n );\n return data;\n },\n async get(packageName, leaderboardId) {\n const { data } = await http.get<Leaderboard>(\n `/leaderboards/${encodeURIComponent(leaderboardId)}?${qs({ applicationId: packageName })}`,\n );\n return data;\n },\n async getScores(packageName, leaderboardId, collection, timeSpan) {\n const { data } = await http.get<{ items?: LeaderboardScore[] }>(\n `/leaderboards/${encodeURIComponent(leaderboardId)}/scores/${encodeURIComponent(collection)}?${qs({ timeSpan, applicationId: packageName })}`,\n );\n return data;\n },\n },\n achievements: {\n async list(packageName) {\n const { data } = await http.get<{ items?: Achievement[]; nextPageToken?: string }>(\n `/achievements?${qs({ applicationId: packageName })}`,\n );\n return data;\n },\n async reveal(packageName, achievementId) {\n const { data } = await http.post<{ currentState: string }>(\n `/achievements/${encodeURIComponent(achievementId)}/reveal?${qs({ applicationId: packageName })}`,\n {},\n );\n return data;\n },\n },\n events: {\n async list(packageName) {\n const { data } = await http.get<{ items?: GameEvent[]; nextPageToken?: string }>(\n `/events?${qs({ applicationId: packageName })}`,\n );\n return data;\n },\n },\n };\n}\n","import { createHttpClient } from \"./http.js\";\nimport type { ApiClientOptions } from \"./types.js\";\n\nconst ENTERPRISE_BASE_URL = \"https://playcustomapp.googleapis.com/playcustomapp/v1/organizations\";\n\nexport interface CustomApp {\n packageName?: string;\n title: string;\n languageCode?: string;\n organizations?: Array<{ organizationId: string; organizationName?: string }>;\n}\n\nexport interface CustomAppsListResponse {\n customApps?: CustomApp[];\n nextPageToken?: string;\n}\n\nexport interface EnterpriseApiClient {\n apps: {\n create(organizationId: string, app: Partial<CustomApp>): Promise<CustomApp>;\n list(organizationId: string): Promise<CustomAppsListResponse>;\n };\n}\n\nexport function createEnterpriseClient(options: ApiClientOptions): EnterpriseApiClient {\n const http = createHttpClient({ ...options, baseUrl: ENTERPRISE_BASE_URL });\n\n return {\n apps: {\n async create(organizationId, app) {\n const { data } = await http.post<CustomApp>(`/${organizationId}/apps`, app);\n return data;\n },\n async list(organizationId) {\n const { data } = await http.get<CustomAppsListResponse>(`/${organizationId}/apps`);\n return data;\n },\n },\n };\n}\n","export interface PaginateOptions {\n limit?: number;\n startPageToken?: string;\n}\n\nexport async function* paginate<TItem>(\n fetchPage: (pageToken?: string) => Promise<{ items: TItem[]; nextPageToken?: string }>,\n options?: PaginateOptions,\n): AsyncGenerator<TItem[], void, unknown> {\n let pageToken = options?.startPageToken;\n let collected = 0;\n const limit = options?.limit;\n\n for (;;) {\n if (limit !== undefined && collected >= limit) break;\n\n const page = await fetchPage(pageToken);\n const items = page.items;\n\n if (items.length === 0) break;\n\n if (limit !== undefined) {\n const remaining = limit - collected;\n if (items.length > remaining) {\n yield items.slice(0, remaining);\n return;\n }\n }\n\n yield items;\n collected += items.length;\n pageToken = page.nextPageToken;\n\n if (!pageToken) break;\n }\n}\n\nexport async function paginateAll<TItem>(\n fetchPage: (pageToken?: string) => Promise<{ items: TItem[]; nextPageToken?: string }>,\n options?: PaginateOptions,\n): Promise<{ items: TItem[]; nextPageToken?: string }> {\n const allItems: TItem[] = [];\n let lastPageToken: string | undefined;\n const limit = options?.limit;\n\n for await (const items of paginate(fetchPage, options)) {\n allItems.push(...items);\n if (limit !== undefined && allItems.length >= limit) break;\n }\n\n // If we stopped due to limit, try to get the next page token for resumption\n if (limit !== undefined && allItems.length >= limit) {\n lastPageToken = undefined; // Already truncated by paginate\n }\n\n return { items: allItems, nextPageToken: lastPageToken };\n}\n\n/**\n * Fetch multiple known pages in parallel.\n * Useful when page tokens are predictable or when pre-fetching subsequent pages\n * after an initial sequential fetch reveals the token pattern.\n *\n * @param fetchPage - Function that fetches a page given a token\n * @param pageTokens - Array of page tokens to fetch concurrently\n * @param concurrency - Max concurrent requests (default: 4)\n */\nexport async function paginateParallel<TItem>(\n fetchPage: (pageToken?: string) => Promise<{ items: TItem[]; nextPageToken?: string }>,\n pageTokens: string[],\n concurrency = 4,\n): Promise<{ items: TItem[]; nextPageToken?: string }> {\n const allItems: TItem[] = [];\n let lastNextPageToken: string | undefined;\n\n // Process in batches of `concurrency`\n for (let i = 0; i < pageTokens.length; i += concurrency) {\n const batch = pageTokens.slice(i, i + concurrency);\n const results = await Promise.all(batch.map((token) => fetchPage(token)));\n\n for (const result of results) {\n allItems.push(...result.items);\n if (result.nextPageToken) {\n lastNextPageToken = result.nextPageToken;\n }\n }\n }\n\n return { items: allItems, nextPageToken: lastNextPageToken };\n}\n"],"mappings":"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","names":["stat","envInt","jitteredDelay","stat","options"]}
|