pabal-store-api-mcp 1.1.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.
Files changed (111) hide show
  1. package/README.md +95 -0
  2. package/bin/pabal-mcp.js +6 -0
  3. package/dist/src/core/clients/app-store-factory.d.ts +29 -0
  4. package/dist/src/core/clients/app-store-factory.js +72 -0
  5. package/dist/src/core/clients/client-factory-helpers.d.ts +7 -0
  6. package/dist/src/core/clients/client-factory-helpers.js +10 -0
  7. package/dist/src/core/clients/google-play-factory.d.ts +29 -0
  8. package/dist/src/core/clients/google-play-factory.js +72 -0
  9. package/dist/src/core/clients/types.d.ts +8 -0
  10. package/dist/src/core/clients/types.js +1 -0
  11. package/dist/src/core/helpers/formatters.d.ts +3 -0
  12. package/dist/src/core/helpers/formatters.js +38 -0
  13. package/dist/src/core/helpers/registration.d.ts +21 -0
  14. package/dist/src/core/helpers/registration.js +21 -0
  15. package/dist/src/core/helpers/translate-release-notes.d.ts +46 -0
  16. package/dist/src/core/helpers/translate-release-notes.js +87 -0
  17. package/dist/src/core/services/app-resolution-service.d.ts +14 -0
  18. package/dist/src/core/services/app-resolution-service.js +35 -0
  19. package/dist/src/core/services/app-store-service.d.ts +41 -0
  20. package/dist/src/core/services/app-store-service.js +266 -0
  21. package/dist/src/core/services/google-play-service.d.ts +36 -0
  22. package/dist/src/core/services/google-play-service.js +203 -0
  23. package/dist/src/core/services/service-helpers.d.ts +15 -0
  24. package/dist/src/core/services/service-helpers.js +31 -0
  25. package/dist/src/core/services/types.d.ts +81 -0
  26. package/dist/src/core/services/types.js +1 -0
  27. package/dist/src/core/workflows/version-info.d.ts +29 -0
  28. package/dist/src/core/workflows/version-info.js +100 -0
  29. package/dist/src/index.d.ts +10 -0
  30. package/dist/src/index.js +279 -0
  31. package/dist/src/packages/common/errors/app-error.d.ts +39 -0
  32. package/dist/src/packages/common/errors/app-error.js +134 -0
  33. package/dist/src/packages/common/errors/error-codes.d.ts +63 -0
  34. package/dist/src/packages/common/errors/error-codes.js +71 -0
  35. package/dist/src/packages/common/errors/status-codes.d.ts +10 -0
  36. package/dist/src/packages/common/errors/status-codes.js +9 -0
  37. package/dist/src/packages/configs/aso-config/constants.d.ts +14 -0
  38. package/dist/src/packages/configs/aso-config/constants.js +102 -0
  39. package/dist/src/packages/configs/aso-config/locale-guards.d.ts +3 -0
  40. package/dist/src/packages/configs/aso-config/locale-guards.js +7 -0
  41. package/dist/src/packages/configs/aso-config/store.d.ts +11 -0
  42. package/dist/src/packages/configs/aso-config/store.js +11 -0
  43. package/dist/src/packages/configs/aso-config/types.d.ts +98 -0
  44. package/dist/src/packages/configs/aso-config/types.js +2 -0
  45. package/dist/src/packages/configs/aso-config/utils.d.ts +43 -0
  46. package/dist/src/packages/configs/aso-config/utils.js +223 -0
  47. package/dist/src/packages/configs/secrets-config/config.d.ts +12 -0
  48. package/dist/src/packages/configs/secrets-config/config.js +187 -0
  49. package/dist/src/packages/configs/secrets-config/constants.d.ts +1 -0
  50. package/dist/src/packages/configs/secrets-config/constants.js +1 -0
  51. package/dist/src/packages/configs/secrets-config/errors.d.ts +9 -0
  52. package/dist/src/packages/configs/secrets-config/errors.js +15 -0
  53. package/dist/src/packages/configs/secrets-config/registered-apps.d.ts +52 -0
  54. package/dist/src/packages/configs/secrets-config/registered-apps.js +108 -0
  55. package/dist/src/packages/configs/secrets-config/schemas.d.ts +21 -0
  56. package/dist/src/packages/configs/secrets-config/schemas.js +9 -0
  57. package/dist/src/packages/configs/secrets-config/types.d.ts +8 -0
  58. package/dist/src/packages/configs/secrets-config/types.js +1 -0
  59. package/dist/src/packages/stores/app-store/api-converters.d.ts +26 -0
  60. package/dist/src/packages/stores/app-store/api-converters.js +131 -0
  61. package/dist/src/packages/stores/app-store/api-endpoints.d.ts +33 -0
  62. package/dist/src/packages/stores/app-store/api-endpoints.js +157 -0
  63. package/dist/src/packages/stores/app-store/auth.d.ts +12 -0
  64. package/dist/src/packages/stores/app-store/auth.js +36 -0
  65. package/dist/src/packages/stores/app-store/client.d.ts +78 -0
  66. package/dist/src/packages/stores/app-store/client.js +637 -0
  67. package/dist/src/packages/stores/app-store/constants.d.ts +11 -0
  68. package/dist/src/packages/stores/app-store/constants.js +38 -0
  69. package/dist/src/packages/stores/app-store/generated-types.d.ts +118537 -0
  70. package/dist/src/packages/stores/app-store/generated-types.js +5 -0
  71. package/dist/src/packages/stores/app-store/types.d.ts +39 -0
  72. package/dist/src/packages/stores/app-store/types.js +9 -0
  73. package/dist/src/packages/stores/app-store/verify-auth.d.ts +16 -0
  74. package/dist/src/packages/stores/app-store/verify-auth.js +34 -0
  75. package/dist/src/packages/stores/play-store/api-converters.d.ts +58 -0
  76. package/dist/src/packages/stores/play-store/api-converters.js +209 -0
  77. package/dist/src/packages/stores/play-store/api-endpoints.d.ts +68 -0
  78. package/dist/src/packages/stores/play-store/api-endpoints.js +145 -0
  79. package/dist/src/packages/stores/play-store/client.d.ts +55 -0
  80. package/dist/src/packages/stores/play-store/client.js +628 -0
  81. package/dist/src/packages/stores/play-store/constants.d.ts +10 -0
  82. package/dist/src/packages/stores/play-store/constants.js +17 -0
  83. package/dist/src/packages/stores/play-store/types.d.ts +146 -0
  84. package/dist/src/packages/stores/play-store/types.js +9 -0
  85. package/dist/src/packages/stores/play-store/verify-auth.d.ts +13 -0
  86. package/dist/src/packages/stores/play-store/verify-auth.js +31 -0
  87. package/dist/src/tools/apps/add.d.ts +28 -0
  88. package/dist/src/tools/apps/add.js +307 -0
  89. package/dist/src/tools/apps/init.d.ts +58 -0
  90. package/dist/src/tools/apps/init.js +390 -0
  91. package/dist/src/tools/apps/search.d.ts +33 -0
  92. package/dist/src/tools/apps/search.js +147 -0
  93. package/dist/src/tools/aso/pull.d.ts +22 -0
  94. package/dist/src/tools/aso/pull.js +264 -0
  95. package/dist/src/tools/aso/push.d.ts +23 -0
  96. package/dist/src/tools/aso/push.js +189 -0
  97. package/dist/src/tools/auth/app-store.d.ts +9 -0
  98. package/dist/src/tools/auth/app-store.js +34 -0
  99. package/dist/src/tools/auth/check.d.ts +14 -0
  100. package/dist/src/tools/auth/check.js +50 -0
  101. package/dist/src/tools/auth/play-store.d.ts +9 -0
  102. package/dist/src/tools/auth/play-store.js +30 -0
  103. package/dist/src/tools/release/check-versions.d.ts +14 -0
  104. package/dist/src/tools/release/check-versions.js +65 -0
  105. package/dist/src/tools/release/create.d.ts +23 -0
  106. package/dist/src/tools/release/create.js +128 -0
  107. package/dist/src/tools/release/pull-notes.d.ts +22 -0
  108. package/dist/src/tools/release/pull-notes.js +151 -0
  109. package/dist/src/tools/release/update-notes.d.ts +110 -0
  110. package/dist/src/tools/release/update-notes.js +537 -0
  111. package/package.json +71 -0
@@ -0,0 +1,637 @@
1
+ /**
2
+ * App Store Connect API Client
3
+ *
4
+ * Authentication: API Key (Issuer ID, Key ID, Private Key) required
5
+ * API Documentation: https://developer.apple.com/documentation/appstoreconnectapi
6
+ */
7
+ import { AppStoreConnectAPI } from "appstore-connect-sdk";
8
+ import { AppsApi, AppInfosApi, AppInfoLocalizationsApi, AppScreenshotSetsApi, AppStoreVersionLocalizationsApi, AppStoreVersionsApi, ResponseError, } from "appstore-connect-sdk/openapi";
9
+ import { DEFAULT_LOCALE } from "../../../packages/configs/aso-config/constants.js";
10
+ import { convertToAsoData, convertToMultilingualAsoData, convertToReleaseNote, fetchScreenshotsForLocalization, mapLocalizationsByLocale, selectEnglishAppName, sortReleaseNotes, sortVersions, } from "./api-converters.js";
11
+ import { APP_STORE_API_BASE_URL, APP_STORE_PLATFORM, DEFAULT_APP_LIST_LIMIT, DEFAULT_VERSIONS_FETCH_LIMIT, } from "./constants.js";
12
+ export class AppStoreClient {
13
+ issuerId;
14
+ keyId;
15
+ privateKey;
16
+ bundleId;
17
+ sdk;
18
+ apiCache = new Map();
19
+ constructor(config) {
20
+ this.issuerId = config.issuerId;
21
+ this.keyId = config.keyId;
22
+ this.privateKey = this.normalizePrivateKey(config.privateKey);
23
+ this.bundleId = config.bundleId;
24
+ this.sdk = new AppStoreConnectAPI({
25
+ issuerId: this.issuerId,
26
+ privateKeyId: this.keyId,
27
+ privateKey: this.privateKey,
28
+ });
29
+ }
30
+ normalizePrivateKey(rawKey) {
31
+ if (!rawKey || !rawKey.includes("BEGIN PRIVATE KEY")) {
32
+ throw new Error("Invalid Private Key format. PEM format private key is required.");
33
+ }
34
+ return rawKey.replace(/\\n/g, "\n").trim();
35
+ }
36
+ /**
37
+ * List all apps registered in the account
38
+ * @param options.onlyReleased - If true, returns only released apps (apps with READY_FOR_SALE status)
39
+ */
40
+ async listAllApps(options = {}) {
41
+ const { onlyReleased = false } = options;
42
+ const apps = [];
43
+ let nextUrl = `/apps?limit=${DEFAULT_APP_LIST_LIMIT}`;
44
+ while (nextUrl) {
45
+ const response = await this.listApps(nextUrl);
46
+ for (const app of response.data || []) {
47
+ const isReleased = await this.checkAppReleased(app.id);
48
+ if (onlyReleased && !isReleased) {
49
+ continue;
50
+ }
51
+ const englishName = await this.getEnglishAppName(app.id);
52
+ apps.push({
53
+ id: app.id,
54
+ name: englishName || app.attributes?.name || "Unknown",
55
+ bundleId: app.attributes?.bundleId || "",
56
+ sku: app.attributes?.sku || "",
57
+ isReleased,
58
+ });
59
+ }
60
+ nextUrl = this.normalizeNextLink(response.links?.next);
61
+ }
62
+ return apps;
63
+ }
64
+ async checkAppReleased(appId) {
65
+ try {
66
+ const response = await this.listAppStoreVersions(appId, {
67
+ platform: APP_STORE_PLATFORM,
68
+ state: "READY_FOR_SALE",
69
+ limit: 1,
70
+ });
71
+ return (response.data?.length || 0) > 0;
72
+ }
73
+ catch {
74
+ return false;
75
+ }
76
+ }
77
+ async getEnglishAppName(appId) {
78
+ try {
79
+ const appInfoId = await this.findAppInfoId(appId);
80
+ const localizationsResponse = await this.listAppInfoLocalizations(appInfoId);
81
+ return selectEnglishAppName(localizationsResponse.data || []);
82
+ }
83
+ catch {
84
+ return null;
85
+ }
86
+ }
87
+ /**
88
+ * Get supported locales for an app
89
+ */
90
+ async getSupportedLocales(appId) {
91
+ try {
92
+ const appInfoId = await this.findAppInfoId(appId);
93
+ const localizationsResponse = await this.listAppInfoLocalizations(appInfoId);
94
+ return (localizationsResponse.data || [])
95
+ .map((l) => l.attributes?.locale)
96
+ .filter((locale) => !!locale)
97
+ .sort();
98
+ }
99
+ catch {
100
+ return [];
101
+ }
102
+ }
103
+ async findAppId() {
104
+ const response = await this.findAppByBundleId(this.bundleId);
105
+ const app = response.data?.[0];
106
+ if (!app) {
107
+ throw new Error(`App not found for Bundle ID "${this.bundleId}".`);
108
+ }
109
+ return app.id;
110
+ }
111
+ async findAppInfoId(appId) {
112
+ const targetAppId = appId || (await this.findAppId());
113
+ const appInfosResponse = await this.listAppInfos(targetAppId);
114
+ const appInfo = appInfosResponse.data?.[0];
115
+ if (!appInfo) {
116
+ throw new Error(`App Info not found for App ID "${targetAppId}".`);
117
+ }
118
+ return appInfo.id;
119
+ }
120
+ async pullAsoData(options = {}) {
121
+ const locale = options.locale || DEFAULT_LOCALE;
122
+ const appId = await this.findAppId();
123
+ const appInfoId = await this.findAppInfoId(appId);
124
+ const [appResponse, versionsResponse, appInfoLocalizationResponse] = await Promise.all([
125
+ this.getApp(appId),
126
+ this.listAppStoreVersions(appId, {
127
+ platform: APP_STORE_PLATFORM,
128
+ limit: DEFAULT_VERSIONS_FETCH_LIMIT,
129
+ }),
130
+ this.listAppInfoLocalizations(appInfoId, locale),
131
+ ]);
132
+ const app = appResponse.data;
133
+ const version = sortVersions(versionsResponse.data || [])[0];
134
+ if (!version)
135
+ throw new Error("App Store version not found.");
136
+ const localizationsResponse = await this.listAppStoreVersionLocalizations(version.id, locale);
137
+ const localization = localizationsResponse.data?.[0];
138
+ const detailedLocalization = localization
139
+ ? (await this.getAppStoreVersionLocalization(localization.id)).data
140
+ : null;
141
+ const screenshots = await fetchScreenshotsForLocalization(localization?.id, (localizationId) => this.listScreenshotSets(localizationId), (setId) => this.listScreenshots(setId));
142
+ const appInfoLocalization = appInfoLocalizationResponse.data?.[0];
143
+ return convertToAsoData({
144
+ app,
145
+ appInfoLocalization,
146
+ localization: detailedLocalization,
147
+ screenshots,
148
+ locale,
149
+ bundleId: this.bundleId,
150
+ });
151
+ }
152
+ async pullAllLocalesAsoData() {
153
+ const appId = await this.findAppId();
154
+ const appInfoId = await this.findAppInfoId(appId);
155
+ const [appResponse, appInfoLocalizationsResponse, versionsResponse] = await Promise.all([
156
+ this.getApp(appId),
157
+ this.listAppInfoLocalizations(appInfoId),
158
+ this.listAppStoreVersions(appId, {
159
+ platform: APP_STORE_PLATFORM,
160
+ limit: DEFAULT_VERSIONS_FETCH_LIMIT,
161
+ }),
162
+ ]);
163
+ const app = appResponse.data;
164
+ const appInfoLocalizationMap = mapLocalizationsByLocale(appInfoLocalizationsResponse.data || []);
165
+ const version = sortVersions(versionsResponse.data || [])[0];
166
+ if (!version)
167
+ throw new Error("App Store version not found.");
168
+ const localizationsResponse = await this.listAppStoreVersionLocalizations(version.id);
169
+ const allLocalizations = localizationsResponse.data || [];
170
+ console.log(`🌍 Found ${allLocalizations.length} App Store localizations: ${allLocalizations
171
+ .map((l) => l.attributes?.locale)
172
+ .join(", ")}`);
173
+ const locales = {};
174
+ let defaultLocale;
175
+ for (const localization of allLocalizations) {
176
+ const locale = localization.attributes?.locale;
177
+ if (!locale)
178
+ continue;
179
+ const detailedLocalization = (await this.getAppStoreVersionLocalization(localization.id)).data ??
180
+ null;
181
+ const screenshots = await fetchScreenshotsForLocalization(localization.id, (localizationId) => this.listScreenshotSets(localizationId), (setId) => this.listScreenshots(setId));
182
+ locales[locale] = convertToAsoData({
183
+ app,
184
+ appInfoLocalization: appInfoLocalizationMap[locale],
185
+ localization: detailedLocalization,
186
+ screenshots,
187
+ locale,
188
+ bundleId: this.bundleId,
189
+ });
190
+ if (!defaultLocale && locale === DEFAULT_LOCALE) {
191
+ defaultLocale = locale;
192
+ }
193
+ }
194
+ return convertToMultilingualAsoData(locales, defaultLocale);
195
+ }
196
+ async pushAsoData(data) {
197
+ const appId = await this.findAppId();
198
+ const appInfoId = await this.findAppInfoId(appId);
199
+ const locale = data.locale || DEFAULT_LOCALE;
200
+ const appInfoLocalizationResponse = await this.listAppInfoLocalizations(appInfoId, locale);
201
+ const appInfoAttributes = {};
202
+ const attemptedFields = [];
203
+ if (typeof data.name === "string") {
204
+ appInfoAttributes.name = data.name;
205
+ attemptedFields.push("name");
206
+ }
207
+ if (typeof data.subtitle === "string") {
208
+ appInfoAttributes.subtitle = data.subtitle;
209
+ attemptedFields.push("subtitle");
210
+ }
211
+ const failedFields = [];
212
+ if (Object.keys(appInfoAttributes).length > 0) {
213
+ try {
214
+ if (appInfoLocalizationResponse.data?.[0]) {
215
+ const localizationId = appInfoLocalizationResponse.data[0].id;
216
+ await this.updateAppInfoLocalization(localizationId, appInfoAttributes);
217
+ }
218
+ else {
219
+ await this.createAppInfoLocalization(appInfoId, locale, appInfoAttributes);
220
+ }
221
+ }
222
+ catch (error) {
223
+ const msg = error instanceof Error ? error.message : String(error);
224
+ if (msg.includes("STATE_ERROR") || msg.includes("409 Conflict")) {
225
+ failedFields.push(...attemptedFields);
226
+ console.error(`[AppStore] ⚠️ Name/Subtitle update failed for ${locale}: ${msg}`);
227
+ console.error(`[AppStore] ℹ️ Name and Subtitle can only be updated when a new version is created.`);
228
+ console.error(`[AppStore] ⏭️ Continuing with other ASO fields (description, keywords, etc.)...`);
229
+ }
230
+ else {
231
+ throw error;
232
+ }
233
+ }
234
+ }
235
+ const versionsResponse = await this.listAppStoreVersions(appId, {
236
+ platform: APP_STORE_PLATFORM,
237
+ limit: DEFAULT_VERSIONS_FETCH_LIMIT,
238
+ });
239
+ const version = sortVersions(versionsResponse.data || [])[0];
240
+ if (!version)
241
+ throw new Error("App Store version not found.");
242
+ const localizationsResponse = await this.listAppStoreVersionLocalizations(version.id, locale);
243
+ let localizationId;
244
+ if (localizationsResponse.data?.[0]) {
245
+ localizationId = localizationsResponse.data[0].id;
246
+ await this.updateAppStoreVersionLocalization(localizationId, {
247
+ description: data.description,
248
+ keywords: data.keywords,
249
+ promotionalText: data.promotionalText,
250
+ supportUrl: data.supportUrl,
251
+ marketingUrl: data.marketingUrl,
252
+ });
253
+ }
254
+ else {
255
+ const createResponse = await this.createAppStoreVersionLocalization(version.id, locale, {
256
+ description: data.description,
257
+ keywords: data.keywords,
258
+ promotionalText: data.promotionalText,
259
+ supportUrl: data.supportUrl,
260
+ marketingUrl: data.marketingUrl,
261
+ });
262
+ localizationId = createResponse.data.id;
263
+ }
264
+ return failedFields.length > 0 ? { failedFields } : {};
265
+ }
266
+ async getAllVersions(limit = 50) {
267
+ const appId = await this.findAppId();
268
+ const response = await this.listAppStoreVersions(appId, {
269
+ platform: APP_STORE_PLATFORM,
270
+ limit,
271
+ });
272
+ return sortVersions(response.data || []);
273
+ }
274
+ async getLatestVersion() {
275
+ const versions = await this.getAllVersions();
276
+ return versions[0] || null;
277
+ }
278
+ incrementVersion(versionString) {
279
+ const parts = versionString.split(".").map(Number);
280
+ while (parts.length < 3)
281
+ parts.push(0);
282
+ parts[parts.length - 1] = (parts[parts.length - 1] || 0) + 1;
283
+ return parts.join(".");
284
+ }
285
+ async createNewVersion(versionString) {
286
+ const appId = await this.findAppId();
287
+ const versionsResponse = await this.listAppStoreVersions(appId, {
288
+ platform: APP_STORE_PLATFORM,
289
+ limit: 50,
290
+ });
291
+ const existing = versionsResponse.data?.find((v) => v.attributes?.versionString === versionString);
292
+ if (existing) {
293
+ console.log(`⚠️ Version ${versionString} already exists.`);
294
+ return existing;
295
+ }
296
+ const createResponse = await this.createAppStoreVersion(appId, versionString, APP_STORE_PLATFORM);
297
+ console.log(`✅ Created new version: ${versionString}`);
298
+ return createResponse.data;
299
+ }
300
+ async createNewVersionWithAutoIncrement(baseVersion) {
301
+ let newVersionString;
302
+ if (baseVersion) {
303
+ newVersionString = this.incrementVersion(baseVersion);
304
+ }
305
+ else {
306
+ const latestVersion = await this.getLatestVersion();
307
+ if (!latestVersion) {
308
+ newVersionString = "1.0.0";
309
+ console.log(`📦 No existing versions. Starting with ${newVersionString}`);
310
+ }
311
+ else {
312
+ const latest = latestVersion.attributes?.versionString ?? "0.0.0";
313
+ newVersionString = this.incrementVersion(latest);
314
+ console.log(`📦 Latest: ${latest} → New: ${newVersionString}`);
315
+ }
316
+ }
317
+ return this.createNewVersion(newVersionString);
318
+ }
319
+ async updateWhatsNew(options) {
320
+ const { versionId, locale, whatsNew } = options;
321
+ const localizationsResponse = await this.listAppStoreVersionLocalizations(versionId, locale);
322
+ if (localizationsResponse.data?.[0]) {
323
+ const localizationId = localizationsResponse.data[0].id;
324
+ await this.updateAppStoreVersionLocalization(localizationId, {
325
+ whatsNew,
326
+ });
327
+ }
328
+ else {
329
+ await this.createAppStoreVersionLocalization(versionId, locale, {
330
+ whatsNew,
331
+ });
332
+ }
333
+ console.log(`✅ Updated What's New for ${locale}`);
334
+ }
335
+ async pullReleaseNotes() {
336
+ const appId = await this.findAppId();
337
+ const versionsResponse = await this.listAppStoreVersions(appId, {
338
+ platform: APP_STORE_PLATFORM,
339
+ limit: 50,
340
+ });
341
+ const releaseNotes = [];
342
+ for (const version of versionsResponse.data || []) {
343
+ const localizationsResponse = await this.listAppStoreVersionLocalizations(version.id);
344
+ const releaseNote = convertToReleaseNote(version, localizationsResponse.data || []);
345
+ if (releaseNote) {
346
+ releaseNotes.push(releaseNote);
347
+ }
348
+ }
349
+ return sortReleaseNotes(releaseNotes);
350
+ }
351
+ normalizeNextLink(nextLink) {
352
+ if (!nextLink)
353
+ return null;
354
+ return nextLink.replace(APP_STORE_API_BASE_URL, "");
355
+ }
356
+ async listApps(nextUrl = `/apps?limit=${DEFAULT_APP_LIST_LIMIT}`) {
357
+ return this.requestCollection(nextUrl);
358
+ }
359
+ async findAppByBundleId(bundleId) {
360
+ const appsApi = await this.getApi(AppsApi);
361
+ try {
362
+ return await appsApi.appsGetCollection({
363
+ filterBundleId: [bundleId],
364
+ });
365
+ }
366
+ catch (error) {
367
+ return await this.handleSdkError(error);
368
+ }
369
+ }
370
+ async getApp(appId) {
371
+ const appsApi = await this.getApi(AppsApi);
372
+ try {
373
+ return await appsApi.appsGetInstance({ id: appId });
374
+ }
375
+ catch (error) {
376
+ return await this.handleSdkError(error);
377
+ }
378
+ }
379
+ async listAppInfos(appId) {
380
+ const appsApi = await this.getApi(AppsApi);
381
+ try {
382
+ return await appsApi.appsAppInfosGetToManyRelated({
383
+ id: appId,
384
+ limit: 1,
385
+ });
386
+ }
387
+ catch (error) {
388
+ return await this.handleSdkError(error);
389
+ }
390
+ }
391
+ async listAppInfoLocalizations(appInfoId, locale) {
392
+ const appInfosApi = await this.getApi(AppInfosApi);
393
+ try {
394
+ return await appInfosApi.appInfosAppInfoLocalizationsGetToManyRelated({
395
+ id: appInfoId,
396
+ filterLocale: locale ? [locale] : undefined,
397
+ });
398
+ }
399
+ catch (error) {
400
+ return await this.handleSdkError(error);
401
+ }
402
+ }
403
+ async updateAppInfoLocalization(localizationId, attributes) {
404
+ const appInfoLocalizationsApi = await this.getApi(AppInfoLocalizationsApi);
405
+ try {
406
+ await appInfoLocalizationsApi.appInfoLocalizationsUpdateInstance({
407
+ id: localizationId,
408
+ appInfoLocalizationUpdateRequest: {
409
+ data: {
410
+ type: "appInfoLocalizations",
411
+ id: localizationId,
412
+ attributes,
413
+ },
414
+ },
415
+ });
416
+ }
417
+ catch (error) {
418
+ return await this.handleSdkError(error);
419
+ }
420
+ }
421
+ async createAppInfoLocalization(appInfoId, locale, attributes) {
422
+ const appInfoLocalizationsApi = await this.getApi(AppInfoLocalizationsApi);
423
+ try {
424
+ return await appInfoLocalizationsApi.appInfoLocalizationsCreateInstance({
425
+ appInfoLocalizationCreateRequest: {
426
+ data: {
427
+ type: "appInfoLocalizations",
428
+ attributes: { locale, ...attributes },
429
+ relationships: {
430
+ appInfo: {
431
+ data: { type: "appInfos", id: appInfoId },
432
+ },
433
+ },
434
+ },
435
+ },
436
+ });
437
+ }
438
+ catch (error) {
439
+ return await this.handleSdkError(error);
440
+ }
441
+ }
442
+ async listAppStoreVersions(appId, options = {}) {
443
+ const { platform = APP_STORE_PLATFORM, state, limit = DEFAULT_VERSIONS_FETCH_LIMIT, } = options;
444
+ const appsApi = await this.getApi(AppsApi);
445
+ try {
446
+ return await appsApi.appsAppStoreVersionsGetToManyRelated({
447
+ id: appId,
448
+ filterPlatform: [
449
+ platform,
450
+ ],
451
+ filterAppStoreState: state
452
+ ? [
453
+ state,
454
+ ]
455
+ : undefined,
456
+ limit,
457
+ });
458
+ }
459
+ catch (error) {
460
+ return await this.handleSdkError(error);
461
+ }
462
+ }
463
+ async createAppStoreVersion(appId, versionString, platform = APP_STORE_PLATFORM) {
464
+ const appStoreVersionsApi = await this.getApi(AppStoreVersionsApi);
465
+ try {
466
+ return await appStoreVersionsApi.appStoreVersionsCreateInstance({
467
+ appStoreVersionCreateRequest: {
468
+ data: {
469
+ type: "appStoreVersions",
470
+ attributes: {
471
+ platform: platform,
472
+ versionString,
473
+ },
474
+ relationships: {
475
+ app: { data: { type: "apps", id: appId } },
476
+ },
477
+ },
478
+ },
479
+ });
480
+ }
481
+ catch (error) {
482
+ return await this.handleSdkError(error);
483
+ }
484
+ }
485
+ async listAppStoreVersionLocalizations(versionId, locale) {
486
+ const appStoreVersionsApi = await this.getApi(AppStoreVersionsApi);
487
+ try {
488
+ const response = await appStoreVersionsApi.appStoreVersionsAppStoreVersionLocalizationsGetToManyRelated({
489
+ id: versionId,
490
+ });
491
+ if (!locale)
492
+ return response;
493
+ return {
494
+ ...response,
495
+ data: (response.data || []).filter((localization) => localization.attributes?.locale === locale),
496
+ };
497
+ }
498
+ catch (error) {
499
+ return await this.handleSdkError(error);
500
+ }
501
+ }
502
+ async getAppStoreVersionLocalization(localizationId) {
503
+ const appStoreVersionLocalizationsApi = await this.getApi(AppStoreVersionLocalizationsApi);
504
+ try {
505
+ return await appStoreVersionLocalizationsApi.appStoreVersionLocalizationsGetInstance({ id: localizationId });
506
+ }
507
+ catch (error) {
508
+ return await this.handleSdkError(error);
509
+ }
510
+ }
511
+ async updateAppStoreVersionLocalization(localizationId, attributes) {
512
+ const appStoreVersionLocalizationsApi = await this.getApi(AppStoreVersionLocalizationsApi);
513
+ try {
514
+ await appStoreVersionLocalizationsApi.appStoreVersionLocalizationsUpdateInstance({
515
+ id: localizationId,
516
+ appStoreVersionLocalizationUpdateRequest: {
517
+ data: {
518
+ type: "appStoreVersionLocalizations",
519
+ id: localizationId,
520
+ attributes,
521
+ },
522
+ },
523
+ });
524
+ }
525
+ catch (error) {
526
+ return await this.handleSdkError(error);
527
+ }
528
+ }
529
+ async createAppStoreVersionLocalization(versionId, locale, attributes) {
530
+ const appStoreVersionLocalizationsApi = await this.getApi(AppStoreVersionLocalizationsApi);
531
+ try {
532
+ return await appStoreVersionLocalizationsApi.appStoreVersionLocalizationsCreateInstance({
533
+ appStoreVersionLocalizationCreateRequest: {
534
+ data: {
535
+ type: "appStoreVersionLocalizations",
536
+ attributes: { locale, ...attributes },
537
+ relationships: {
538
+ appStoreVersion: {
539
+ data: { type: "appStoreVersions", id: versionId },
540
+ },
541
+ },
542
+ },
543
+ },
544
+ });
545
+ }
546
+ catch (error) {
547
+ return await this.handleSdkError(error);
548
+ }
549
+ }
550
+ async listScreenshotSets(localizationId) {
551
+ const appStoreVersionLocalizationsApi = await this.getApi(AppStoreVersionLocalizationsApi);
552
+ try {
553
+ return await appStoreVersionLocalizationsApi.appStoreVersionLocalizationsAppScreenshotSetsGetToManyRelated({ id: localizationId });
554
+ }
555
+ catch (error) {
556
+ return await this.handleSdkError(error);
557
+ }
558
+ }
559
+ async listScreenshots(screenshotSetId) {
560
+ const appScreenshotSetsApi = await this.getApi(AppScreenshotSetsApi);
561
+ try {
562
+ return await appScreenshotSetsApi.appScreenshotSetsAppScreenshotsGetToManyRelated({ id: screenshotSetId });
563
+ }
564
+ catch (error) {
565
+ return await this.handleSdkError(error);
566
+ }
567
+ }
568
+ async getApi(apiClass) {
569
+ if (!this.apiCache.has(apiClass)) {
570
+ this.apiCache.set(apiClass, this.sdk.create(apiClass));
571
+ }
572
+ return this.apiCache.get(apiClass);
573
+ }
574
+ normalizeEndpoint(endpoint) {
575
+ if (endpoint.startsWith("http"))
576
+ return endpoint;
577
+ if (endpoint.startsWith("/v1/"))
578
+ return endpoint;
579
+ if (endpoint.startsWith("/"))
580
+ return `/v1${endpoint}`;
581
+ return `/v1/${endpoint}`;
582
+ }
583
+ async requestCollection(endpoint) {
584
+ try {
585
+ const response = await this.sdk.request(endpoint.startsWith("http")
586
+ ? { url: endpoint }
587
+ : { path: this.normalizeEndpoint(endpoint) });
588
+ return (await response.json());
589
+ }
590
+ catch (error) {
591
+ return await this.handleSdkError(error);
592
+ }
593
+ }
594
+ formatErrorPayload(payload) {
595
+ if (!payload)
596
+ return "";
597
+ if (typeof payload === "string")
598
+ return payload;
599
+ try {
600
+ return JSON.stringify(payload, null, 2);
601
+ }
602
+ catch {
603
+ return String(payload);
604
+ }
605
+ }
606
+ async handleSdkError(error) {
607
+ if (error instanceof ResponseError) {
608
+ let errorBody = null;
609
+ try {
610
+ errorBody = await error.response.json();
611
+ }
612
+ catch {
613
+ // ignore JSON parse errors for error bodies
614
+ }
615
+ if (error.response.status === 401) {
616
+ throw new Error(`App Store Connect API authentication failed (401 Unauthorized)\n` +
617
+ `Issuer ID: ${this.issuerId}\n` +
618
+ `Key ID: ${this.keyId}\n` +
619
+ `Error: ${this.formatErrorPayload(errorBody)}`);
620
+ }
621
+ if (error.response.status === 409) {
622
+ const errorDetails = this.formatErrorPayload(errorBody);
623
+ if (errorDetails.includes("STATE_ERROR")) {
624
+ throw new Error(`App Store Connect API error: 409 Conflict (STATE_ERROR)\n` +
625
+ `Metadata cannot be modified in current state. Please check app status.\n` +
626
+ `Error: ${errorDetails}`);
627
+ }
628
+ }
629
+ const statusText = error.response.statusText || "Unknown Error";
630
+ throw new Error(`App Store Connect API error: ${error.response.status} ${statusText}\n${this.formatErrorPayload(errorBody)}`);
631
+ }
632
+ if (error instanceof Error) {
633
+ throw error;
634
+ }
635
+ throw new Error(String(error));
636
+ }
637
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * App Store Connect Constants
3
+ *
4
+ * Constant values for App Store operations
5
+ */
6
+ import type { AppStoreScreenshots } from "../../../packages/configs/aso-config/types.js";
7
+ export declare const APP_STORE_API_BASE_URL = "https://api.appstoreconnect.apple.com/v1";
8
+ export declare const APP_STORE_PLATFORM = "IOS";
9
+ export declare const DEFAULT_APP_LIST_LIMIT = 200;
10
+ export declare const DEFAULT_VERSIONS_FETCH_LIMIT = 10;
11
+ export declare const SCREENSHOT_TYPE_MAP: Record<string, keyof AppStoreScreenshots>;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * App Store Connect Constants
3
+ *
4
+ * Constant values for App Store operations
5
+ */
6
+ export const APP_STORE_API_BASE_URL = "https://api.appstoreconnect.apple.com/v1";
7
+ export const APP_STORE_PLATFORM = "IOS";
8
+ export const DEFAULT_APP_LIST_LIMIT = 200;
9
+ export const DEFAULT_VERSIONS_FETCH_LIMIT = 10;
10
+ export const SCREENSHOT_TYPE_MAP = {
11
+ IPHONE_65: "iphone65",
12
+ IPHONE_61: "iphone61",
13
+ IPHONE_58: "iphone58",
14
+ IPHONE_55: "iphone55",
15
+ IPHONE_47: "iphone47",
16
+ IPHONE_40: "iphone40",
17
+ IPAD_PRO_129: "ipadPro129",
18
+ IPAD_PRO_11: "ipadPro11",
19
+ IPAD_105: "ipad105",
20
+ IPAD_97: "ipad97",
21
+ APPLE_WATCH_SERIES_4: "appleWatch",
22
+ APPLE_WATCH_SERIES_3: "appleWatch",
23
+ APP_IPHONE_67: "iphone65",
24
+ APP_IPHONE_65: "iphone65",
25
+ APP_IPHONE_61: "iphone61",
26
+ APP_IPHONE_58: "iphone58",
27
+ APP_IPHONE_55: "iphone55",
28
+ APP_IPHONE_47: "iphone47",
29
+ APP_IPHONE_40: "iphone40",
30
+ APP_IPAD_PRO_3GEN_129: "ipadPro129",
31
+ APP_IPAD_PRO_129: "ipadPro129",
32
+ APP_IPAD_PRO_3GEN_11: "ipadPro11",
33
+ APP_IPAD_PRO_11: "ipadPro11",
34
+ APP_IPAD_105: "ipad105",
35
+ APP_IPAD_97: "ipad97",
36
+ APP_APPLE_WATCH_SERIES_4: "appleWatch",
37
+ APP_APPLE_WATCH_SERIES_3: "appleWatch",
38
+ };