@mpgd/cli 0.62.0 → 0.64.0
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/app-store-iap-asc.d.ts +14 -0
- package/dist/app-store-iap-asc.js +219 -0
- package/dist/app-store-iap-port.d.ts +125 -0
- package/dist/app-store-iap-port.js +14 -0
- package/dist/app-store-presence-asc.d.ts +23 -0
- package/dist/app-store-presence-asc.js +162 -0
- package/dist/app-store-presence-port.d.ts +101 -0
- package/dist/app-store-presence-port.js +13 -0
- package/dist/asc-session.d.ts +43 -0
- package/dist/asc-session.js +118 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +169 -1
- package/dist/native-build-info.json +2 -2
- package/dist/native-build-target.js +411 -339
- package/dist/play-listing-port.d.ts +88 -0
- package/dist/play-listing-port.js +12 -0
- package/dist/play-product-port.d.ts +101 -0
- package/dist/play-product-port.js +7 -0
- package/dist/play-sdk-adapter.js +168 -15
- package/dist/store-presence-app-store.d.ts +139 -0
- package/dist/store-presence-app-store.js +537 -0
- package/dist/store-presence-command.d.ts +50 -0
- package/dist/store-presence-command.js +145 -0
- package/dist/store-presence-play.d.ts +116 -0
- package/dist/store-presence-play.js +495 -0
- package/dist/store-presence.d.ts +106 -0
- package/dist/store-presence.js +173 -0
- package/dist/store-products-app-store.d.ts +144 -0
- package/dist/store-products-app-store.js +632 -0
- package/dist/store-products-command.d.ts +51 -0
- package/dist/store-products-command.js +155 -0
- package/dist/store-products-play.d.ts +112 -0
- package/dist/store-products-play.js +622 -0
- package/dist/store-products.d.ts +38 -0
- package/dist/store-products.js +56 -0
- package/dist/store-sync.d.ts +50 -0
- package/dist/store-sync.js +137 -0
- package/dist/testflight-submission.d.ts +1 -1
- package/package.json +13 -13
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Play store listing port (Android Publisher v3 edits: `edits.details`,
|
|
3
|
+
* `edits.listings`, `edits.images`). The Google SDK lives under
|
|
4
|
+
* adapters/play-publisher; the CLI sees only these shapes.
|
|
5
|
+
*
|
|
6
|
+
* - Edits: https://developers.google.com/android-publisher/edits
|
|
7
|
+
* - Listings: https://developers.google.com/android-publisher/api-ref/rest/v3/edits.listings
|
|
8
|
+
* - Images: https://developers.google.com/android-publisher/api-ref/rest/v3/edits.images
|
|
9
|
+
* - Details: https://developers.google.com/android-publisher/api-ref/rest/v3/edits.details
|
|
10
|
+
* - Commit: https://developers.google.com/android-publisher/api-ref/rest/v3/edits/commit
|
|
11
|
+
*/
|
|
12
|
+
/** `AppDetails`. */
|
|
13
|
+
export interface PlayAppDetails {
|
|
14
|
+
readonly defaultLanguage?: string | null | undefined;
|
|
15
|
+
readonly contactEmail?: string | null | undefined;
|
|
16
|
+
readonly contactPhone?: string | null | undefined;
|
|
17
|
+
readonly contactWebsite?: string | null | undefined;
|
|
18
|
+
}
|
|
19
|
+
/** `Listing`: one language of the main store listing. */
|
|
20
|
+
export interface PlayListing {
|
|
21
|
+
readonly language?: string | null | undefined;
|
|
22
|
+
readonly title?: string | null | undefined;
|
|
23
|
+
readonly shortDescription?: string | null | undefined;
|
|
24
|
+
readonly fullDescription?: string | null | undefined;
|
|
25
|
+
/** YouTube URL of the promo video. */
|
|
26
|
+
readonly video?: string | null | undefined;
|
|
27
|
+
}
|
|
28
|
+
/** `Image`. */
|
|
29
|
+
export interface PlayImage {
|
|
30
|
+
readonly id?: string | null | undefined;
|
|
31
|
+
readonly url?: string | null | undefined;
|
|
32
|
+
readonly sha1?: string | null | undefined;
|
|
33
|
+
readonly sha256?: string | null | undefined;
|
|
34
|
+
}
|
|
35
|
+
/** The `AppImageType` values the store presence sync manages. */
|
|
36
|
+
export type PlayImageType = 'icon' | 'featureGraphic' | 'phoneScreenshots' | 'sevenInchScreenshots' | 'tenInchScreenshots';
|
|
37
|
+
export interface PlayImageTarget {
|
|
38
|
+
readonly packageName: string;
|
|
39
|
+
readonly editId: string;
|
|
40
|
+
readonly language: string;
|
|
41
|
+
readonly imageType: PlayImageType;
|
|
42
|
+
readonly signal?: AbortSignal;
|
|
43
|
+
}
|
|
44
|
+
export interface PlayStoreListingPort {
|
|
45
|
+
/** `edits.insert`. A new edit invalidates the caller's other open edit. */
|
|
46
|
+
insertEdit(packageName: string, signal?: AbortSignal): Promise<string>;
|
|
47
|
+
/** `edits.delete`: discards an uncommitted edit. */
|
|
48
|
+
deleteEdit(packageName: string, editId: string, signal?: AbortSignal): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* `edits.commit` with `changesInReviewBehavior=ERROR_IF_IN_REVIEW`, and
|
|
51
|
+
* `changesNotSentForReview` when asked.
|
|
52
|
+
*/
|
|
53
|
+
commitEdit(input: {
|
|
54
|
+
readonly packageName: string;
|
|
55
|
+
readonly editId: string;
|
|
56
|
+
readonly changesNotSentForReview: boolean;
|
|
57
|
+
readonly signal?: AbortSignal;
|
|
58
|
+
}): Promise<void>;
|
|
59
|
+
getDetails(packageName: string, editId: string, signal?: AbortSignal): Promise<PlayAppDetails>;
|
|
60
|
+
/** `edits.details.update` with the whole object. */
|
|
61
|
+
updateDetails(input: {
|
|
62
|
+
readonly packageName: string;
|
|
63
|
+
readonly editId: string;
|
|
64
|
+
readonly details: PlayAppDetails;
|
|
65
|
+
readonly signal?: AbortSignal;
|
|
66
|
+
}): Promise<void>;
|
|
67
|
+
/** `edits.listings.list`: every language. */
|
|
68
|
+
listListings(packageName: string, editId: string, signal?: AbortSignal): Promise<PlayListing[]>;
|
|
69
|
+
/** `edits.listings.update`: creates or replaces one language. */
|
|
70
|
+
updateListing(input: {
|
|
71
|
+
readonly packageName: string;
|
|
72
|
+
readonly editId: string;
|
|
73
|
+
readonly listing: PlayListing & {
|
|
74
|
+
readonly language: string;
|
|
75
|
+
};
|
|
76
|
+
readonly signal?: AbortSignal;
|
|
77
|
+
}): Promise<void>;
|
|
78
|
+
/** `edits.images.list`, in the order Play shows them. */
|
|
79
|
+
listImages(input: PlayImageTarget): Promise<PlayImage[]>;
|
|
80
|
+
/** `edits.images.upload`: adds one image after the ones the slot holds. */
|
|
81
|
+
uploadImage(input: PlayImageTarget & {
|
|
82
|
+
readonly file: string;
|
|
83
|
+
readonly mimeType: 'image/png' | 'image/jpeg';
|
|
84
|
+
readonly timeoutMs: number;
|
|
85
|
+
}): Promise<PlayImage>;
|
|
86
|
+
/** `edits.images.deleteall`. */
|
|
87
|
+
deleteAllImages(input: PlayImageTarget): Promise<void>;
|
|
88
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Play store listing port (Android Publisher v3 edits: `edits.details`,
|
|
3
|
+
* `edits.listings`, `edits.images`). The Google SDK lives under
|
|
4
|
+
* adapters/play-publisher; the CLI sees only these shapes.
|
|
5
|
+
*
|
|
6
|
+
* - Edits: https://developers.google.com/android-publisher/edits
|
|
7
|
+
* - Listings: https://developers.google.com/android-publisher/api-ref/rest/v3/edits.listings
|
|
8
|
+
* - Images: https://developers.google.com/android-publisher/api-ref/rest/v3/edits.images
|
|
9
|
+
* - Details: https://developers.google.com/android-publisher/api-ref/rest/v3/edits.details
|
|
10
|
+
* - Commit: https://developers.google.com/android-publisher/api-ref/rest/v3/edits/commit
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Play one-time product port (Android Publisher v3
|
|
3
|
+
* `monetization.onetimeproducts`). The Google SDK lives under
|
|
4
|
+
* adapters/play-publisher; the CLI sees only these shapes.
|
|
5
|
+
* https://developers.google.com/android-publisher/api-ref/rest/v3/monetization.onetimeproducts
|
|
6
|
+
*/
|
|
7
|
+
/** `Money`: whole `units` as a decimal string and `nanos` (10^-9 units). */
|
|
8
|
+
export interface PlayMoney {
|
|
9
|
+
readonly currencyCode?: string | null | undefined;
|
|
10
|
+
readonly units?: string | null | undefined;
|
|
11
|
+
readonly nanos?: number | null | undefined;
|
|
12
|
+
}
|
|
13
|
+
export interface PlayOneTimeProductListing {
|
|
14
|
+
readonly languageCode?: string | null | undefined;
|
|
15
|
+
readonly title?: string | null | undefined;
|
|
16
|
+
readonly description?: string | null | undefined;
|
|
17
|
+
}
|
|
18
|
+
export interface PlayRegionalPricingConfig {
|
|
19
|
+
readonly regionCode?: string | null | undefined;
|
|
20
|
+
readonly price?: PlayMoney | undefined;
|
|
21
|
+
/** `AVAILABLE`, `NO_LONGER_AVAILABLE`, `AVAILABLE_IF_RELEASED`, `AVAILABLE_FOR_OFFERS_ONLY`. */
|
|
22
|
+
readonly availability?: string | null | undefined;
|
|
23
|
+
}
|
|
24
|
+
export interface PlayNewRegionsConfig {
|
|
25
|
+
readonly usdPrice?: PlayMoney | undefined;
|
|
26
|
+
readonly eurPrice?: PlayMoney | undefined;
|
|
27
|
+
readonly availability?: string | null | undefined;
|
|
28
|
+
}
|
|
29
|
+
export interface PlayPurchaseOption {
|
|
30
|
+
readonly purchaseOptionId?: string | null | undefined;
|
|
31
|
+
readonly buyOption?: {
|
|
32
|
+
readonly legacyCompatible?: boolean | null | undefined;
|
|
33
|
+
readonly multiQuantityEnabled?: boolean | null | undefined;
|
|
34
|
+
} | undefined;
|
|
35
|
+
/** A rent option; the sync never manages one. */
|
|
36
|
+
readonly rentOption?: Readonly<Record<string, unknown>> | undefined;
|
|
37
|
+
readonly regionalPricingAndAvailabilityConfigs?: readonly PlayRegionalPricingConfig[] | undefined;
|
|
38
|
+
readonly newRegionsConfig?: PlayNewRegionsConfig | undefined;
|
|
39
|
+
/** Output only: `DRAFT`, `ACTIVE`, `INACTIVE` or `INACTIVE_PUBLISHED`. */
|
|
40
|
+
readonly state?: string | null | undefined;
|
|
41
|
+
readonly [field: string]: unknown;
|
|
42
|
+
}
|
|
43
|
+
export interface PlayOneTimeProduct {
|
|
44
|
+
readonly packageName?: string | null | undefined;
|
|
45
|
+
readonly productId?: string | null | undefined;
|
|
46
|
+
readonly listings?: readonly PlayOneTimeProductListing[] | undefined;
|
|
47
|
+
readonly purchaseOptions?: readonly PlayPurchaseOption[] | undefined;
|
|
48
|
+
readonly taxAndComplianceSettings?: {
|
|
49
|
+
readonly productTaxCategoryCode?: string | null | undefined;
|
|
50
|
+
readonly [field: string]: unknown;
|
|
51
|
+
} | undefined;
|
|
52
|
+
/** Output only. */
|
|
53
|
+
readonly regionsVersion?: {
|
|
54
|
+
readonly version?: string | null | undefined;
|
|
55
|
+
} | undefined;
|
|
56
|
+
readonly [field: string]: unknown;
|
|
57
|
+
}
|
|
58
|
+
/** `monetization.convertRegionPrices` response. */
|
|
59
|
+
export interface PlayConvertedPrices {
|
|
60
|
+
readonly convertedRegionPrices?: Readonly<Record<string, {
|
|
61
|
+
readonly regionCode?: string | null | undefined;
|
|
62
|
+
/** Tax inclusive. */
|
|
63
|
+
readonly price?: PlayMoney | undefined;
|
|
64
|
+
readonly taxAmount?: PlayMoney | undefined;
|
|
65
|
+
}>> | null | undefined;
|
|
66
|
+
readonly convertedOtherRegionsPrice?: {
|
|
67
|
+
readonly usdPrice?: PlayMoney | undefined;
|
|
68
|
+
readonly eurPrice?: PlayMoney | undefined;
|
|
69
|
+
} | undefined;
|
|
70
|
+
readonly regionVersion?: {
|
|
71
|
+
readonly version?: string | null | undefined;
|
|
72
|
+
} | undefined;
|
|
73
|
+
}
|
|
74
|
+
export interface PlayProductCatalogPort {
|
|
75
|
+
/** Every one-time product of the app (`onetimeproducts.list`, all pages). */
|
|
76
|
+
listOneTimeProducts(packageName: string, signal?: AbortSignal): Promise<PlayOneTimeProduct[]>;
|
|
77
|
+
/** Read-only price conversion (`monetization.convertRegionPrices`). */
|
|
78
|
+
convertRegionPrices(input: {
|
|
79
|
+
readonly packageName: string;
|
|
80
|
+
readonly price: PlayMoney;
|
|
81
|
+
readonly productTaxCategoryCode?: string;
|
|
82
|
+
readonly signal?: AbortSignal;
|
|
83
|
+
}): Promise<PlayConvertedPrices>;
|
|
84
|
+
/** `onetimeproducts.patch`; `allowMissing` creates the product. */
|
|
85
|
+
patchOneTimeProduct(input: {
|
|
86
|
+
readonly packageName: string;
|
|
87
|
+
readonly productId: string;
|
|
88
|
+
readonly product: PlayOneTimeProduct;
|
|
89
|
+
readonly updateMask: string;
|
|
90
|
+
readonly allowMissing: boolean;
|
|
91
|
+
readonly regionsVersion: string;
|
|
92
|
+
readonly signal?: AbortSignal;
|
|
93
|
+
}): Promise<PlayOneTimeProduct>;
|
|
94
|
+
/** `onetimeproducts.purchaseOptions.batchUpdateStates` with one activate request. */
|
|
95
|
+
activatePurchaseOption(input: {
|
|
96
|
+
readonly packageName: string;
|
|
97
|
+
readonly productId: string;
|
|
98
|
+
readonly purchaseOptionId: string;
|
|
99
|
+
readonly signal?: AbortSignal;
|
|
100
|
+
}): Promise<void>;
|
|
101
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Play one-time product port (Android Publisher v3
|
|
3
|
+
* `monetization.onetimeproducts`). The Google SDK lives under
|
|
4
|
+
* adapters/play-publisher; the CLI sees only these shapes.
|
|
5
|
+
* https://developers.google.com/android-publisher/api-ref/rest/v3/monetization.onetimeproducts
|
|
6
|
+
*/
|
|
7
|
+
export {};
|
package/dist/play-sdk-adapter.js
CHANGED
|
@@ -11,18 +11,22 @@ function createMockPublisher(accessToken, rootUrl) {
|
|
|
11
11
|
oauth.setCredentials({ access_token: accessToken });
|
|
12
12
|
return wrapPublisher(androidpublisher({ version: "v3", auth: oauth, rootUrl }));
|
|
13
13
|
}
|
|
14
|
+
async function insertEdit(publisher, packageName, signal) {
|
|
15
|
+
const response = await publisher.edits.insert(
|
|
16
|
+
{ packageName },
|
|
17
|
+
{ retry: false, signal }
|
|
18
|
+
);
|
|
19
|
+
if (typeof response.data.id !== "string" || response.data.id.trim() === "") {
|
|
20
|
+
throw new Error("Google Play did not return an edit ID.");
|
|
21
|
+
}
|
|
22
|
+
return response.data.id;
|
|
23
|
+
}
|
|
24
|
+
async function deleteEdit(publisher, packageName, editId, signal) {
|
|
25
|
+
await publisher.edits.delete({ packageName, editId }, { retry: false, signal });
|
|
26
|
+
}
|
|
14
27
|
function wrapPublisher(publisher) {
|
|
15
28
|
return {
|
|
16
|
-
|
|
17
|
-
const response = await publisher.edits.insert(
|
|
18
|
-
{ packageName },
|
|
19
|
-
{ retry: false, signal }
|
|
20
|
-
);
|
|
21
|
-
if (typeof response.data.id !== "string" || response.data.id.trim() === "") {
|
|
22
|
-
throw new Error("Google Play did not return an edit ID.");
|
|
23
|
-
}
|
|
24
|
-
return response.data.id;
|
|
25
|
-
},
|
|
29
|
+
insertEdit: (packageName, signal) => insertEdit(publisher, packageName, signal),
|
|
26
30
|
async listBundles(packageName, editId, signal) {
|
|
27
31
|
return (await publisher.edits.bundles.list(
|
|
28
32
|
{ packageName, editId },
|
|
@@ -87,15 +91,164 @@ function wrapPublisher(publisher) {
|
|
|
87
91
|
changesInReviewBehavior: "ERROR_IF_IN_REVIEW"
|
|
88
92
|
}, { retry: false, signal });
|
|
89
93
|
},
|
|
90
|
-
|
|
91
|
-
|
|
94
|
+
deleteEdit: (packageName, editId, signal) => deleteEdit(publisher, packageName, editId, signal)
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function createServiceAccountProductCatalog(serviceAccountFile) {
|
|
98
|
+
const googleAuth = new auth.GoogleAuth({ keyFile: serviceAccountFile, scopes: [scope] });
|
|
99
|
+
return wrapProductCatalog(androidpublisher({ version: "v3", auth: googleAuth }));
|
|
100
|
+
}
|
|
101
|
+
function createMockProductCatalog(accessToken, rootUrl) {
|
|
102
|
+
const oauth = new auth.OAuth2();
|
|
103
|
+
oauth.setCredentials({ access_token: accessToken });
|
|
104
|
+
return wrapProductCatalog(androidpublisher({ version: "v3", auth: oauth, rootUrl }));
|
|
105
|
+
}
|
|
106
|
+
function wrapProductCatalog(publisher) {
|
|
107
|
+
const products = publisher.monetization.onetimeproducts;
|
|
108
|
+
return {
|
|
109
|
+
async listOneTimeProducts(packageName, signal) {
|
|
110
|
+
const all = [];
|
|
111
|
+
let pageToken;
|
|
112
|
+
do {
|
|
113
|
+
const response = await products.list({
|
|
114
|
+
packageName,
|
|
115
|
+
pageSize: 1e3,
|
|
116
|
+
...pageToken === void 0 ? {} : { pageToken }
|
|
117
|
+
}, { signal });
|
|
118
|
+
all.push(...response.data.oneTimeProducts ?? []);
|
|
119
|
+
const next = response.data.nextPageToken;
|
|
120
|
+
pageToken = typeof next === "string" && next !== "" ? next : void 0;
|
|
121
|
+
} while (pageToken !== void 0);
|
|
122
|
+
return all;
|
|
123
|
+
},
|
|
124
|
+
async convertRegionPrices(input) {
|
|
125
|
+
const response = await publisher.monetization.convertRegionPrices({
|
|
126
|
+
packageName: input.packageName,
|
|
127
|
+
requestBody: {
|
|
128
|
+
price: input.price,
|
|
129
|
+
...input.productTaxCategoryCode === void 0 ? {} : { productTaxCategoryCode: input.productTaxCategoryCode }
|
|
130
|
+
}
|
|
131
|
+
}, { signal: input.signal });
|
|
132
|
+
return response.data;
|
|
133
|
+
},
|
|
134
|
+
async patchOneTimeProduct(input) {
|
|
135
|
+
const response = await products.patch({
|
|
136
|
+
packageName: input.packageName,
|
|
137
|
+
productId: input.productId,
|
|
138
|
+
allowMissing: input.allowMissing,
|
|
139
|
+
updateMask: input.updateMask,
|
|
140
|
+
"regionsVersion.version": input.regionsVersion,
|
|
141
|
+
requestBody: input.product
|
|
142
|
+
}, { retry: false, signal: input.signal });
|
|
143
|
+
return response.data;
|
|
144
|
+
},
|
|
145
|
+
async activatePurchaseOption(input) {
|
|
146
|
+
await products.purchaseOptions.batchUpdateStates({
|
|
147
|
+
packageName: input.packageName,
|
|
148
|
+
productId: input.productId,
|
|
149
|
+
requestBody: {
|
|
150
|
+
requests: [{
|
|
151
|
+
activatePurchaseOptionRequest: {
|
|
152
|
+
packageName: input.packageName,
|
|
153
|
+
productId: input.productId,
|
|
154
|
+
purchaseOptionId: input.purchaseOptionId
|
|
155
|
+
}
|
|
156
|
+
}]
|
|
157
|
+
}
|
|
158
|
+
}, { retry: false, signal: input.signal });
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function createServiceAccountStoreListing(serviceAccountFile) {
|
|
163
|
+
const googleAuth = new auth.GoogleAuth({ keyFile: serviceAccountFile, scopes: [scope] });
|
|
164
|
+
return wrapStoreListing(androidpublisher({ version: "v3", auth: googleAuth }));
|
|
165
|
+
}
|
|
166
|
+
function createMockStoreListing(accessToken, rootUrl, requestRootUrl) {
|
|
167
|
+
const oauth = new auth.OAuth2();
|
|
168
|
+
oauth.setCredentials({ access_token: accessToken });
|
|
169
|
+
return wrapStoreListing(
|
|
170
|
+
androidpublisher({ version: "v3", auth: oauth, rootUrl }),
|
|
171
|
+
requestRootUrl
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
function wrapStoreListing(publisher, requestRootUrl) {
|
|
175
|
+
const mediaRoot = requestRootUrl === void 0 ? {} : { rootUrl: requestRootUrl };
|
|
176
|
+
return {
|
|
177
|
+
insertEdit: (packageName, signal) => insertEdit(publisher, packageName, signal),
|
|
178
|
+
deleteEdit: (packageName, editId, signal) => deleteEdit(publisher, packageName, editId, signal),
|
|
179
|
+
async commitEdit(input) {
|
|
180
|
+
await publisher.edits.commit({
|
|
181
|
+
packageName: input.packageName,
|
|
182
|
+
editId: input.editId,
|
|
183
|
+
changesInReviewBehavior: "ERROR_IF_IN_REVIEW",
|
|
184
|
+
...input.changesNotSentForReview ? { changesNotSentForReview: true } : {}
|
|
185
|
+
}, { retry: false, signal: input.signal });
|
|
186
|
+
},
|
|
187
|
+
async getDetails(packageName, editId, signal) {
|
|
188
|
+
return (await publisher.edits.details.get(
|
|
92
189
|
{ packageName, editId },
|
|
93
|
-
{
|
|
94
|
-
);
|
|
190
|
+
{ signal }
|
|
191
|
+
)).data;
|
|
192
|
+
},
|
|
193
|
+
async updateDetails(input) {
|
|
194
|
+
await publisher.edits.details.update({
|
|
195
|
+
packageName: input.packageName,
|
|
196
|
+
editId: input.editId,
|
|
197
|
+
requestBody: input.details
|
|
198
|
+
}, { retry: false, signal: input.signal });
|
|
199
|
+
},
|
|
200
|
+
async listListings(packageName, editId, signal) {
|
|
201
|
+
return (await publisher.edits.listings.list(
|
|
202
|
+
{ packageName, editId },
|
|
203
|
+
{ signal }
|
|
204
|
+
)).data.listings ?? [];
|
|
205
|
+
},
|
|
206
|
+
async updateListing(input) {
|
|
207
|
+
await publisher.edits.listings.update({
|
|
208
|
+
packageName: input.packageName,
|
|
209
|
+
editId: input.editId,
|
|
210
|
+
language: input.listing.language,
|
|
211
|
+
requestBody: input.listing
|
|
212
|
+
}, { retry: false, signal: input.signal });
|
|
213
|
+
},
|
|
214
|
+
async listImages(input) {
|
|
215
|
+
return (await publisher.edits.images.list({
|
|
216
|
+
packageName: input.packageName,
|
|
217
|
+
editId: input.editId,
|
|
218
|
+
language: input.language,
|
|
219
|
+
imageType: input.imageType
|
|
220
|
+
}, { signal: input.signal })).data.images ?? [];
|
|
221
|
+
},
|
|
222
|
+
async uploadImage(input) {
|
|
223
|
+
const response = await publisher.edits.images.upload({
|
|
224
|
+
packageName: input.packageName,
|
|
225
|
+
editId: input.editId,
|
|
226
|
+
language: input.language,
|
|
227
|
+
imageType: input.imageType,
|
|
228
|
+
media: { mimeType: input.mimeType, body: createReadStream(input.file) }
|
|
229
|
+
}, {
|
|
230
|
+
timeout: input.timeoutMs,
|
|
231
|
+
retry: false,
|
|
232
|
+
signal: input.signal,
|
|
233
|
+
...mediaRoot
|
|
234
|
+
});
|
|
235
|
+
return response.data.image ?? {};
|
|
236
|
+
},
|
|
237
|
+
async deleteAllImages(input) {
|
|
238
|
+
await publisher.edits.images.deleteall({
|
|
239
|
+
packageName: input.packageName,
|
|
240
|
+
editId: input.editId,
|
|
241
|
+
language: input.language,
|
|
242
|
+
imageType: input.imageType
|
|
243
|
+
}, { retry: false, signal: input.signal });
|
|
95
244
|
}
|
|
96
245
|
};
|
|
97
246
|
}
|
|
98
247
|
export {
|
|
248
|
+
createMockProductCatalog,
|
|
99
249
|
createMockPublisher,
|
|
100
|
-
|
|
250
|
+
createMockStoreListing,
|
|
251
|
+
createServiceAccountProductCatalog,
|
|
252
|
+
createServiceAccountPublisher,
|
|
253
|
+
createServiceAccountStoreListing
|
|
101
254
|
};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { type AppStorePresenceDisplay, type StorePresence } from '@mpgd/catalog';
|
|
2
|
+
import type { AscStorePresencePort, AscVersionLocalizationFields } from './app-store-presence-port.js';
|
|
3
|
+
import { type StorePresenceTextChange } from './store-presence.js';
|
|
4
|
+
/**
|
|
5
|
+
* App Store Connect store page sync: compare the store presence file with
|
|
6
|
+
* the app information localizations (name, subtitle, privacy policy URL),
|
|
7
|
+
* the editable App Store version's localizations (description, keywords,
|
|
8
|
+
* promotional text, what's new, URLs) and its screenshot sets, and plan the
|
|
9
|
+
* writes.
|
|
10
|
+
*
|
|
11
|
+
* The sync writes only to an editable app information record and an
|
|
12
|
+
* editable iOS version (`PREPARE_FOR_SUBMISSION` or rejected). It never
|
|
13
|
+
* creates a version, never submits for review, and never touches pricing,
|
|
14
|
+
* availability or release settings. App Store Connect has no transaction:
|
|
15
|
+
* apply writes step by step and, on a failure, names the steps already
|
|
16
|
+
* written; `plan` then shows what is left.
|
|
17
|
+
*/
|
|
18
|
+
export type AppStorePresenceAction = 'create' | 'update' | 'unchanged' | 'blocked';
|
|
19
|
+
export interface PlannedAscScreenshotSet {
|
|
20
|
+
readonly display: AppStorePresenceDisplay;
|
|
21
|
+
readonly displayType: string;
|
|
22
|
+
readonly action: 'unchanged' | 'add' | 'replace';
|
|
23
|
+
readonly complete: boolean;
|
|
24
|
+
/** Uploads, in order, by path relative to the file and MD5. */
|
|
25
|
+
readonly upload: readonly {
|
|
26
|
+
readonly path: string;
|
|
27
|
+
readonly md5: string;
|
|
28
|
+
}[];
|
|
29
|
+
/** MD5 of App Store Connect's screenshots that stay. */
|
|
30
|
+
readonly keep: readonly string[];
|
|
31
|
+
/** App Store Connect's screenshots the write removes and does not upload again. */
|
|
32
|
+
readonly remove: readonly {
|
|
33
|
+
readonly id: string;
|
|
34
|
+
readonly md5: string;
|
|
35
|
+
}[];
|
|
36
|
+
/** `replace`: every screenshot of the set, deleted before the uploads. */
|
|
37
|
+
readonly deleteIds: readonly string[];
|
|
38
|
+
/** The set cannot be planned now (App Store Connect is still processing it). */
|
|
39
|
+
readonly blocked?: true;
|
|
40
|
+
readonly notes: readonly string[];
|
|
41
|
+
}
|
|
42
|
+
export interface PlannedAppStoreLocale {
|
|
43
|
+
/** The locale as App Store Connect spells it (BCP-47 casing for a new one). */
|
|
44
|
+
readonly locale: string;
|
|
45
|
+
readonly appInfo: {
|
|
46
|
+
readonly action: AppStorePresenceAction;
|
|
47
|
+
readonly text: readonly StorePresenceTextChange[];
|
|
48
|
+
readonly write?: {
|
|
49
|
+
readonly name: string;
|
|
50
|
+
readonly subtitle?: string;
|
|
51
|
+
readonly privacyPolicyUrl?: string;
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
readonly version: {
|
|
55
|
+
readonly action: AppStorePresenceAction;
|
|
56
|
+
readonly localizationId?: string;
|
|
57
|
+
readonly text: readonly StorePresenceTextChange[];
|
|
58
|
+
readonly write?: AscVersionLocalizationFields;
|
|
59
|
+
};
|
|
60
|
+
readonly screenshots: readonly PlannedAscScreenshotSet[];
|
|
61
|
+
}
|
|
62
|
+
export interface AppStorePresencePlan {
|
|
63
|
+
readonly schemaVersion: 1;
|
|
64
|
+
readonly store: 'app-store';
|
|
65
|
+
/** Numeric App Store Connect app ID. */
|
|
66
|
+
readonly appId: string;
|
|
67
|
+
readonly bundleId: string;
|
|
68
|
+
/** The editable app information record, when there is one. */
|
|
69
|
+
readonly appInfo?: {
|
|
70
|
+
readonly id: string;
|
|
71
|
+
readonly state: string;
|
|
72
|
+
};
|
|
73
|
+
/** The editable iOS version, when there is one. */
|
|
74
|
+
readonly version?: {
|
|
75
|
+
readonly id: string;
|
|
76
|
+
readonly versionString?: string;
|
|
77
|
+
readonly state: string;
|
|
78
|
+
/** The app's first version: App Store Connect takes no what's new text. */
|
|
79
|
+
readonly first: boolean;
|
|
80
|
+
};
|
|
81
|
+
readonly locales: readonly PlannedAppStoreLocale[];
|
|
82
|
+
/** Locales on App Store Connect that the file does not name. Never deleted. */
|
|
83
|
+
readonly remoteOnlyLocales: readonly string[];
|
|
84
|
+
/**
|
|
85
|
+
* Screenshots apply deletes: the ones a complete set drops, and every one
|
|
86
|
+
* of a set it replaces (re-uploaded after). Apply needs --confirm-image-removal.
|
|
87
|
+
*/
|
|
88
|
+
readonly imageRemovals: number;
|
|
89
|
+
readonly changed: boolean;
|
|
90
|
+
/** Parts that differ but cannot be written now (nothing editable, still processing). */
|
|
91
|
+
readonly blocked: number;
|
|
92
|
+
readonly notes: readonly string[];
|
|
93
|
+
/** What the sync cannot do; the reviewer does it in App Store Connect. */
|
|
94
|
+
readonly manual: readonly string[];
|
|
95
|
+
readonly remoteSha256: string;
|
|
96
|
+
readonly desiredSha256: string;
|
|
97
|
+
}
|
|
98
|
+
export interface AppStorePresencePlanInput {
|
|
99
|
+
readonly appId: string;
|
|
100
|
+
readonly bundleId: string;
|
|
101
|
+
readonly presence: StorePresence;
|
|
102
|
+
/** Directory of the store presence file; screenshots resolve against it. */
|
|
103
|
+
readonly directory: string;
|
|
104
|
+
}
|
|
105
|
+
/** Read App Store Connect and plan; never writes. */
|
|
106
|
+
export declare function planAppStorePresence(input: AppStorePresencePlanInput, port: AscStorePresencePort): Promise<AppStorePresencePlan>;
|
|
107
|
+
export interface AppStorePresenceApplyResult {
|
|
108
|
+
readonly appId: string;
|
|
109
|
+
readonly status: 'updated' | 'unchanged';
|
|
110
|
+
/** The writes, in order. */
|
|
111
|
+
readonly steps: readonly string[];
|
|
112
|
+
readonly manual: readonly string[];
|
|
113
|
+
}
|
|
114
|
+
export declare class AppStorePresenceApplyError extends Error {
|
|
115
|
+
readonly stage: string;
|
|
116
|
+
/** The writes that landed before the failure. */
|
|
117
|
+
readonly written: readonly string[];
|
|
118
|
+
/** App Store Connect refused the step (asc exited with a 4xx-class code): nothing from it landed. */
|
|
119
|
+
readonly definitive: boolean;
|
|
120
|
+
constructor(stage: string,
|
|
121
|
+
/** The writes that landed before the failure. */
|
|
122
|
+
written: readonly string[], cause: unknown, detail?: string,
|
|
123
|
+
/** App Store Connect refused the step (asc exited with a 4xx-class code): nothing from it landed. */
|
|
124
|
+
definitive?: boolean);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Apply a reviewed plan: re-plan, refuse when App Store Connect or the files
|
|
128
|
+
* moved past the reviewed plan, then write step by step. A plan whose
|
|
129
|
+
* changes already landed is a no-op, so a re-run is safe.
|
|
130
|
+
*/
|
|
131
|
+
export declare function applyAppStorePresence(input: {
|
|
132
|
+
readonly saved: AppStorePresencePlan;
|
|
133
|
+
readonly plan: AppStorePresencePlanInput;
|
|
134
|
+
readonly port: AscStorePresencePort;
|
|
135
|
+
readonly approved: boolean;
|
|
136
|
+
readonly confirmImageRemoval?: boolean;
|
|
137
|
+
}): Promise<AppStorePresenceApplyResult>;
|
|
138
|
+
/** The plan for the terminal: per locale, the text and screenshot changes. */
|
|
139
|
+
export declare function formatAppStorePresencePlan(plan: AppStorePresencePlan): string;
|