@gpc-cli/core 0.9.64 → 0.9.66
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-Q5TLP5QV.js → chunk-DL2CTMCL.js} +213 -27
- package/dist/chunk-DL2CTMCL.js.map +1 -0
- package/dist/index.d.ts +28 -3
- package/dist/index.js +8 -51
- package/dist/index.js.map +1 -1
- package/dist/{releases-X4RWUWL4.js → releases-2G45I6K2.js} +4 -2
- package/package.json +1 -1
- package/dist/chunk-Q5TLP5QV.js.map +0 -1
- /package/dist/{releases-X4RWUWL4.js.map → releases-2G45I6K2.js.map} +0 -0
|
@@ -159,7 +159,7 @@ function validateReleaseNotes(notes) {
|
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
// src/commands/releases.ts
|
|
162
|
-
import { PlayApiError as
|
|
162
|
+
import { PlayApiError as PlayApiError3 } from "@gpc-cli/api";
|
|
163
163
|
|
|
164
164
|
// src/utils/file-validation.ts
|
|
165
165
|
import { open, stat } from "fs/promises";
|
|
@@ -244,37 +244,111 @@ function formatSize(bytes) {
|
|
|
244
244
|
|
|
245
245
|
// src/utils/edit-helpers.ts
|
|
246
246
|
import { PlayApiError } from "@gpc-cli/api";
|
|
247
|
+
var REVIEW_PENDING_WARNING = "Changes committed but NOT sent for review (app has a rejected update).\nNext step: Open Google Play Console > Publishing overview > Send for review";
|
|
247
248
|
async function commitWithRescue(client, packageName, editId, commitOptions) {
|
|
248
249
|
try {
|
|
249
250
|
await client.edits.commit(packageName, editId, commitOptions);
|
|
251
|
+
return { rescued: false };
|
|
250
252
|
} catch (error) {
|
|
251
253
|
if (error instanceof PlayApiError && error.code === "API_CHANGES_NOT_SENT_FOR_REVIEW" && !commitOptions?.changesNotSentForReview) {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
);
|
|
254
|
+
console.error(`
|
|
255
|
+
WARNING: ${REVIEW_PENDING_WARNING}
|
|
256
|
+
`);
|
|
256
257
|
await client.edits.commit(packageName, editId, {
|
|
257
258
|
...commitOptions,
|
|
258
259
|
changesNotSentForReview: true
|
|
259
260
|
});
|
|
260
|
-
return;
|
|
261
|
+
return { rescued: true };
|
|
261
262
|
}
|
|
262
263
|
throw error;
|
|
263
264
|
}
|
|
264
265
|
}
|
|
265
266
|
async function validateAndCommit(client, packageName, editId, commitOptions) {
|
|
267
|
+
let rescuedFromValidate = false;
|
|
266
268
|
if (!commitOptions?.changesNotSentForReview) {
|
|
267
|
-
|
|
269
|
+
try {
|
|
270
|
+
await client.edits.validate(packageName, editId);
|
|
271
|
+
} catch (error) {
|
|
272
|
+
if (error instanceof PlayApiError && error.code === "API_CHANGES_NOT_SENT_FOR_REVIEW") {
|
|
273
|
+
console.error(`
|
|
274
|
+
WARNING: ${REVIEW_PENDING_WARNING}
|
|
275
|
+
`);
|
|
276
|
+
rescuedFromValidate = true;
|
|
277
|
+
} else {
|
|
278
|
+
throw error;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if (rescuedFromValidate) {
|
|
283
|
+
await client.edits.commit(packageName, editId, {
|
|
284
|
+
...commitOptions,
|
|
285
|
+
changesNotSentForReview: true
|
|
286
|
+
});
|
|
287
|
+
return { rescued: true };
|
|
288
|
+
}
|
|
289
|
+
return commitWithRescue(client, packageName, editId, commitOptions);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// src/commands/bundles.ts
|
|
293
|
+
import { PlayApiError as PlayApiError2 } from "@gpc-cli/api";
|
|
294
|
+
async function listBundles(client, packageName) {
|
|
295
|
+
const edit = await client.edits.insert(packageName);
|
|
296
|
+
try {
|
|
297
|
+
const bundles = await client.bundles.list(packageName, edit.id);
|
|
298
|
+
await client.edits.delete(packageName, edit.id).catch(() => {
|
|
299
|
+
});
|
|
300
|
+
return bundles;
|
|
301
|
+
} catch (error) {
|
|
302
|
+
await client.edits.delete(packageName, edit.id).catch(() => {
|
|
303
|
+
});
|
|
304
|
+
throw error;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
async function findBundle(client, packageName, versionCode) {
|
|
308
|
+
const bundles = await listBundles(client, packageName);
|
|
309
|
+
return bundles.find((b) => b.versionCode === versionCode) ?? null;
|
|
310
|
+
}
|
|
311
|
+
function isRetryableError(error) {
|
|
312
|
+
if (!(error instanceof PlayApiError2)) return false;
|
|
313
|
+
const status = error.statusCode;
|
|
314
|
+
return status === 429 || status === 500 || status === 503 || status === 409;
|
|
315
|
+
}
|
|
316
|
+
async function waitForBundle(client, packageName, versionCode, options) {
|
|
317
|
+
const timeout = options?.timeout ?? 6e5;
|
|
318
|
+
const interval = options?.interval ?? 15e3;
|
|
319
|
+
const deadline = Date.now() + timeout;
|
|
320
|
+
while (Date.now() < deadline) {
|
|
321
|
+
try {
|
|
322
|
+
const bundles = await listBundles(client, packageName);
|
|
323
|
+
const match = bundles.find((b) => b.versionCode === versionCode);
|
|
324
|
+
if (match) return match;
|
|
325
|
+
} catch (error) {
|
|
326
|
+
if (!isRetryableError(error)) throw error;
|
|
327
|
+
}
|
|
328
|
+
const remaining = deadline - Date.now();
|
|
329
|
+
if (remaining <= 0) break;
|
|
330
|
+
await new Promise((r) => setTimeout(r, Math.min(interval, remaining)));
|
|
268
331
|
}
|
|
269
|
-
|
|
332
|
+
throw new GpcError(
|
|
333
|
+
`Bundle version code ${versionCode} not found after ${Math.round(timeout / 1e3)}s`,
|
|
334
|
+
"BUNDLE_WAIT_TIMEOUT",
|
|
335
|
+
4,
|
|
336
|
+
"The bundle may still be processing. Try again with a longer --timeout, or check the Play Console."
|
|
337
|
+
);
|
|
270
338
|
}
|
|
271
339
|
|
|
272
340
|
// src/commands/releases.ts
|
|
273
341
|
var BUNDLE_POLL_BACKOFF = [2e3, 3e3, 5e3, 8e3, 13e3, 21e3, 34e3];
|
|
342
|
+
var REVIEW_PENDING_NEXT_STEP = "Send for review from Google Play Console > Publishing overview";
|
|
274
343
|
async function waitForBundleProcessing(client, packageName, editId, versionCode, backoff = BUNDLE_POLL_BACKOFF) {
|
|
344
|
+
const start = Date.now();
|
|
275
345
|
for (let i = 0; i < backoff.length; i++) {
|
|
276
346
|
const bundles = await client.bundles.list(packageName, editId);
|
|
277
347
|
if (bundles.some((b) => b.versionCode === versionCode)) return;
|
|
348
|
+
const elapsed = Math.round((Date.now() - start) / 1e3);
|
|
349
|
+
console.error(
|
|
350
|
+
` Waiting for Google to finish processing bundle... (${elapsed}s, attempt ${i + 1}/${backoff.length})`
|
|
351
|
+
);
|
|
278
352
|
await new Promise((r) => setTimeout(r, backoff[i]));
|
|
279
353
|
}
|
|
280
354
|
throw new GpcError(
|
|
@@ -291,7 +365,7 @@ async function retryOnUploadNotComplete(fn) {
|
|
|
291
365
|
try {
|
|
292
366
|
return await fn();
|
|
293
367
|
} catch (error) {
|
|
294
|
-
const isUploadNotComplete = error instanceof
|
|
368
|
+
const isUploadNotComplete = error instanceof PlayApiError3 && UPLOAD_NOT_COMPLETE_RE.test(error.message);
|
|
295
369
|
if (!isUploadNotComplete || i === UPLOAD_RETRY_BACKOFF.length - 1) throw error;
|
|
296
370
|
await new Promise((r) => setTimeout(r, UPLOAD_RETRY_BACKOFF[i]));
|
|
297
371
|
}
|
|
@@ -303,7 +377,7 @@ async function withRetryOnConflict(client, packageName, operation) {
|
|
|
303
377
|
try {
|
|
304
378
|
return await operation(edit);
|
|
305
379
|
} catch (error) {
|
|
306
|
-
const isConflict = error instanceof
|
|
380
|
+
const isConflict = error instanceof PlayApiError3 && error.statusCode === 409;
|
|
307
381
|
if (!isConflict) {
|
|
308
382
|
await client.edits.delete(packageName, edit.id).catch(() => {
|
|
309
383
|
});
|
|
@@ -347,7 +421,7 @@ async function withFreshEdit(client, packageName, operation) {
|
|
|
347
421
|
try {
|
|
348
422
|
return await operation(edit.id);
|
|
349
423
|
} catch (error) {
|
|
350
|
-
if (error instanceof
|
|
424
|
+
if (error instanceof PlayApiError3 && error.code === "API_EDIT_EXPIRED") {
|
|
351
425
|
await client.edits.delete(packageName, edit.id).catch(() => {
|
|
352
426
|
});
|
|
353
427
|
const freshEdit = await client.edits.insert(packageName);
|
|
@@ -395,7 +469,14 @@ async function uploadRelease(client, packageName, filePath, options) {
|
|
|
395
469
|
plannedRelease: {
|
|
396
470
|
status: plannedStatus,
|
|
397
471
|
...options.userFraction !== void 0 && { userFraction: options.userFraction }
|
|
398
|
-
}
|
|
472
|
+
},
|
|
473
|
+
executed: [
|
|
474
|
+
"Validated file",
|
|
475
|
+
"Opened edit session (read-only)",
|
|
476
|
+
"Fetched current track state",
|
|
477
|
+
"Discarded edit"
|
|
478
|
+
],
|
|
479
|
+
skipped: ["Upload bundle", "Assign to track", "Commit edit"]
|
|
399
480
|
};
|
|
400
481
|
}
|
|
401
482
|
if (!validation.valid) {
|
|
@@ -481,13 +562,24 @@ ${validation.errors.join("\n")}`,
|
|
|
481
562
|
validateOnly: true
|
|
482
563
|
};
|
|
483
564
|
}
|
|
484
|
-
await retryOnUploadNotComplete(
|
|
565
|
+
const commitResult = await retryOnUploadNotComplete(
|
|
485
566
|
() => commitWithRescue(client, packageName, edit.id, options.commitOptions)
|
|
486
567
|
);
|
|
568
|
+
const isInternal = options.track === "internal";
|
|
569
|
+
if (isInternal) {
|
|
570
|
+
console.error(
|
|
571
|
+
" Internal track: no Google review required. Testers will see this build immediately."
|
|
572
|
+
);
|
|
573
|
+
}
|
|
487
574
|
return {
|
|
488
575
|
versionCode: bundle.versionCode,
|
|
489
576
|
track: options.track,
|
|
490
|
-
status: release.status
|
|
577
|
+
status: release.status,
|
|
578
|
+
...isInternal && { reviewSkipped: true },
|
|
579
|
+
...commitResult.rescued && !isInternal && {
|
|
580
|
+
reviewPending: true,
|
|
581
|
+
nextStep: REVIEW_PENDING_NEXT_STEP
|
|
582
|
+
}
|
|
491
583
|
};
|
|
492
584
|
} catch (error) {
|
|
493
585
|
await client.edits.delete(packageName, edit.id).catch(() => {
|
|
@@ -552,15 +644,86 @@ async function promoteRelease(client, packageName, fromTrack, toTrack, options)
|
|
|
552
644
|
...currentRelease.name && { name: currentRelease.name }
|
|
553
645
|
};
|
|
554
646
|
await client.tracks.update(packageName, edit.id, toTrack, release);
|
|
555
|
-
await validateAndCommit(
|
|
647
|
+
const commitResult = await validateAndCommit(
|
|
648
|
+
client,
|
|
649
|
+
packageName,
|
|
650
|
+
edit.id,
|
|
651
|
+
options?.commitOptions
|
|
652
|
+
);
|
|
556
653
|
return {
|
|
557
654
|
track: toTrack,
|
|
558
655
|
status: release.status,
|
|
559
656
|
versionCodes: release.versionCodes,
|
|
560
|
-
userFraction: release.userFraction
|
|
657
|
+
userFraction: release.userFraction,
|
|
658
|
+
...commitResult.rescued && {
|
|
659
|
+
reviewPending: true,
|
|
660
|
+
nextStep: REVIEW_PENDING_NEXT_STEP
|
|
661
|
+
}
|
|
561
662
|
};
|
|
562
663
|
});
|
|
563
664
|
}
|
|
665
|
+
async function assignRelease(client, packageName, versionCode, options) {
|
|
666
|
+
if (options.userFraction && (options.userFraction <= 0 || options.userFraction > 1)) {
|
|
667
|
+
throw new GpcError(
|
|
668
|
+
"Rollout percentage must be between 0 and 1 (e.g., 0.1 for 10%)",
|
|
669
|
+
"RELEASE_INVALID_FRACTION",
|
|
670
|
+
2,
|
|
671
|
+
"Use a decimal value like 0.1 for 10%, 0.5 for 50%, or 1.0 for 100%."
|
|
672
|
+
);
|
|
673
|
+
}
|
|
674
|
+
const bundle = await findBundle(client, packageName, versionCode);
|
|
675
|
+
if (!bundle) {
|
|
676
|
+
throw new GpcError(
|
|
677
|
+
`Version code ${versionCode} not found \u2014 the bundle must be uploaded before it can be assigned`,
|
|
678
|
+
"BUNDLE_NOT_FOUND",
|
|
679
|
+
4,
|
|
680
|
+
"Upload the bundle first with 'gpc releases upload', or check the version code with 'gpc bundles list'."
|
|
681
|
+
);
|
|
682
|
+
}
|
|
683
|
+
const code = String(versionCode);
|
|
684
|
+
const release = {
|
|
685
|
+
versionCodes: [...(options.retainVersionCodes || []).filter((vc) => vc !== code), code],
|
|
686
|
+
status: options.status || (options.userFraction ? "inProgress" : "completed"),
|
|
687
|
+
...options.userFraction && { userFraction: options.userFraction },
|
|
688
|
+
...options.releaseNotes && { releaseNotes: options.releaseNotes },
|
|
689
|
+
...options.releaseName && { name: options.releaseName },
|
|
690
|
+
...options.inAppUpdatePriority !== void 0 && {
|
|
691
|
+
inAppUpdatePriority: options.inAppUpdatePriority
|
|
692
|
+
}
|
|
693
|
+
};
|
|
694
|
+
if (options.dryRun) {
|
|
695
|
+
return {
|
|
696
|
+
track: options.track,
|
|
697
|
+
status: release.status,
|
|
698
|
+
versionCodes: release.versionCodes,
|
|
699
|
+
userFraction: release.userFraction
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
const edit = await client.edits.insert(packageName);
|
|
703
|
+
try {
|
|
704
|
+
await client.tracks.update(packageName, edit.id, options.track, release);
|
|
705
|
+
const commitResult = await validateAndCommit(
|
|
706
|
+
client,
|
|
707
|
+
packageName,
|
|
708
|
+
edit.id,
|
|
709
|
+
options.commitOptions
|
|
710
|
+
);
|
|
711
|
+
return {
|
|
712
|
+
track: options.track,
|
|
713
|
+
status: release.status,
|
|
714
|
+
versionCodes: release.versionCodes,
|
|
715
|
+
userFraction: release.userFraction,
|
|
716
|
+
...commitResult.rescued && {
|
|
717
|
+
reviewPending: true,
|
|
718
|
+
nextStep: REVIEW_PENDING_NEXT_STEP
|
|
719
|
+
}
|
|
720
|
+
};
|
|
721
|
+
} catch (error) {
|
|
722
|
+
await client.edits.delete(packageName, edit.id).catch(() => {
|
|
723
|
+
});
|
|
724
|
+
throw error;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
564
727
|
async function updateRollout(client, packageName, track, action, userFraction, commitOptions) {
|
|
565
728
|
const edit = await client.edits.insert(packageName);
|
|
566
729
|
try {
|
|
@@ -618,12 +781,16 @@ async function updateRollout(client, packageName, track, action, userFraction, c
|
|
|
618
781
|
releaseNotes: currentRelease.releaseNotes || []
|
|
619
782
|
};
|
|
620
783
|
await client.tracks.update(packageName, edit.id, track, release);
|
|
621
|
-
await validateAndCommit(client, packageName, edit.id, commitOptions);
|
|
784
|
+
const commitResult = await validateAndCommit(client, packageName, edit.id, commitOptions);
|
|
622
785
|
return {
|
|
623
786
|
track,
|
|
624
787
|
status: newStatus,
|
|
625
788
|
versionCodes: release.versionCodes,
|
|
626
|
-
userFraction: newFraction
|
|
789
|
+
userFraction: newFraction,
|
|
790
|
+
...commitResult.rescued && {
|
|
791
|
+
reviewPending: true,
|
|
792
|
+
nextStep: REVIEW_PENDING_NEXT_STEP
|
|
793
|
+
}
|
|
627
794
|
};
|
|
628
795
|
} catch (error) {
|
|
629
796
|
await client.edits.delete(packageName, edit.id).catch(() => {
|
|
@@ -674,18 +841,33 @@ async function updateTrackConfig(client, packageName, trackName, config, commitO
|
|
|
674
841
|
}
|
|
675
842
|
const edit = await client.edits.insert(packageName);
|
|
676
843
|
try {
|
|
844
|
+
const nested = Array.isArray(config["releases"]) && config["releases"].length > 0 ? config["releases"][0] : void 0;
|
|
845
|
+
const pick = (key) => nested?.[key] ?? config[key];
|
|
846
|
+
const rawCodes = pick("versionCodes");
|
|
847
|
+
if (!rawCodes || rawCodes.length === 0) {
|
|
848
|
+
throw new GpcError(
|
|
849
|
+
"versionCodes must not be empty \u2014 refusing to update track with no versions",
|
|
850
|
+
"TRACK_MISSING_VERSION_CODES",
|
|
851
|
+
2,
|
|
852
|
+
"Provide at least one versionCode in the config file."
|
|
853
|
+
);
|
|
854
|
+
}
|
|
855
|
+
const versionCodes = rawCodes.map(String);
|
|
677
856
|
const release = {
|
|
678
|
-
versionCodes
|
|
679
|
-
status:
|
|
857
|
+
versionCodes,
|
|
858
|
+
status: pick("status") || "completed"
|
|
680
859
|
};
|
|
681
|
-
if (
|
|
682
|
-
release.userFraction =
|
|
860
|
+
if (pick("userFraction") !== void 0) {
|
|
861
|
+
release.userFraction = pick("userFraction");
|
|
862
|
+
}
|
|
863
|
+
if (pick("releaseNotes")) {
|
|
864
|
+
release.releaseNotes = pick("releaseNotes");
|
|
683
865
|
}
|
|
684
|
-
if (
|
|
685
|
-
release.
|
|
866
|
+
if (pick("name")) {
|
|
867
|
+
release.name = pick("name");
|
|
686
868
|
}
|
|
687
|
-
if (
|
|
688
|
-
release.
|
|
869
|
+
if (pick("inAppUpdatePriority") !== void 0) {
|
|
870
|
+
release.inAppUpdatePriority = pick("inAppUpdatePriority");
|
|
689
871
|
}
|
|
690
872
|
const track = await client.tracks.update(packageName, edit.id, trackName, release);
|
|
691
873
|
await validateAndCommit(client, packageName, edit.id, commitOptions);
|
|
@@ -808,12 +990,16 @@ export {
|
|
|
808
990
|
validateUploadFile,
|
|
809
991
|
commitWithRescue,
|
|
810
992
|
validateAndCommit,
|
|
993
|
+
listBundles,
|
|
994
|
+
findBundle,
|
|
995
|
+
waitForBundle,
|
|
811
996
|
waitForBundleProcessing,
|
|
812
997
|
retryOnUploadNotComplete,
|
|
813
998
|
withFreshEdit,
|
|
814
999
|
uploadRelease,
|
|
815
1000
|
getReleasesStatus,
|
|
816
1001
|
promoteRelease,
|
|
1002
|
+
assignRelease,
|
|
817
1003
|
updateRollout,
|
|
818
1004
|
listTracks,
|
|
819
1005
|
createTrack,
|
|
@@ -823,4 +1009,4 @@ export {
|
|
|
823
1009
|
diffReleases,
|
|
824
1010
|
uploadExternallyHosted
|
|
825
1011
|
};
|
|
826
|
-
//# sourceMappingURL=chunk-
|
|
1012
|
+
//# sourceMappingURL=chunk-DL2CTMCL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/releases.ts","../src/utils/release-notes.ts","../src/errors.ts","../src/utils/file-validation.ts","../src/utils/edit-helpers.ts","../src/commands/bundles.ts"],"sourcesContent":["import { stat } from \"node:fs/promises\";\nimport { extname } from \"node:path\";\nimport { readReleaseNotesForVersion } from \"../utils/release-notes.js\";\nimport type {\n PlayApiClient,\n Release,\n Track,\n ExternallyHostedApk,\n ExternallyHostedApkResponse,\n UploadProgressEvent,\n ResumableUploadOptions,\n EditCommitOptions,\n DeobfuscationFileType,\n} from \"@gpc-cli/api\";\nimport type { AppEdit } from \"@gpc-cli/api\";\nimport { PlayApiError } from \"@gpc-cli/api\";\nimport { GpcError } from \"../errors.js\";\nimport { validateUploadFile } from \"../utils/file-validation.js\";\nimport { validateAndCommit, commitWithRescue } from \"../utils/edit-helpers.js\";\nimport { findBundle } from \"./bundles.js\";\n\nconst BUNDLE_POLL_BACKOFF = [2_000, 3_000, 5_000, 8_000, 13_000, 21_000, 34_000];\nconst REVIEW_PENDING_NEXT_STEP = \"Send for review from Google Play Console > Publishing overview\";\n\nexport async function waitForBundleProcessing(\n client: PlayApiClient,\n packageName: string,\n editId: string,\n versionCode: number,\n backoff: number[] = BUNDLE_POLL_BACKOFF,\n): Promise<void> {\n const start = Date.now();\n for (let i = 0; i < backoff.length; i++) {\n const bundles = await client.bundles.list(packageName, editId);\n if (bundles.some((b) => b.versionCode === versionCode)) return;\n const elapsed = Math.round((Date.now() - start) / 1000);\n console.error(\n ` Waiting for Google to finish processing bundle... (${elapsed}s, attempt ${i + 1}/${backoff.length})`,\n );\n await new Promise((r) => setTimeout(r, backoff[i]));\n }\n throw new GpcError(\n `Bundle versionCode ${versionCode} not ready after ${backoff.length} poll attempts (~${Math.round(backoff.reduce((a, b) => a + b, 0) / 1000)}s)`,\n \"BUNDLE_PROCESSING_TIMEOUT\",\n 4,\n \"The AAB is still being processed by Google. Retry the upload, or use --status draft and commit later.\",\n );\n}\n\nconst UPLOAD_NOT_COMPLETE_RE = /uploads? (?:is|are) not completed/i;\nconst UPLOAD_RETRY_BACKOFF = [15_000, 30_000, 45_000];\n\nexport async function retryOnUploadNotComplete<T>(fn: () => Promise<T>): Promise<T> {\n for (let i = 0; i < UPLOAD_RETRY_BACKOFF.length; i++) {\n try {\n return await fn();\n } catch (error) {\n const isUploadNotComplete =\n error instanceof PlayApiError && UPLOAD_NOT_COMPLETE_RE.test(error.message);\n if (!isUploadNotComplete || i === UPLOAD_RETRY_BACKOFF.length - 1) throw error;\n await new Promise((r) => setTimeout(r, UPLOAD_RETRY_BACKOFF[i]));\n }\n }\n return fn();\n}\n\n/**\n * Retry an edit-based operation once if it fails with 409 Conflict (stale edit).\n * Automatically discards the stale edit and creates a fresh one on retry.\n */\nasync function withRetryOnConflict<T>(\n client: PlayApiClient,\n packageName: string,\n operation: (edit: AppEdit) => Promise<T>,\n): Promise<T> {\n const edit = await client.edits.insert(packageName);\n try {\n return await operation(edit);\n } catch (error) {\n const isConflict = error instanceof PlayApiError && error.statusCode === 409;\n if (!isConflict) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n // Discard stale edit, retry with fresh one\n await client.edits.delete(packageName, edit.id).catch(() => {});\n const freshEdit = await client.edits.insert(packageName);\n try {\n return await operation(freshEdit);\n } catch (retryError) {\n await client.edits.delete(packageName, freshEdit.id).catch(() => {});\n throw retryError;\n }\n }\n}\n\n/** Warn if edit is within 5 minutes of expiry. */\nlet _consoleEditWarningShown = false;\nfunction warnAboutConcurrentEdits(): void {\n if (_consoleEditWarningShown) return;\n _consoleEditWarningShown = true;\n process.emitWarning?.(\n \"If the Play Console has pending changes, they may be discarded when this edit is committed. \" +\n \"Avoid making changes in the Play Console while CLI operations are in progress.\",\n \"ConcurrentEditWarning\",\n );\n}\n\nfunction warnIfEditExpiring(edit: AppEdit): void {\n if (!edit.expiryTimeSeconds) return;\n const expiryMs = Number(edit.expiryTimeSeconds) * 1000;\n const remainingMs = expiryMs - Date.now();\n if (remainingMs < 5 * 60 * 1000 && remainingMs > 0) {\n const minutes = Math.round(remainingMs / 60_000);\n process.emitWarning?.(\n `Edit session expires in ~${minutes} minute${minutes !== 1 ? \"s\" : \"\"}. Long uploads may fail. Consider starting a fresh operation.`,\n \"EditExpiryWarning\",\n );\n }\n}\n\n/**\n * Run an edit-lifecycle operation with automatic retry on expired-edit errors.\n * If the API returns API_EDIT_EXPIRED (FAILED_PRECONDITION), the helper opens a\n * fresh edit and retries the operation exactly once.\n */\nexport async function withFreshEdit<T>(\n client: PlayApiClient,\n packageName: string,\n operation: (editId: string) => Promise<T>,\n): Promise<T> {\n const edit = await client.edits.insert(packageName);\n try {\n return await operation(edit.id);\n } catch (error) {\n if (error instanceof PlayApiError && error.code === \"API_EDIT_EXPIRED\") {\n // Discard stale edit (best effort) and retry with a fresh one\n await client.edits.delete(packageName, edit.id).catch(() => {});\n const freshEdit = await client.edits.insert(packageName);\n try {\n return await operation(freshEdit.id);\n } catch (retryError) {\n await client.edits.delete(packageName, freshEdit.id).catch(() => {});\n throw retryError;\n }\n }\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport interface UploadResult {\n versionCode: number;\n track: string;\n status: string;\n validateOnly?: true;\n reviewPending?: boolean;\n reviewSkipped?: boolean;\n nextStep?: string;\n}\n\nexport interface ReleaseStatusResult {\n track: string;\n status: string;\n versionCodes: string[];\n userFraction?: number;\n releaseNotes?: { language: string; text: string }[];\n reviewPending?: boolean;\n reviewSkipped?: boolean;\n nextStep?: string;\n}\n\nexport interface DryRunUploadResult {\n dryRun: true;\n file: { path: string; valid: boolean; errors: string[]; warnings: string[] };\n track: string;\n currentReleases: { versionCodes: string[]; status: string; userFraction?: number }[];\n plannedRelease: { status: string; userFraction?: number };\n executed: string[];\n skipped: string[];\n}\n\nexport async function uploadRelease(\n client: PlayApiClient,\n packageName: string,\n filePath: string,\n options: {\n track: string;\n status?: string;\n userFraction?: number;\n releaseNotes?: { language: string; text: string }[];\n releaseName?: string;\n mappingFile?: string;\n mappingFileType?: DeobfuscationFileType;\n dryRun?: boolean;\n validateOnly?: boolean;\n onProgress?: (uploaded: number, total: number) => void;\n onUploadProgress?: (event: UploadProgressEvent) => void;\n uploadOptions?: Pick<\n ResumableUploadOptions,\n \"chunkSize\" | \"resumeSessionUri\" | \"maxResumeAttempts\"\n >;\n deviceTierConfigId?: string;\n commitOptions?: EditCommitOptions;\n inAppUpdatePriority?: number;\n retainVersionCodes?: string[];\n notesDirVersioned?: string;\n },\n): Promise<UploadResult | DryRunUploadResult> {\n // Validate file before upload\n const validation = await validateUploadFile(filePath);\n\n if (options.dryRun) {\n const plannedStatus = options.status || (options.userFraction ? \"inProgress\" : \"completed\");\n\n // Fetch current track state without modifying anything\n let currentReleases: DryRunUploadResult[\"currentReleases\"] = [];\n const edit = await client.edits.insert(packageName);\n try {\n const trackData = await client.tracks.get(packageName, edit.id, options.track);\n currentReleases = (trackData.releases || []).map((r) => ({\n versionCodes: r.versionCodes || [],\n status: r.status,\n ...(r.userFraction !== undefined && { userFraction: r.userFraction }),\n }));\n } catch {\n // Track may not exist yet — that's fine for dry-run\n } finally {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n }\n\n return {\n dryRun: true,\n file: {\n path: filePath,\n valid: validation.valid,\n errors: validation.errors,\n warnings: validation.warnings,\n },\n track: options.track,\n currentReleases,\n plannedRelease: {\n status: plannedStatus,\n ...(options.userFraction !== undefined && { userFraction: options.userFraction }),\n },\n executed: [\n \"Validated file\",\n \"Opened edit session (read-only)\",\n \"Fetched current track state\",\n \"Discarded edit\",\n ],\n skipped: [\"Upload bundle\", \"Assign to track\", \"Commit edit\"],\n };\n }\n\n if (!validation.valid) {\n throw new GpcError(\n `File validation failed:\\n${validation.errors.join(\"\\n\")}`,\n \"RELEASE_INVALID_FILE\",\n 2,\n \"Check that the file is a valid AAB or APK and is not corrupted.\",\n );\n }\n\n // Get file size for progress reporting\n let fileSize = 0;\n try {\n const { size } = await stat(filePath);\n fileSize = size;\n } catch {\n /* ignore — file was validated above */\n }\n\n if (options.onProgress) options.onProgress(0, fileSize);\n\n const edit = await client.edits.insert(packageName);\n warnIfEditExpiring(edit);\n warnAboutConcurrentEdits();\n try {\n // Upload AAB or APK via the appropriate endpoint\n const isApk = extname(filePath).toLowerCase() === \".apk\";\n const uploadOpts = {\n ...options.uploadOptions,\n onProgress: (event: UploadProgressEvent) => {\n if (options.onProgress) options.onProgress(event.bytesUploaded, event.totalBytes);\n if (options.onUploadProgress) options.onUploadProgress(event);\n },\n };\n const bundle = isApk\n ? await client.apks.upload(packageName, edit.id, filePath, uploadOpts)\n : await client.bundles.upload(\n packageName,\n edit.id,\n filePath,\n uploadOpts,\n options.deviceTierConfigId,\n );\n\n // Wait for server-side AAB processing before proceeding.\n // Google's API returns from bundles.upload before manifest extraction\n // and signature verification finish, causing edits.validate to fail\n // with \"uploads are not completed yet\" on large bundles (~65MB+).\n if (!isApk) {\n await waitForBundleProcessing(client, packageName, edit.id, bundle.versionCode);\n }\n\n // Upload mapping file if provided\n if (options.mappingFile) {\n await client.deobfuscation.upload(\n packageName,\n edit.id,\n bundle.versionCode,\n options.mappingFile,\n options.mappingFileType,\n );\n }\n\n // Resolve versioned release notes now that versionCode is known\n let releaseNotes = options.releaseNotes;\n if (!releaseNotes && options.notesDirVersioned) {\n releaseNotes = await readReleaseNotesForVersion(\n options.notesDirVersioned,\n bundle.versionCode,\n );\n if (releaseNotes.length === 0) releaseNotes = undefined;\n }\n\n // Create release and assign to track\n const uploadedCode = String(bundle.versionCode);\n const release: Release = {\n versionCodes: [\n ...(options.retainVersionCodes || []).filter((vc) => vc !== uploadedCode),\n uploadedCode,\n ],\n status: (options.status ||\n (options.userFraction ? \"inProgress\" : \"completed\")) as Release[\"status\"],\n ...(options.userFraction && { userFraction: options.userFraction }),\n ...(releaseNotes && { releaseNotes }),\n ...(options.releaseName && { name: options.releaseName }),\n ...(options.inAppUpdatePriority !== undefined && {\n inAppUpdatePriority: options.inAppUpdatePriority,\n }),\n };\n\n await client.tracks.update(packageName, edit.id, options.track, release);\n\n if (!options.commitOptions?.changesNotSentForReview) {\n await retryOnUploadNotComplete(() => client.edits.validate(packageName, edit.id));\n }\n\n if (options.validateOnly) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n return {\n versionCode: bundle.versionCode,\n track: options.track,\n status: release.status,\n validateOnly: true,\n };\n }\n\n const commitResult = await retryOnUploadNotComplete(() =>\n commitWithRescue(client, packageName, edit.id, options.commitOptions),\n );\n\n const isInternal = options.track === \"internal\";\n if (isInternal) {\n console.error(\n \" Internal track: no Google review required. Testers will see this build immediately.\",\n );\n }\n\n return {\n versionCode: bundle.versionCode,\n track: options.track,\n status: release.status,\n ...(isInternal && { reviewSkipped: true }),\n ...(commitResult.rescued &&\n !isInternal && {\n reviewPending: true,\n nextStep: REVIEW_PENDING_NEXT_STEP,\n }),\n };\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function getReleasesStatus(\n client: PlayApiClient,\n packageName: string,\n trackFilter?: string,\n): Promise<ReleaseStatusResult[]> {\n const edit = await client.edits.insert(packageName);\n try {\n const tracks = trackFilter\n ? [await client.tracks.get(packageName, edit.id, trackFilter)]\n : await client.tracks.list(packageName, edit.id);\n\n await client.edits.delete(packageName, edit.id);\n\n const results: ReleaseStatusResult[] = [];\n for (const track of tracks) {\n for (const release of track.releases || []) {\n results.push({\n track: track.track,\n status: release.status,\n versionCodes: release.versionCodes || [],\n userFraction: release.userFraction,\n releaseNotes: release.releaseNotes,\n });\n }\n }\n return results;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function promoteRelease(\n client: PlayApiClient,\n packageName: string,\n fromTrack: string,\n toTrack: string,\n options?: {\n status?: string;\n userFraction?: number;\n releaseNotes?: { language: string; text: string }[];\n commitOptions?: EditCommitOptions;\n },\n): Promise<ReleaseStatusResult> {\n // Validate inputs before opening an edit\n if (options?.userFraction && (options.userFraction <= 0 || options.userFraction > 1)) {\n throw new GpcError(\n \"Rollout percentage must be between 0 and 1 (e.g., 0.1 for 10%)\",\n \"RELEASE_INVALID_FRACTION\",\n 2,\n \"Use a decimal value like 0.1 for 10%, 0.5 for 50%, or 1.0 for 100%.\",\n );\n }\n\n return withRetryOnConflict(client, packageName, async (edit) => {\n // Get current release from source track\n const sourceTrack = await client.tracks.get(packageName, edit.id, fromTrack);\n const currentRelease = sourceTrack.releases?.find(\n (r) => r.status === \"completed\" || r.status === \"inProgress\",\n );\n\n if (!currentRelease) {\n throw new GpcError(\n `No active release found on track \"${fromTrack}\"`,\n \"RELEASE_NOT_FOUND\",\n 1,\n `Ensure there is a completed or in-progress release on the \"${fromTrack}\" track before promoting.`,\n );\n }\n\n const release: Release = {\n versionCodes: currentRelease.versionCodes,\n status: (options?.status ||\n (options?.userFraction ? \"inProgress\" : \"completed\")) as Release[\"status\"],\n ...(options?.userFraction && { userFraction: options.userFraction }),\n releaseNotes: options?.releaseNotes || currentRelease.releaseNotes || [],\n ...(currentRelease.inAppUpdatePriority !== undefined && {\n inAppUpdatePriority: currentRelease.inAppUpdatePriority,\n }),\n ...(currentRelease.name && { name: currentRelease.name }),\n };\n\n await client.tracks.update(packageName, edit.id, toTrack, release);\n const commitResult = await validateAndCommit(\n client,\n packageName,\n edit.id,\n options?.commitOptions,\n );\n\n return {\n track: toTrack,\n status: release.status,\n versionCodes: release.versionCodes,\n userFraction: release.userFraction,\n ...(commitResult.rescued && {\n reviewPending: true,\n nextStep: REVIEW_PENDING_NEXT_STEP,\n }),\n };\n });\n}\n\nexport async function assignRelease(\n client: PlayApiClient,\n packageName: string,\n versionCode: number,\n options: {\n track: string;\n status?: string;\n userFraction?: number;\n releaseNotes?: { language: string; text: string }[];\n releaseName?: string;\n inAppUpdatePriority?: number;\n retainVersionCodes?: string[];\n dryRun?: boolean;\n commitOptions?: EditCommitOptions;\n },\n): Promise<ReleaseStatusResult> {\n if (options.userFraction && (options.userFraction <= 0 || options.userFraction > 1)) {\n throw new GpcError(\n \"Rollout percentage must be between 0 and 1 (e.g., 0.1 for 10%)\",\n \"RELEASE_INVALID_FRACTION\",\n 2,\n \"Use a decimal value like 0.1 for 10%, 0.5 for 50%, or 1.0 for 100%.\",\n );\n }\n\n const bundle = await findBundle(client, packageName, versionCode);\n if (!bundle) {\n throw new GpcError(\n `Version code ${versionCode} not found — the bundle must be uploaded before it can be assigned`,\n \"BUNDLE_NOT_FOUND\",\n 4,\n \"Upload the bundle first with 'gpc releases upload', or check the version code with 'gpc bundles list'.\",\n );\n }\n\n const code = String(versionCode);\n const release: Release = {\n versionCodes: [...(options.retainVersionCodes || []).filter((vc) => vc !== code), code],\n status: (options.status ||\n (options.userFraction ? \"inProgress\" : \"completed\")) as Release[\"status\"],\n ...(options.userFraction && { userFraction: options.userFraction }),\n ...(options.releaseNotes && { releaseNotes: options.releaseNotes }),\n ...(options.releaseName && { name: options.releaseName }),\n ...(options.inAppUpdatePriority !== undefined && {\n inAppUpdatePriority: options.inAppUpdatePriority,\n }),\n };\n\n if (options.dryRun) {\n return {\n track: options.track,\n status: release.status,\n versionCodes: release.versionCodes,\n userFraction: release.userFraction,\n };\n }\n\n const edit = await client.edits.insert(packageName);\n try {\n await client.tracks.update(packageName, edit.id, options.track, release);\n const commitResult = await validateAndCommit(\n client,\n packageName,\n edit.id,\n options.commitOptions,\n );\n\n return {\n track: options.track,\n status: release.status,\n versionCodes: release.versionCodes,\n userFraction: release.userFraction,\n ...(commitResult.rescued && {\n reviewPending: true,\n nextStep: REVIEW_PENDING_NEXT_STEP,\n }),\n };\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function updateRollout(\n client: PlayApiClient,\n packageName: string,\n track: string,\n action: \"increase\" | \"halt\" | \"resume\" | \"complete\",\n userFraction?: number,\n commitOptions?: EditCommitOptions,\n): Promise<ReleaseStatusResult> {\n const edit = await client.edits.insert(packageName);\n try {\n const trackData = await client.tracks.get(packageName, edit.id, track);\n const currentRelease = trackData.releases?.find(\n (r) => r.status === \"inProgress\" || r.status === \"halted\",\n );\n\n if (!currentRelease) {\n throw new GpcError(\n `No active rollout found on track \"${track}\"`,\n \"ROLLOUT_NOT_FOUND\",\n 1,\n `There is no in-progress or halted rollout on the \"${track}\" track. Start a staged rollout first with: gpc releases upload --track ${track} --status inProgress --fraction 0.1`,\n );\n }\n\n let newStatus: string;\n let newFraction: number | undefined;\n\n switch (action) {\n case \"increase\":\n if (!userFraction)\n throw new GpcError(\n \"--to <percentage> is required for rollout increase\",\n \"ROLLOUT_MISSING_FRACTION\",\n 2,\n \"Specify the target rollout percentage with --to, e.g.: gpc rollout increase --to 0.5\",\n );\n if (userFraction <= 0 || userFraction > 1) {\n throw new GpcError(\n \"Rollout percentage must be between 0 and 1 (e.g., 0.1 for 10%)\",\n \"RELEASE_INVALID_FRACTION\",\n 2,\n \"Use a decimal value like 0.1 for 10%, 0.5 for 50%, or 1.0 for 100%.\",\n );\n }\n newStatus = \"inProgress\";\n newFraction = userFraction;\n break;\n case \"halt\":\n newStatus = \"halted\";\n newFraction = currentRelease.userFraction;\n break;\n case \"resume\":\n newStatus = \"inProgress\";\n newFraction = currentRelease.userFraction;\n break;\n case \"complete\":\n newStatus = \"completed\";\n newFraction = undefined;\n break;\n }\n\n const release: Release = {\n versionCodes: currentRelease.versionCodes,\n status: newStatus as Release[\"status\"],\n ...(newFraction !== undefined && { userFraction: newFraction }),\n releaseNotes: currentRelease.releaseNotes || [],\n };\n\n await client.tracks.update(packageName, edit.id, track, release);\n const commitResult = await validateAndCommit(client, packageName, edit.id, commitOptions);\n\n return {\n track,\n status: newStatus,\n versionCodes: release.versionCodes,\n userFraction: newFraction,\n ...(commitResult.rescued && {\n reviewPending: true,\n nextStep: REVIEW_PENDING_NEXT_STEP,\n }),\n };\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function listTracks(client: PlayApiClient, packageName: string): Promise<Track[]> {\n const edit = await client.edits.insert(packageName);\n try {\n const tracks = await client.tracks.list(packageName, edit.id);\n await client.edits.delete(packageName, edit.id);\n return tracks;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function createTrack(\n client: PlayApiClient,\n packageName: string,\n trackName: string,\n commitOptions?: EditCommitOptions,\n): Promise<Track> {\n if (!trackName || trackName.trim().length === 0) {\n throw new GpcError(\n \"Track name must not be empty\",\n \"TRACK_INVALID_NAME\",\n 2,\n \"Provide a valid custom track name, e.g.: gpc tracks create my-qa-track\",\n );\n }\n\n const edit = await client.edits.insert(packageName);\n try {\n const track = await client.tracks.create(packageName, edit.id, trackName);\n await validateAndCommit(client, packageName, edit.id, commitOptions);\n return track;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function updateTrackConfig(\n client: PlayApiClient,\n packageName: string,\n trackName: string,\n config: Record<string, unknown>,\n commitOptions?: EditCommitOptions,\n): Promise<Track> {\n if (!trackName || trackName.trim().length === 0) {\n throw new GpcError(\n \"Track name must not be empty\",\n \"TRACK_INVALID_NAME\",\n 2,\n \"Provide a valid track name.\",\n );\n }\n\n const edit = await client.edits.insert(packageName);\n try {\n // Support both flat config and nested track-shaped JSON (releases[0].*)\n const nested =\n Array.isArray(config[\"releases\"]) && config[\"releases\"].length > 0\n ? (config[\"releases\"] as Record<string, unknown>[])[0]\n : undefined;\n const pick = (key: string) => nested?.[key] ?? config[key];\n\n const rawCodes = pick(\"versionCodes\") as (string | number)[] | undefined;\n if (!rawCodes || rawCodes.length === 0) {\n throw new GpcError(\n \"versionCodes must not be empty — refusing to update track with no versions\",\n \"TRACK_MISSING_VERSION_CODES\",\n 2,\n \"Provide at least one versionCode in the config file.\",\n );\n }\n const versionCodes = rawCodes.map(String);\n\n const release: Release = {\n versionCodes,\n status: ((pick(\"status\") as string) || \"completed\") as Release[\"status\"],\n };\n if (pick(\"userFraction\") !== undefined) {\n release.userFraction = pick(\"userFraction\") as number;\n }\n if (pick(\"releaseNotes\")) {\n release.releaseNotes = pick(\"releaseNotes\") as { language: string; text: string }[];\n }\n if (pick(\"name\")) {\n release.name = pick(\"name\") as string;\n }\n if (pick(\"inAppUpdatePriority\") !== undefined) {\n release.inAppUpdatePriority = pick(\"inAppUpdatePriority\") as number;\n }\n\n const track = await client.tracks.update(packageName, edit.id, trackName, release);\n await validateAndCommit(client, packageName, edit.id, commitOptions);\n return track;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\n/**\n * Fetch release notes from the latest active release on a given track.\n * Opens and discards an edit — read-only, no mutations.\n */\nexport async function fetchReleaseNotes(\n client: PlayApiClient,\n packageName: string,\n track: string,\n): Promise<{ language: string; text: string }[]> {\n const edit = await client.edits.insert(packageName);\n try {\n const trackData = await client.tracks.get(packageName, edit.id, track);\n const release =\n trackData.releases?.find((r) => r.status === \"completed\" || r.status === \"inProgress\") ??\n trackData.releases?.[0];\n\n if (!release) {\n throw new GpcError(\n `No release found on track \"${track}\" to copy notes from`,\n \"RELEASE_NOT_FOUND\",\n 1,\n `Ensure there is a release on the \"${track}\" track.`,\n );\n }\n\n return release.releaseNotes ?? [];\n } finally {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n }\n}\n\nexport interface ApplyReleaseNotesResult {\n track: string;\n versionCodes: string[];\n localeCount: number;\n releaseNotes: { language: string; text: string }[];\n}\n\nexport async function applyReleaseNotes(\n client: PlayApiClient,\n packageName: string,\n track: string,\n releaseNotes: { language: string; text: string }[],\n commitOptions?: EditCommitOptions,\n): Promise<ApplyReleaseNotesResult> {\n return withRetryOnConflict(client, packageName, async (edit) => {\n const trackData = await client.tracks.get(packageName, edit.id, track);\n const draft = trackData.releases?.find((r) => r.status === \"draft\");\n\n if (!draft) {\n throw new GpcError(\n `No draft release found on track \"${track}\"`,\n \"RELEASE_NO_DRAFT\",\n 1,\n `Upload an AAB/APK first to create a draft, or check the --track value. Current track: \"${track}\".`,\n );\n }\n\n const patched: Release = {\n ...draft,\n releaseNotes,\n };\n\n await client.tracks.update(packageName, edit.id, track, patched);\n await validateAndCommit(client, packageName, edit.id, commitOptions);\n\n return {\n track,\n versionCodes: draft.versionCodes || [],\n localeCount: releaseNotes.length,\n releaseNotes,\n };\n });\n}\n\nexport interface ReleaseDiff {\n field: string;\n track1Value: string;\n track2Value: string;\n}\n\nexport async function diffReleases(\n client: PlayApiClient,\n packageName: string,\n fromTrack: string,\n toTrack: string,\n): Promise<{ fromTrack: string; toTrack: string; diffs: ReleaseDiff[] }> {\n const edit = await client.edits.insert(packageName);\n try {\n const [fromData, toData] = await Promise.all([\n client.tracks.get(packageName, edit.id, fromTrack),\n client.tracks.get(packageName, edit.id, toTrack),\n ]);\n await client.edits.delete(packageName, edit.id);\n\n const fromRelease = fromData.releases?.[0];\n const toRelease = toData.releases?.[0];\n const diffs: ReleaseDiff[] = [];\n\n const fields = [\"versionCodes\", \"status\", \"userFraction\", \"releaseNotes\", \"name\"] as const;\n for (const field of fields) {\n const v1 = fromRelease ? JSON.stringify(fromRelease[field] ?? null) : \"null\";\n const v2 = toRelease ? JSON.stringify(toRelease[field] ?? null) : \"null\";\n if (v1 !== v2) {\n diffs.push({ field, track1Value: v1, track2Value: v2 });\n }\n }\n\n return { fromTrack, toTrack, diffs };\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function uploadExternallyHosted(\n client: PlayApiClient,\n packageName: string,\n data: ExternallyHostedApk,\n commitOptions?: EditCommitOptions,\n): Promise<ExternallyHostedApkResponse> {\n if (!data.externallyHostedUrl) {\n throw new GpcError(\n \"externallyHostedUrl is required\",\n \"EXTERNAL_APK_MISSING_URL\",\n 2,\n \"Provide a valid URL for the externally hosted APK.\",\n );\n }\n\n if (!data.packageName) {\n throw new GpcError(\n \"packageName is required in externally hosted APK data\",\n \"EXTERNAL_APK_MISSING_PACKAGE\",\n 2,\n \"Include the packageName field in the APK configuration.\",\n );\n }\n\n const edit = await client.edits.insert(packageName);\n try {\n const result = await client.apks.addExternallyHosted(packageName, edit.id, data);\n await validateAndCommit(client, packageName, edit.id, commitOptions);\n return result;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n","import { readdir, readFile, lstat } from \"node:fs/promises\";\nimport { extname, basename, join } from \"node:path\";\nimport { GpcError } from \"../errors.js\";\n\nexport interface ReleaseNote {\n language: string;\n text: string;\n}\n\nexport interface ReleaseNotesValidation {\n valid: boolean;\n errors: string[];\n warnings: string[];\n}\n\nconst MAX_NOTES_LENGTH = 500;\n\nexport async function readReleaseNotesFromDir(dir: string): Promise<ReleaseNote[]> {\n let entries: string[];\n try {\n entries = await readdir(dir);\n } catch {\n throw new GpcError(\n `Release notes directory not found: ${dir}`,\n \"RELEASE_NOTES_DIR_NOT_FOUND\",\n 1,\n `Create the directory and add .txt files named by language code (e.g., en-US.txt). Path: ${dir}`,\n );\n }\n\n const notes: ReleaseNote[] = [];\n\n for (const entry of entries) {\n if (extname(entry) !== \".txt\") continue;\n\n const language = basename(entry, \".txt\");\n const filePath = join(dir, entry);\n\n const stats = await lstat(filePath);\n if (!stats.isFile() || stats.isSymbolicLink()) continue;\n\n const text = (await readFile(filePath, \"utf-8\")).trim();\n if (text.length === 0) continue;\n\n notes.push({ language, text });\n }\n\n return notes;\n}\n\nconst LOCALE_PATTERN = /^[a-z]{2}(-[A-Z]{2,3})?$/;\n\nexport async function isVersionedNotesDir(dir: string): Promise<boolean> {\n let entries: string[];\n try {\n entries = await readdir(dir);\n } catch {\n return false;\n }\n for (const entry of entries) {\n if (!LOCALE_PATTERN.test(entry)) continue;\n const entryPath = join(dir, entry);\n const stats = await lstat(entryPath);\n if (stats.isDirectory() && !stats.isSymbolicLink()) return true;\n }\n return false;\n}\n\nexport async function readReleaseNotesForVersion(\n dir: string,\n versionCode: number,\n): Promise<ReleaseNote[]> {\n if (!Number.isInteger(versionCode) || versionCode <= 0) {\n throw new GpcError(\n `Invalid version code: ${versionCode}`,\n \"INVALID_VERSION_CODE\",\n 2,\n \"Version code must be a positive integer.\",\n );\n }\n\n let entries: string[];\n try {\n entries = await readdir(dir);\n } catch {\n throw new GpcError(\n `Release notes directory not found: ${dir}`,\n \"RELEASE_NOTES_DIR_NOT_FOUND\",\n 1,\n `Create the directory with language subdirectories containing {versionCode}.txt or default.txt. Path: ${dir}`,\n );\n }\n\n const notes: ReleaseNote[] = [];\n\n for (const entry of entries) {\n const entryPath = join(dir, entry);\n const stats = await lstat(entryPath);\n if (!stats.isDirectory() || stats.isSymbolicLink()) continue;\n\n const language = entry;\n const versionFile = join(entryPath, `${versionCode}.txt`);\n const defaultFile = join(entryPath, \"default.txt\");\n\n let text: string | undefined;\n try {\n text = (await readFile(versionFile, \"utf-8\")).trim();\n } catch (err: unknown) {\n if (err && typeof err === \"object\" && \"code\" in err && err.code !== \"ENOENT\") throw err;\n try {\n text = (await readFile(defaultFile, \"utf-8\")).trim();\n } catch (err2: unknown) {\n if (err2 && typeof err2 === \"object\" && \"code\" in err2 && err2.code !== \"ENOENT\")\n throw err2;\n continue;\n }\n }\n\n if (text && text.length > 0) {\n notes.push({ language, text });\n }\n }\n\n return notes;\n}\n\nexport function validateReleaseNotes(notes: ReleaseNote[]): ReleaseNotesValidation {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n const seen = new Set<string>();\n for (const note of notes) {\n if (seen.has(note.language)) {\n errors.push(`Duplicate language code: ${note.language}`);\n }\n seen.add(note.language);\n\n if (note.text.length > MAX_NOTES_LENGTH) {\n warnings.push(\n `Release notes for \"${note.language}\" are ${note.text.length} chars (max ${MAX_NOTES_LENGTH}) — Google Play will reject notes exceeding this limit`,\n );\n }\n }\n\n return { valid: errors.length === 0, errors, warnings };\n}\n","export class GpcError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly exitCode: number,\n public readonly suggestion?: string,\n ) {\n super(message);\n this.name = \"GpcError\";\n }\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\nexport class ConfigError extends GpcError {\n constructor(message: string, code: string, suggestion?: string) {\n super(message, code, 1, suggestion);\n this.name = \"ConfigError\";\n }\n}\n\nexport class ApiError extends GpcError {\n constructor(\n message: string,\n code: string,\n public readonly statusCode?: number,\n suggestion?: string,\n ) {\n super(message, code, 4, suggestion);\n this.name = \"ApiError\";\n }\n}\n\nexport class NetworkError extends GpcError {\n constructor(message: string, suggestion?: string) {\n super(message, \"NETWORK_ERROR\", 5, suggestion);\n this.name = \"NetworkError\";\n }\n}\n","import { open, stat } from \"node:fs/promises\";\nimport { extname } from \"node:path\";\n\nexport interface FileValidationResult {\n valid: boolean;\n fileType: \"aab\" | \"apk\" | \"unknown\";\n sizeBytes: number;\n errors: string[];\n warnings: string[];\n}\n\n// ZIP magic bytes: PK\\x03\\x04\nconst ZIP_MAGIC = Buffer.from([0x50, 0x4b, 0x03, 0x04]);\n\nconst MAX_APK_SIZE = 1024 * 1024 * 1024; // 1 GB (Google Play API limit)\nconst MAX_AAB_SIZE = 2 * 1024 * 1024 * 1024; // 2 GB (Google Play API limit)\nconst LARGE_FILE_THRESHOLD = 100 * 1024 * 1024; // 100 MB — warn about upload time\n\nexport async function validateUploadFile(filePath: string): Promise<FileValidationResult> {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n // Check extension\n const ext = extname(filePath).toLowerCase();\n let fileType: FileValidationResult[\"fileType\"] = \"unknown\";\n\n if (ext === \".aab\") {\n fileType = \"aab\";\n } else if (ext === \".apk\") {\n fileType = \"apk\";\n } else {\n errors.push(`Unsupported file extension \"${ext}\". Expected .aab or .apk`);\n }\n\n // Check file exists and get size\n let sizeBytes: number;\n try {\n const stats = await stat(filePath);\n sizeBytes = stats.size;\n\n if (sizeBytes === 0) {\n errors.push(\"File is empty (0 bytes)\");\n }\n } catch {\n errors.push(`File not found: ${filePath}`);\n return { valid: false, fileType, sizeBytes: 0, errors, warnings };\n }\n\n // Check size limits\n if (fileType === \"apk\" && sizeBytes > MAX_APK_SIZE) {\n errors.push(\n `APK exceeds 1 GB limit (${formatSize(sizeBytes)}). Consider using AAB format instead.`,\n );\n }\n if (fileType === \"aab\" && sizeBytes > MAX_AAB_SIZE) {\n errors.push(`AAB exceeds 2 GB limit (${formatSize(sizeBytes)}).`);\n }\n\n if (sizeBytes > LARGE_FILE_THRESHOLD && errors.length === 0) {\n warnings.push(\n `Large file (${formatSize(sizeBytes)}). Upload may take a while on slow connections.`,\n );\n }\n\n // Check magic bytes — only read first 4 bytes, not the entire file\n if (sizeBytes > 0) {\n let fh;\n try {\n fh = await open(filePath, \"r\");\n const buf = Buffer.alloc(4);\n await fh.read(buf, 0, 4, 0);\n\n if (!buf.equals(ZIP_MAGIC)) {\n errors.push(\n \"File does not have valid ZIP magic bytes (PK\\\\x03\\\\x04). \" +\n \"Both AAB and APK files must be valid ZIP archives.\",\n );\n }\n } catch {\n errors.push(\"Unable to read file header for validation\");\n } finally {\n await fh?.close();\n }\n }\n\n return {\n valid: errors.length === 0,\n fileType,\n sizeBytes,\n errors,\n warnings,\n };\n}\n\nfunction formatSize(bytes: number): string {\n if (bytes >= 1024 * 1024 * 1024) {\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;\n }\n if (bytes >= 1024 * 1024) {\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n }\n if (bytes >= 1024) {\n return `${(bytes / 1024).toFixed(1)} KB`;\n }\n return `${bytes} B`;\n}\n","import type { PlayApiClient, EditCommitOptions } from \"@gpc-cli/api\";\nimport { PlayApiError } from \"@gpc-cli/api\";\n\nexport interface CommitResult {\n rescued: boolean;\n}\n\nconst REVIEW_PENDING_WARNING =\n \"Changes committed but NOT sent for review (app has a rejected update).\\n\" +\n \"Next step: Open Google Play Console > Publishing overview > Send for review\";\n\nexport async function commitWithRescue(\n client: PlayApiClient,\n packageName: string,\n editId: string,\n commitOptions?: EditCommitOptions,\n): Promise<CommitResult> {\n try {\n await client.edits.commit(packageName, editId, commitOptions);\n return { rescued: false };\n } catch (error) {\n if (\n error instanceof PlayApiError &&\n error.code === \"API_CHANGES_NOT_SENT_FOR_REVIEW\" &&\n !commitOptions?.changesNotSentForReview\n ) {\n console.error(`\\n WARNING: ${REVIEW_PENDING_WARNING}\\n`);\n await client.edits.commit(packageName, editId, {\n ...commitOptions,\n changesNotSentForReview: true,\n });\n return { rescued: true };\n }\n throw error;\n }\n}\n\nexport async function validateAndCommit(\n client: PlayApiClient,\n packageName: string,\n editId: string,\n commitOptions?: EditCommitOptions,\n): Promise<CommitResult> {\n let rescuedFromValidate = false;\n if (!commitOptions?.changesNotSentForReview) {\n try {\n await client.edits.validate(packageName, editId);\n } catch (error) {\n if (error instanceof PlayApiError && error.code === \"API_CHANGES_NOT_SENT_FOR_REVIEW\") {\n console.error(`\\n WARNING: ${REVIEW_PENDING_WARNING}\\n`);\n rescuedFromValidate = true;\n } else {\n throw error;\n }\n }\n }\n\n if (rescuedFromValidate) {\n await client.edits.commit(packageName, editId, {\n ...commitOptions,\n changesNotSentForReview: true,\n });\n return { rescued: true };\n }\n return commitWithRescue(client, packageName, editId, commitOptions);\n}\n","import type { PlayApiClient, Bundle } from \"@gpc-cli/api\";\nimport { PlayApiError } from \"@gpc-cli/api\";\nimport { GpcError } from \"../errors.js\";\n\nexport interface BundlesWaitOptions {\n timeout?: number;\n interval?: number;\n}\n\nexport async function listBundles(client: PlayApiClient, packageName: string): Promise<Bundle[]> {\n const edit = await client.edits.insert(packageName);\n try {\n const bundles = await client.bundles.list(packageName, edit.id);\n await client.edits.delete(packageName, edit.id).catch(() => {});\n return bundles;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function findBundle(\n client: PlayApiClient,\n packageName: string,\n versionCode: number,\n): Promise<Bundle | null> {\n const bundles = await listBundles(client, packageName);\n return bundles.find((b) => b.versionCode === versionCode) ?? null;\n}\n\nfunction isRetryableError(error: unknown): boolean {\n if (!(error instanceof PlayApiError)) return false;\n const status = error.statusCode;\n return status === 429 || status === 500 || status === 503 || status === 409;\n}\n\nexport async function waitForBundle(\n client: PlayApiClient,\n packageName: string,\n versionCode: number,\n options?: BundlesWaitOptions,\n): Promise<Bundle> {\n const timeout = options?.timeout ?? 600_000;\n const interval = options?.interval ?? 15_000;\n const deadline = Date.now() + timeout;\n\n while (Date.now() < deadline) {\n try {\n const bundles = await listBundles(client, packageName);\n const match = bundles.find((b) => b.versionCode === versionCode);\n if (match) return match;\n } catch (error) {\n if (!isRetryableError(error)) throw error;\n }\n\n const remaining = deadline - Date.now();\n if (remaining <= 0) break;\n await new Promise((r) => setTimeout(r, Math.min(interval, remaining)));\n }\n\n throw new GpcError(\n `Bundle version code ${versionCode} not found after ${Math.round(timeout / 1000)}s`,\n \"BUNDLE_WAIT_TIMEOUT\",\n 4,\n \"The bundle may still be processing. Try again with a longer --timeout, or check the Play Console.\",\n );\n}\n"],"mappings":"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","names":["stat","extname","PlayApiError","extname","PlayApiError","PlayApiError","PlayApiError","edit","stat","extname"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -117,6 +117,9 @@ interface UploadResult {
|
|
|
117
117
|
track: string;
|
|
118
118
|
status: string;
|
|
119
119
|
validateOnly?: true;
|
|
120
|
+
reviewPending?: boolean;
|
|
121
|
+
reviewSkipped?: boolean;
|
|
122
|
+
nextStep?: string;
|
|
120
123
|
}
|
|
121
124
|
interface ReleaseStatusResult {
|
|
122
125
|
track: string;
|
|
@@ -127,6 +130,9 @@ interface ReleaseStatusResult {
|
|
|
127
130
|
language: string;
|
|
128
131
|
text: string;
|
|
129
132
|
}[];
|
|
133
|
+
reviewPending?: boolean;
|
|
134
|
+
reviewSkipped?: boolean;
|
|
135
|
+
nextStep?: string;
|
|
130
136
|
}
|
|
131
137
|
interface DryRunUploadResult {
|
|
132
138
|
dryRun: true;
|
|
@@ -146,6 +152,8 @@ interface DryRunUploadResult {
|
|
|
146
152
|
status: string;
|
|
147
153
|
userFraction?: number;
|
|
148
154
|
};
|
|
155
|
+
executed: string[];
|
|
156
|
+
skipped: string[];
|
|
149
157
|
}
|
|
150
158
|
declare function uploadRelease(client: PlayApiClient, packageName: string, filePath: string, options: {
|
|
151
159
|
track: string;
|
|
@@ -179,6 +187,20 @@ declare function promoteRelease(client: PlayApiClient, packageName: string, from
|
|
|
179
187
|
}[];
|
|
180
188
|
commitOptions?: EditCommitOptions;
|
|
181
189
|
}): Promise<ReleaseStatusResult>;
|
|
190
|
+
declare function assignRelease(client: PlayApiClient, packageName: string, versionCode: number, options: {
|
|
191
|
+
track: string;
|
|
192
|
+
status?: string;
|
|
193
|
+
userFraction?: number;
|
|
194
|
+
releaseNotes?: {
|
|
195
|
+
language: string;
|
|
196
|
+
text: string;
|
|
197
|
+
}[];
|
|
198
|
+
releaseName?: string;
|
|
199
|
+
inAppUpdatePriority?: number;
|
|
200
|
+
retainVersionCodes?: string[];
|
|
201
|
+
dryRun?: boolean;
|
|
202
|
+
commitOptions?: EditCommitOptions;
|
|
203
|
+
}): Promise<ReleaseStatusResult>;
|
|
182
204
|
declare function updateRollout(client: PlayApiClient, packageName: string, track: string, action: "increase" | "halt" | "resume" | "complete", userFraction?: number, commitOptions?: EditCommitOptions): Promise<ReleaseStatusResult>;
|
|
183
205
|
declare function listTracks(client: PlayApiClient, packageName: string): Promise<Track[]>;
|
|
184
206
|
declare function createTrack(client: PlayApiClient, packageName: string, trackName: string, commitOptions?: EditCommitOptions): Promise<Track>;
|
|
@@ -1555,8 +1577,11 @@ declare function getKeystoreFingerprint(keystorePath: string, storePassword: str
|
|
|
1555
1577
|
declare function getApiSigningFingerprint(accessToken: string, packageName: string, apiHost?: string): Promise<ApiSigningFingerprint | null>;
|
|
1556
1578
|
declare function compareFingerprints(a: string, b: string): boolean;
|
|
1557
1579
|
|
|
1558
|
-
|
|
1559
|
-
|
|
1580
|
+
interface CommitResult {
|
|
1581
|
+
rescued: boolean;
|
|
1582
|
+
}
|
|
1583
|
+
declare function commitWithRescue(client: PlayApiClient, packageName: string, editId: string, commitOptions?: EditCommitOptions): Promise<CommitResult>;
|
|
1584
|
+
declare function validateAndCommit(client: PlayApiClient, packageName: string, editId: string, commitOptions?: EditCommitOptions): Promise<CommitResult>;
|
|
1560
1585
|
|
|
1561
1586
|
declare function sha256File(filePath: string): Promise<string>;
|
|
1562
1587
|
|
|
@@ -1624,4 +1649,4 @@ interface ChecklistInput {
|
|
|
1624
1649
|
declare function buildChecklist(input: ChecklistInput): ChecklistResult;
|
|
1625
1650
|
declare function renderChecklistMarkdown(result: ChecklistResult, accountEmail: string): string;
|
|
1626
1651
|
|
|
1627
|
-
export { ApiError, type ApiSigningFingerprint, type AppInfo, type AppStatus, type ApplyReleaseNotesResult, type AuditEntry, type BatchSyncResult, type BundleAnalysis, type BundleComparison, type BundleEntry, type BundleSizeCheckResult, type BundleSizeConfig, type BundlesWaitOptions, type ChangelogEntry, type ChecklistInput, type ChecklistItem, type ChecklistResult, type CommandContext, type CommitCluster, ConfigError, type CreateEnterpriseAppParams, DEFAULT_LIMITS, DEFAULT_MODELS, DEFAULT_PREFLIGHT_CONFIG, DEFAULT_WATCH_THRESHOLDS, type DecodedNotification, type DiffToken, type DiscoverPluginsOptions, type DryRunPublishResult, type DryRunResult, type DryRunUploadResult, type ErrorReason, type ExportImagesOptions, type ExportImagesSummary, type FastlaneDetection, type FastlaneLane, type FetchChangelogOptions, type FieldLintResult, type FileValidationResult, type FindingSeverity, GOOGLE_PLAY_LANGUAGES, type GenerateOptions, type GeneratedChangelog, type GetAppStatusOptions, type GitNotesOptions, type GitReleaseNotes, type GitRunner, GpcError, type ImageSyncDetail, type ImageSyncOptions, type ImageSyncResult, type ImageValidationResult, type InitOptions, type InitResult, type InternalSharingUploadResult, type KeystoreFingerprint, type ListIapOptions, type ListSubscriptionsOptions, type ListUsersOptions, type ListVoidedOptions, type ListingDiff, type ListingFieldLimits, type ListingLintResult, type ListingsResult, type LoadedPlugin, type LocaleBundle, type LocaleEntry, type MigrationResult, NetworkError, type OneTimeProductDiff, type OutputMode, PERMISSION_PROPAGATION_WARNING, PLACEHOLDER_TEXT, PLAY_STORE_LIMIT, PROVIDER_WHITELIST, type ParsedCommit, type ParsedManifest, type ParsedMonth, type PlayStoreFormat, type PlayStoreRenderOptions, PluginManager, type PreflightConfig, type PreflightFinding, type PreflightOptions, type PreflightResult, type PreflightScanner, type Provider, type PublishOptions, type PublishResult, type PushResult, type QuotaUsage, RENDERERS, type RawCommit, type ReleaseDiff, type ReleaseNotesValidation, type ReleaseStatusResult, type Renderer, type ResolveAiConfigOptions, type ResolveLocalesOptions, type ReviewAnalysis, type ReviewExportOptions, type ReviewsFilterOptions, type RevocationRefundType, type RtdnStatus, SECTION_ORDER, SENSITIVE_ARG_KEYS, SENSITIVE_KEYS, SEVERITY_ORDER, type ScaffoldOptions, type ScaffoldResult, type SigningConsistencyResult, type SigningKeyComparison, type Spinner, type StatusDiff, type StatusRelease, type StatusReviews, type StatusVitalMetric, type SubscriptionAnalytics, type SubscriptionDiff, type SyncResult, type ThresholdResult, type TrainConfig, type TrainState, type TranslateBundleOptions, type TranslatedBundle, type TranslationFailure, type TranslationPath, type TranslationResult, type Translator, type TranslatorConfig, type UploadResult, VALID_WATCH_METRICS, type ValidateCheck, type ValidateOptions, type ValidateResult, type VersionVitalsComparison, type VersionVitalsRow, type VitalsOverview, type VitalsQueryOptions, type VitalsTrendComparison, type WatchAction, type WatchCallbacks, type WatchConfig, type WatchEvent, type WatchMetric, type WatchOptions, type WatchRollout, type WatchSummary, type WatchVitalReading, type WatchVitalsOptions, type WebhookPayload, abortTrain, acknowledgeProductPurchase, activateBasePlan, activateOffer, addRecoveryTargeting, addTesters, advanceTrain, analyzeBundle, analyzeRemoteListings, analyzeReviews, applyReleaseNotes, batchGetOrders, batchSyncInAppProducts, buildChecklist, buildLocaleBundle, bundleToReleaseNotes, cancelRecoveryAction, cancelSubscriptionPurchase, cancelSubscriptionV2, checkBundleSize, checkSigningConsistency, checkThreshold, classifyError, clearAuditLog, commitWithRescue, compareBundles, compareFingerprints, compareVersionVitals, compareVitalsTrend, computeStatusDiff, consumeProductPurchase, convertRegionPrices, createAuditEntry, createDeviceTier, createEnterpriseApp, createExternalTransaction, createGrant, createInAppProduct, createOffer, createOneTimeOffer, createOneTimeProduct, createRecoveryAction, createSpinner, createSubscription, createTrack, createTranslator, deactivateBasePlan, deactivateOffer, decodeNotification, defaultGitRunner, deferSubscriptionPurchase, deferSubscriptionV2, deleteBasePlan, deleteGrant, deleteImage, deleteInAppProduct, deleteListing, deleteOffer, deleteOneTimeOffer, deleteOneTimeProduct, deleteSubscription, deployRecoveryAction, detectFastlane, detectOutputFormat, diffListings, diffListingsCommand, diffListingsEnhanced, diffOneTimeProduct, diffReleases, diffSubscription, discoverPlugins, dispatchWebhook, downloadGeneratedApk, downloadReport, exportImages, exportReviews, fetchAggregateCost, fetchChangelog, fetchReleaseNotes, findBundle, formatChangelogEntry, formatCustomPayload, formatDiscordPayload, formatJunit, formatNotification, formatOutput, formatPathLabel, formatSlackPayload, formatStatusDiff, formatStatusSummary, formatStatusTable, formatWordDiff, generateChangelog, generateMigrationPlan, generateNotesFromGit, getAllScannerNames, getApiSigningFingerprint, getAppInfo, getAppStatus, getCountryAvailability, getDeviceTier, getExternalTransaction, getInAppProduct, getKeystoreFingerprint, getListings, getOffer, getOneTimeOffer, getOneTimeProduct, getOrderDetails, getProductPurchase, getProductPurchaseV2, getQuotaUsage, getReleasesStatus, getReview, getRtdnStatus, getSubscription, getSubscriptionAnalytics, getSubscriptionPurchase, getTrainStatus, getUser, getVitalsAnomalies, getVitalsAnr, getVitalsBattery, getVitalsCrashes, getVitalsErrorCount, getVitalsLmk, getVitalsMemory, getVitalsOverview, getVitalsRendering, getVitalsStartup, handleBreach, importDataSafety, importTestersFromCsv, initAudit, initProject, inviteUser, isFinancialReportType, isStatsReportType, isValidBcp47, isValidReportType, isValidStatsDimension, isVersionedNotesDir, lintListing, lintListings, lintLocalListings, listAchievements, listAuditEvents, listBundles, listDeviceTiers, listEvents, listGeneratedApks, listGrants, listImages, listInAppProducts, listLeaderboards, listOffers, listOneTimeOffers, listOneTimeProducts, listRecoveryActions, listReports, listReviews, listSubscriptions, listTesters, listTracks, listUsers, listVoidedPurchases, loadPreflightConfig, loadStatusCache, maybePaginate, migratePrices, normalizeFingerprint, parseAppfile, parseCommit, parseFastfile, parseGrantArg, parseKeytoolOutput, parseMonth, parseRemoteUrl, pauseTrain, promoteRelease, publish, publishEnterpriseApp, pullListings, pushListings, readListingsFromDir, readReleaseNotesForVersion, readReleaseNotesFromDir, redactAuditArgs, redactSensitive, refundExternalTransaction, refundOrder, relativeTime, removeTesters, removeUser, renderChecklistMarkdown, renderJson, renderMarkdown, renderPlayStore, renderPlayStoreJson, renderPlayStoreMd, renderPlayStorePrompt, renderPrompt, replyToReview, resolveAiConfig, resolveLocales, retryOnUploadNotComplete, revokeSubscriptionPurchase, runPreflight, runWatch, runWatchLoop, safePath, safePathWithin, saveStatusCache, scaffoldPlugin, searchAuditEvents, searchVitalsErrors, sendNotification, sendWebhook, sha256File, sortResults, startTrain, statusHasBreach, syncImages, syncInAppProducts, topFiles, trackBreachState, translateBundle, updateAppDetails, updateDataSafety, updateGrant, updateInAppProduct, updateListing, updateOffer, updateOneTimeOffer, updateOneTimeProduct, updateRollout, updateSubscription, updateTrackConfig, updateUser, uploadExternallyHosted, uploadImage, uploadInternalSharing, uploadRelease, validateAndCommit, validateBundleForApply, validateImage, validateLanguageCode, validatePackageName, validatePreSubmission, validateReleaseNotes, validateSku, validateTrackName, validateUploadFile, validateVersionCode, waitForBundle, waitForBundleProcessing, watchVitalsWithAutoHalt, wordDiff, writeAuditLog, writeListingsToDir, writeMigrationOutput };
|
|
1652
|
+
export { ApiError, type ApiSigningFingerprint, type AppInfo, type AppStatus, type ApplyReleaseNotesResult, type AuditEntry, type BatchSyncResult, type BundleAnalysis, type BundleComparison, type BundleEntry, type BundleSizeCheckResult, type BundleSizeConfig, type BundlesWaitOptions, type ChangelogEntry, type ChecklistInput, type ChecklistItem, type ChecklistResult, type CommandContext, type CommitCluster, type CommitResult, ConfigError, type CreateEnterpriseAppParams, DEFAULT_LIMITS, DEFAULT_MODELS, DEFAULT_PREFLIGHT_CONFIG, DEFAULT_WATCH_THRESHOLDS, type DecodedNotification, type DiffToken, type DiscoverPluginsOptions, type DryRunPublishResult, type DryRunResult, type DryRunUploadResult, type ErrorReason, type ExportImagesOptions, type ExportImagesSummary, type FastlaneDetection, type FastlaneLane, type FetchChangelogOptions, type FieldLintResult, type FileValidationResult, type FindingSeverity, GOOGLE_PLAY_LANGUAGES, type GenerateOptions, type GeneratedChangelog, type GetAppStatusOptions, type GitNotesOptions, type GitReleaseNotes, type GitRunner, GpcError, type ImageSyncDetail, type ImageSyncOptions, type ImageSyncResult, type ImageValidationResult, type InitOptions, type InitResult, type InternalSharingUploadResult, type KeystoreFingerprint, type ListIapOptions, type ListSubscriptionsOptions, type ListUsersOptions, type ListVoidedOptions, type ListingDiff, type ListingFieldLimits, type ListingLintResult, type ListingsResult, type LoadedPlugin, type LocaleBundle, type LocaleEntry, type MigrationResult, NetworkError, type OneTimeProductDiff, type OutputMode, PERMISSION_PROPAGATION_WARNING, PLACEHOLDER_TEXT, PLAY_STORE_LIMIT, PROVIDER_WHITELIST, type ParsedCommit, type ParsedManifest, type ParsedMonth, type PlayStoreFormat, type PlayStoreRenderOptions, PluginManager, type PreflightConfig, type PreflightFinding, type PreflightOptions, type PreflightResult, type PreflightScanner, type Provider, type PublishOptions, type PublishResult, type PushResult, type QuotaUsage, RENDERERS, type RawCommit, type ReleaseDiff, type ReleaseNotesValidation, type ReleaseStatusResult, type Renderer, type ResolveAiConfigOptions, type ResolveLocalesOptions, type ReviewAnalysis, type ReviewExportOptions, type ReviewsFilterOptions, type RevocationRefundType, type RtdnStatus, SECTION_ORDER, SENSITIVE_ARG_KEYS, SENSITIVE_KEYS, SEVERITY_ORDER, type ScaffoldOptions, type ScaffoldResult, type SigningConsistencyResult, type SigningKeyComparison, type Spinner, type StatusDiff, type StatusRelease, type StatusReviews, type StatusVitalMetric, type SubscriptionAnalytics, type SubscriptionDiff, type SyncResult, type ThresholdResult, type TrainConfig, type TrainState, type TranslateBundleOptions, type TranslatedBundle, type TranslationFailure, type TranslationPath, type TranslationResult, type Translator, type TranslatorConfig, type UploadResult, VALID_WATCH_METRICS, type ValidateCheck, type ValidateOptions, type ValidateResult, type VersionVitalsComparison, type VersionVitalsRow, type VitalsOverview, type VitalsQueryOptions, type VitalsTrendComparison, type WatchAction, type WatchCallbacks, type WatchConfig, type WatchEvent, type WatchMetric, type WatchOptions, type WatchRollout, type WatchSummary, type WatchVitalReading, type WatchVitalsOptions, type WebhookPayload, abortTrain, acknowledgeProductPurchase, activateBasePlan, activateOffer, addRecoveryTargeting, addTesters, advanceTrain, analyzeBundle, analyzeRemoteListings, analyzeReviews, applyReleaseNotes, assignRelease, batchGetOrders, batchSyncInAppProducts, buildChecklist, buildLocaleBundle, bundleToReleaseNotes, cancelRecoveryAction, cancelSubscriptionPurchase, cancelSubscriptionV2, checkBundleSize, checkSigningConsistency, checkThreshold, classifyError, clearAuditLog, commitWithRescue, compareBundles, compareFingerprints, compareVersionVitals, compareVitalsTrend, computeStatusDiff, consumeProductPurchase, convertRegionPrices, createAuditEntry, createDeviceTier, createEnterpriseApp, createExternalTransaction, createGrant, createInAppProduct, createOffer, createOneTimeOffer, createOneTimeProduct, createRecoveryAction, createSpinner, createSubscription, createTrack, createTranslator, deactivateBasePlan, deactivateOffer, decodeNotification, defaultGitRunner, deferSubscriptionPurchase, deferSubscriptionV2, deleteBasePlan, deleteGrant, deleteImage, deleteInAppProduct, deleteListing, deleteOffer, deleteOneTimeOffer, deleteOneTimeProduct, deleteSubscription, deployRecoveryAction, detectFastlane, detectOutputFormat, diffListings, diffListingsCommand, diffListingsEnhanced, diffOneTimeProduct, diffReleases, diffSubscription, discoverPlugins, dispatchWebhook, downloadGeneratedApk, downloadReport, exportImages, exportReviews, fetchAggregateCost, fetchChangelog, fetchReleaseNotes, findBundle, formatChangelogEntry, formatCustomPayload, formatDiscordPayload, formatJunit, formatNotification, formatOutput, formatPathLabel, formatSlackPayload, formatStatusDiff, formatStatusSummary, formatStatusTable, formatWordDiff, generateChangelog, generateMigrationPlan, generateNotesFromGit, getAllScannerNames, getApiSigningFingerprint, getAppInfo, getAppStatus, getCountryAvailability, getDeviceTier, getExternalTransaction, getInAppProduct, getKeystoreFingerprint, getListings, getOffer, getOneTimeOffer, getOneTimeProduct, getOrderDetails, getProductPurchase, getProductPurchaseV2, getQuotaUsage, getReleasesStatus, getReview, getRtdnStatus, getSubscription, getSubscriptionAnalytics, getSubscriptionPurchase, getTrainStatus, getUser, getVitalsAnomalies, getVitalsAnr, getVitalsBattery, getVitalsCrashes, getVitalsErrorCount, getVitalsLmk, getVitalsMemory, getVitalsOverview, getVitalsRendering, getVitalsStartup, handleBreach, importDataSafety, importTestersFromCsv, initAudit, initProject, inviteUser, isFinancialReportType, isStatsReportType, isValidBcp47, isValidReportType, isValidStatsDimension, isVersionedNotesDir, lintListing, lintListings, lintLocalListings, listAchievements, listAuditEvents, listBundles, listDeviceTiers, listEvents, listGeneratedApks, listGrants, listImages, listInAppProducts, listLeaderboards, listOffers, listOneTimeOffers, listOneTimeProducts, listRecoveryActions, listReports, listReviews, listSubscriptions, listTesters, listTracks, listUsers, listVoidedPurchases, loadPreflightConfig, loadStatusCache, maybePaginate, migratePrices, normalizeFingerprint, parseAppfile, parseCommit, parseFastfile, parseGrantArg, parseKeytoolOutput, parseMonth, parseRemoteUrl, pauseTrain, promoteRelease, publish, publishEnterpriseApp, pullListings, pushListings, readListingsFromDir, readReleaseNotesForVersion, readReleaseNotesFromDir, redactAuditArgs, redactSensitive, refundExternalTransaction, refundOrder, relativeTime, removeTesters, removeUser, renderChecklistMarkdown, renderJson, renderMarkdown, renderPlayStore, renderPlayStoreJson, renderPlayStoreMd, renderPlayStorePrompt, renderPrompt, replyToReview, resolveAiConfig, resolveLocales, retryOnUploadNotComplete, revokeSubscriptionPurchase, runPreflight, runWatch, runWatchLoop, safePath, safePathWithin, saveStatusCache, scaffoldPlugin, searchAuditEvents, searchVitalsErrors, sendNotification, sendWebhook, sha256File, sortResults, startTrain, statusHasBreach, syncImages, syncInAppProducts, topFiles, trackBreachState, translateBundle, updateAppDetails, updateDataSafety, updateGrant, updateInAppProduct, updateListing, updateOffer, updateOneTimeOffer, updateOneTimeProduct, updateRollout, updateSubscription, updateTrackConfig, updateUser, uploadExternallyHosted, uploadImage, uploadInternalSharing, uploadRelease, validateAndCommit, validateBundleForApply, validateImage, validateLanguageCode, validatePackageName, validatePreSubmission, validateReleaseNotes, validateSku, validateTrackName, validateUploadFile, validateVersionCode, waitForBundle, waitForBundleProcessing, watchVitalsWithAutoHalt, wordDiff, writeAuditLog, writeListingsToDir, writeMigrationOutput };
|