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,187 @@
1
+ import { existsSync, readFileSync, statSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { isAbsolute, join, resolve } from "node:path";
4
+ import { ERROR_CODES } from "../../../packages/common/errors/error-codes.js";
5
+ import { HTTP_STATUS } from "../../../packages/common/errors/status-codes.js";
6
+ import { ConfigError } from "./errors.js";
7
+ import { DATA_DIR_ENV_KEY } from "./constants.js";
8
+ import { appStoreSchema, playStoreSchema } from "./schemas.js";
9
+ // Config paths
10
+ const CONFIG_DIR = join(homedir(), ".config", "pabal-store-api-mcp");
11
+ const CONFIG_PATH = join(CONFIG_DIR, "config.json");
12
+ // Security: recommended permissions
13
+ const RECOMMENDED_DIR_MODE = 0o700; // drwx------
14
+ const RECOMMENDED_FILE_MODE = 0o600; // -rw-------
15
+ /**
16
+ * Check file/directory permissions and warn if too permissive
17
+ */
18
+ function checkPermissions(path, isDirectory) {
19
+ try {
20
+ const stats = statSync(path);
21
+ const mode = stats.mode & 0o777;
22
+ const recommended = isDirectory
23
+ ? RECOMMENDED_DIR_MODE
24
+ : RECOMMENDED_FILE_MODE;
25
+ // Check if "group" or "others" have any permissions
26
+ const groupOthersPerms = mode & 0o077;
27
+ if (groupOthersPerms !== 0) {
28
+ const typeLabel = isDirectory ? "directory" : "file";
29
+ const recommendedStr = recommended.toString(8);
30
+ console.error(`[Security] ⚠️ Warning: ${typeLabel} "${path}" has permissive permissions (${mode.toString(8)})`);
31
+ console.error(`[Security] Run: chmod ${recommendedStr} "${path}"`);
32
+ }
33
+ }
34
+ catch {
35
+ // Ignore permission check errors
36
+ }
37
+ }
38
+ /**
39
+ * Check all config files for secure permissions
40
+ */
41
+ function checkConfigSecurity(configDir) {
42
+ // Check directory permissions
43
+ checkPermissions(configDir, true);
44
+ // Check all files in config directory
45
+ const sensitiveFiles = [
46
+ "config.json",
47
+ "app-store-key.p8",
48
+ "google-play-service-account.json",
49
+ "registered-apps.json",
50
+ ];
51
+ for (const file of sensitiveFiles) {
52
+ const filePath = join(configDir, file);
53
+ if (existsSync(filePath)) {
54
+ checkPermissions(filePath, false);
55
+ }
56
+ }
57
+ }
58
+ /**
59
+ * Get config directory path: ~/.config/pabal-mcp/
60
+ */
61
+ export function getConfigDir() {
62
+ return CONFIG_DIR;
63
+ }
64
+ /**
65
+ * Get config file path: ~/.config/pabal-mcp/config.json
66
+ */
67
+ export function getConfigPath() {
68
+ return CONFIG_PATH;
69
+ }
70
+ export function readConfigFile() {
71
+ const configPath = getConfigPath();
72
+ if (!existsSync(configPath)) {
73
+ return null;
74
+ }
75
+ try {
76
+ const configContent = readFileSync(configPath, "utf-8");
77
+ return JSON.parse(configContent);
78
+ }
79
+ catch (error) {
80
+ console.error(`[Config] ⚠️ Failed to read config file: ${error instanceof Error ? error.message : String(error)}`);
81
+ return null;
82
+ }
83
+ }
84
+ export function getDataDir() {
85
+ const configDir = getConfigDir();
86
+ console.error(`[Config] 🔍 Checking data directory config...`);
87
+ // 1. Check config file first
88
+ const config = readConfigFile();
89
+ if (config?.dataDir && typeof config.dataDir === "string") {
90
+ const normalized = config.dataDir.trim();
91
+ if (normalized) {
92
+ const resultDir = isAbsolute(normalized)
93
+ ? normalized
94
+ : resolve(configDir, normalized);
95
+ console.error(`[Config] ✅ Using config file dataDir: ${resultDir}`);
96
+ return resultDir;
97
+ }
98
+ }
99
+ // 2. Check environment variable
100
+ const override = process.env[DATA_DIR_ENV_KEY];
101
+ if (override && override.trim()) {
102
+ const normalized = override.trim();
103
+ const resultDir = isAbsolute(normalized)
104
+ ? normalized
105
+ : resolve(configDir, normalized);
106
+ console.error(`[Config] ✅ Using environment variable: ${resultDir}`);
107
+ return resultDir;
108
+ }
109
+ // 3. Default to config directory
110
+ console.error(`[Config] ✅ Using default (config dir): ${configDir}`);
111
+ return configDir;
112
+ }
113
+ export function loadConfig() {
114
+ const configDir = getConfigDir();
115
+ // Security check: warn if permissions are too permissive
116
+ checkConfigSecurity(configDir);
117
+ const config = readConfigFile();
118
+ if (!config) {
119
+ const configPath = getConfigPath();
120
+ throw new ConfigError(`Config file not found: ${configPath}\n` +
121
+ `Please create ~/.config/pabal-store-api-mcp/config.json`, { status: HTTP_STATUS.NOT_FOUND, code: ERROR_CODES.CONFIG_NOT_FOUND });
122
+ }
123
+ try {
124
+ const result = {};
125
+ if (config.appStore) {
126
+ const { issuerId, keyId, privateKeyPath } = config.appStore;
127
+ if (issuerId && keyId && privateKeyPath) {
128
+ const keyPath = isAbsolute(privateKeyPath)
129
+ ? privateKeyPath
130
+ : resolve(configDir, privateKeyPath);
131
+ const privateKey = readFileSafe(keyPath);
132
+ if (privateKey) {
133
+ result.appStore = appStoreSchema.parse({
134
+ keyId,
135
+ issuerId,
136
+ privateKey: normalizePrivateKey(privateKey),
137
+ });
138
+ }
139
+ }
140
+ }
141
+ if (config.googlePlay?.serviceAccountKeyPath) {
142
+ const serviceAccountPath = config.googlePlay.serviceAccountKeyPath;
143
+ const jsonPath = isAbsolute(serviceAccountPath)
144
+ ? serviceAccountPath
145
+ : resolve(configDir, serviceAccountPath);
146
+ const json = readFileSafe(jsonPath);
147
+ if (json) {
148
+ result.playStore = playStoreSchema.parse({
149
+ serviceAccountJson: json,
150
+ });
151
+ }
152
+ }
153
+ if (!result.appStore && !result.playStore) {
154
+ throw new ConfigError("Config file does not contain App Store or Play Store authentication information.", {
155
+ status: HTTP_STATUS.UNAUTHORIZED,
156
+ code: ERROR_CODES.CONFIG_AUTH_NOT_FOUND,
157
+ });
158
+ }
159
+ return result;
160
+ }
161
+ catch (error) {
162
+ if (error instanceof ConfigError) {
163
+ throw error;
164
+ }
165
+ throw new ConfigError(`Error reading config file: ${error instanceof Error ? error.message : String(error)}`, {
166
+ status: HTTP_STATUS.INTERNAL_SERVER_ERROR,
167
+ code: ERROR_CODES.CONFIG_READ_FAILED,
168
+ cause: error,
169
+ });
170
+ }
171
+ }
172
+ function readFileSafe(path) {
173
+ try {
174
+ return readFileSync(path, "utf-8");
175
+ }
176
+ catch {
177
+ return undefined;
178
+ }
179
+ }
180
+ // Restore line breaks in PEM key
181
+ function normalizePrivateKey(raw) {
182
+ if (raw.includes("-----BEGIN")) {
183
+ return raw;
184
+ }
185
+ const restored = raw.replace(/\\n/g, "\n");
186
+ return restored;
187
+ }
@@ -0,0 +1 @@
1
+ export declare const DATA_DIR_ENV_KEY = "PABAL_MCP_DATA_DIR";
@@ -0,0 +1 @@
1
+ export const DATA_DIR_ENV_KEY = "PABAL_MCP_DATA_DIR";
@@ -0,0 +1,9 @@
1
+ import { AppError } from "../../../packages/common/errors/app-error.js";
2
+ export declare class ConfigError extends AppError {
3
+ constructor(message: string, options?: {
4
+ code?: string;
5
+ status?: number;
6
+ details?: unknown;
7
+ cause?: unknown;
8
+ });
9
+ }
@@ -0,0 +1,15 @@
1
+ import { AppError } from "../../../packages/common/errors/app-error.js";
2
+ import { ERROR_CODES } from "../../../packages/common/errors/error-codes.js";
3
+ import { HTTP_STATUS } from "../../../packages/common/errors/status-codes.js";
4
+ export class ConfigError extends AppError {
5
+ constructor(message, options) {
6
+ super({
7
+ status: options?.status ?? HTTP_STATUS.INTERNAL_SERVER_ERROR,
8
+ code: options?.code ?? ERROR_CODES.CONFIG_ERROR,
9
+ message,
10
+ details: options?.details,
11
+ cause: options?.cause,
12
+ });
13
+ this.name = "ConfigError";
14
+ }
15
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Registered app management
3
+ * Manage app list through ~/.config/pabal-mcp/registered-apps.json file
4
+ */
5
+ export interface RegisteredAppStoreInfo {
6
+ bundleId: string;
7
+ appId?: string;
8
+ name?: string;
9
+ supportedLocales?: string[];
10
+ }
11
+ export interface RegisteredGooglePlayInfo {
12
+ packageName: string;
13
+ name?: string;
14
+ supportedLocales?: string[];
15
+ }
16
+ export interface RegisteredApp {
17
+ /** App identifier (user-defined, must be unique) */
18
+ slug: string;
19
+ /** Display name */
20
+ name: string;
21
+ /** App Store information */
22
+ appStore?: RegisteredAppStoreInfo;
23
+ /** Google Play information */
24
+ googlePlay?: RegisteredGooglePlayInfo;
25
+ }
26
+ export interface RegisteredAppsConfig {
27
+ apps: RegisteredApp[];
28
+ }
29
+ /**
30
+ * Load registered app list
31
+ */
32
+ export declare function loadRegisteredApps(): RegisteredAppsConfig;
33
+ /**
34
+ * Save registered app list
35
+ */
36
+ export declare function saveRegisteredApps(config: RegisteredAppsConfig): void;
37
+ /**
38
+ * Register app
39
+ */
40
+ export declare function registerApp(app: RegisteredApp): RegisteredApp;
41
+ /**
42
+ * Find app (search by slug, bundleId, packageName)
43
+ */
44
+ export declare function findApp(identifier: string): RegisteredApp | undefined;
45
+ /**
46
+ * Update supported locales for an app
47
+ */
48
+ export declare function updateAppSupportedLocales({ identifier, store, locales, }: {
49
+ identifier: string;
50
+ store: "appStore" | "googlePlay";
51
+ locales: string[];
52
+ }): boolean;
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Registered app management
3
+ * Manage app list through ~/.config/pabal-mcp/registered-apps.json file
4
+ */
5
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
6
+ import { join } from "node:path";
7
+ import { AppError } from "../../../packages/common/errors/app-error.js";
8
+ import { ERROR_CODES } from "../../../packages/common/errors/error-codes.js";
9
+ import { getConfigDir } from "../../../packages/configs/secrets-config/config.js";
10
+ // ============================================================================
11
+ // File Paths
12
+ // ============================================================================
13
+ function getRegisteredAppsPath() {
14
+ return join(getConfigDir(), "registered-apps.json");
15
+ }
16
+ // ============================================================================
17
+ // CRUD Functions
18
+ // ============================================================================
19
+ /**
20
+ * Load registered app list
21
+ */
22
+ export function loadRegisteredApps() {
23
+ const filePath = getRegisteredAppsPath();
24
+ console.error(`[RegisteredApps] 🔍 Loading registered apps...`);
25
+ console.error(`[RegisteredApps] File path: ${filePath}`);
26
+ console.error(`[RegisteredApps] File exists: ${existsSync(filePath)}`);
27
+ if (!existsSync(filePath)) {
28
+ console.error(`[RegisteredApps] ⚠️ File not found, returning empty list`);
29
+ return { apps: [] };
30
+ }
31
+ try {
32
+ const content = readFileSync(filePath, "utf-8");
33
+ const config = JSON.parse(content);
34
+ console.error(`[RegisteredApps] ✅ Loaded ${config.apps.length} apps: ${config.apps.map((a) => a.slug).join(", ")}`);
35
+ return config;
36
+ }
37
+ catch (error) {
38
+ throw AppError.validation(ERROR_CODES.REGISTERED_APPS_READ_FAILED, `Failed to load registered apps: ${error instanceof Error ? error.message : String(error)}`, { filePath });
39
+ }
40
+ }
41
+ /**
42
+ * Save registered app list
43
+ */
44
+ export function saveRegisteredApps(config) {
45
+ const filePath = getRegisteredAppsPath();
46
+ try {
47
+ writeFileSync(filePath, JSON.stringify(config, null, 2));
48
+ }
49
+ catch (error) {
50
+ throw AppError.io(ERROR_CODES.REGISTERED_APPS_WRITE_FAILED, `Failed to save registered apps: ${error instanceof Error ? error.message : String(error)}`, { filePath });
51
+ }
52
+ }
53
+ /**
54
+ * Register app
55
+ */
56
+ export function registerApp(app) {
57
+ const config = loadRegisteredApps();
58
+ // Check for duplicates
59
+ const existing = config.apps.find((a) => a.slug === app.slug);
60
+ if (existing) {
61
+ throw AppError.conflict(ERROR_CODES.REGISTERED_APP_DUPLICATE, `App with slug "${app.slug}" already exists`);
62
+ }
63
+ config.apps.push(app);
64
+ saveRegisteredApps(config);
65
+ return app;
66
+ }
67
+ /**
68
+ * Find app (search by slug, bundleId, packageName)
69
+ */
70
+ export function findApp(identifier) {
71
+ const config = loadRegisteredApps();
72
+ return config.apps.find((app) => app.slug === identifier ||
73
+ app.appStore?.bundleId === identifier ||
74
+ app.googlePlay?.packageName === identifier);
75
+ }
76
+ /**
77
+ * Update supported locales for an app
78
+ */
79
+ export function updateAppSupportedLocales({ identifier, store, locales, }) {
80
+ const config = loadRegisteredApps();
81
+ const appIndex = config.apps.findIndex((app) => app.slug === identifier ||
82
+ app.appStore?.bundleId === identifier ||
83
+ app.googlePlay?.packageName === identifier);
84
+ if (appIndex === -1) {
85
+ throw AppError.notFound(ERROR_CODES.REGISTERED_APP_NOT_FOUND, `App "${identifier}" not found in registered apps`);
86
+ }
87
+ const app = config.apps[appIndex];
88
+ // Merge and deduplicate locales
89
+ const existingLocales = store === "appStore"
90
+ ? app.appStore?.supportedLocales || []
91
+ : app.googlePlay?.supportedLocales || [];
92
+ const mergedLocales = Array.from(new Set([...existingLocales, ...locales])).sort();
93
+ // Update the app
94
+ if (store === "appStore") {
95
+ if (!app.appStore) {
96
+ throw AppError.validation(ERROR_CODES.REGISTERED_APP_STORE_INFO_MISSING, `App "${identifier}" is missing App Store info`);
97
+ }
98
+ app.appStore.supportedLocales = mergedLocales;
99
+ }
100
+ else {
101
+ if (!app.googlePlay) {
102
+ throw AppError.validation(ERROR_CODES.REGISTERED_APP_STORE_INFO_MISSING, `App "${identifier}" is missing Google Play info`);
103
+ }
104
+ app.googlePlay.supportedLocales = mergedLocales;
105
+ }
106
+ saveRegisteredApps(config);
107
+ return true;
108
+ }
@@ -0,0 +1,21 @@
1
+ import { z } from "zod";
2
+ export declare const appStoreSchema: z.ZodObject<{
3
+ keyId: z.ZodString;
4
+ issuerId: z.ZodString;
5
+ privateKey: z.ZodString;
6
+ }, "strip", z.ZodTypeAny, {
7
+ keyId: string;
8
+ issuerId: string;
9
+ privateKey: string;
10
+ }, {
11
+ keyId: string;
12
+ issuerId: string;
13
+ privateKey: string;
14
+ }>;
15
+ export declare const playStoreSchema: z.ZodObject<{
16
+ serviceAccountJson: z.ZodString;
17
+ }, "strip", z.ZodTypeAny, {
18
+ serviceAccountJson: string;
19
+ }, {
20
+ serviceAccountJson: string;
21
+ }>;
@@ -0,0 +1,9 @@
1
+ import { z } from "zod";
2
+ export const appStoreSchema = z.object({
3
+ keyId: z.string().min(1),
4
+ issuerId: z.string().min(1),
5
+ privateKey: z.string().min(1), // PEM format string
6
+ });
7
+ export const playStoreSchema = z.object({
8
+ serviceAccountJson: z.string().min(1), // JSON string
9
+ });
@@ -0,0 +1,8 @@
1
+ import { z } from "zod";
2
+ import { appStoreSchema, playStoreSchema } from "./schemas.js";
3
+ export type AppStoreConfig = z.infer<typeof appStoreSchema>;
4
+ export type PlayStoreConfig = z.infer<typeof playStoreSchema>;
5
+ export type EnvConfig = {
6
+ appStore?: AppStoreConfig;
7
+ playStore?: PlayStoreConfig;
8
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,26 @@
1
+ /**
2
+ * App Store Connect API Data Converters
3
+ *
4
+ * Data transformation logic between API responses and internal types
5
+ */
6
+ import type { AppStoreAsoData, AppStoreMultilingualAsoData, AppStoreReleaseNote, AppStoreScreenshots } from "../../../packages/configs/aso-config/types.js";
7
+ import type { ApiResponse, AppInfoLocalization, AppStoreApp, AppStoreLocalization, AppStoreScreenshot, AppStoreScreenshotSet, AppStoreVersion } from "./types.js";
8
+ export declare function sortVersions(versions: AppStoreVersion[]): AppStoreVersion[];
9
+ export declare function selectEnglishAppName(localizations: AppInfoLocalization[]): string | null;
10
+ export declare function mapLocalizationsByLocale<T extends {
11
+ attributes?: {
12
+ locale?: string;
13
+ };
14
+ }>(localizations: T[]): Record<string, T>;
15
+ export declare function fetchScreenshotsForLocalization(localizationId: string | undefined, listScreenshotSets: (localizationId: string) => Promise<ApiResponse<AppStoreScreenshotSet[]>>, listScreenshots: (screenshotSetId: string) => Promise<ApiResponse<AppStoreScreenshot[]>>): Promise<AppStoreScreenshots>;
16
+ export declare function convertToAsoData(params: {
17
+ app: AppStoreApp;
18
+ appInfoLocalization?: AppInfoLocalization | null;
19
+ localization?: AppStoreLocalization | null;
20
+ screenshots: AppStoreScreenshots;
21
+ locale: string;
22
+ bundleId: string;
23
+ }): AppStoreAsoData;
24
+ export declare function convertToMultilingualAsoData(locales: Record<string, AppStoreAsoData>, defaultLocale?: string): AppStoreMultilingualAsoData;
25
+ export declare function convertToReleaseNote(version: AppStoreVersion, localizations: AppStoreLocalization[]): AppStoreReleaseNote | null;
26
+ export declare function sortReleaseNotes(releaseNotes: AppStoreReleaseNote[]): AppStoreReleaseNote[];
@@ -0,0 +1,131 @@
1
+ /**
2
+ * App Store Connect API Data Converters
3
+ *
4
+ * Data transformation logic between API responses and internal types
5
+ */
6
+ import { DEFAULT_LOCALE } from "../../../packages/configs/aso-config/constants.js";
7
+ import { SCREENSHOT_TYPE_MAP } from "./constants.js";
8
+ export function sortVersions(versions) {
9
+ return versions.sort((a, b) => {
10
+ const vA = (a.attributes?.versionString ?? "0").split(".").map(Number);
11
+ const vB = (b.attributes?.versionString ?? "0").split(".").map(Number);
12
+ for (let i = 0; i < Math.max(vA.length, vB.length); i++) {
13
+ const diff = (vB[i] || 0) - (vA[i] || 0);
14
+ if (diff !== 0)
15
+ return diff;
16
+ }
17
+ return 0;
18
+ });
19
+ }
20
+ export function selectEnglishAppName(localizations) {
21
+ const enUS = localizations.find((l) => l.attributes?.locale === "en-US");
22
+ if (enUS?.attributes?.name)
23
+ return enUS.attributes.name;
24
+ const enGB = localizations.find((l) => l.attributes?.locale === "en-GB");
25
+ if (enGB?.attributes?.name)
26
+ return enGB.attributes.name;
27
+ const enAny = localizations.find((l) => l.attributes?.locale?.startsWith("en"));
28
+ if (enAny?.attributes?.name)
29
+ return enAny.attributes.name;
30
+ return null;
31
+ }
32
+ export function mapLocalizationsByLocale(localizations) {
33
+ return localizations.reduce((acc, loc) => {
34
+ const locale = loc.attributes?.locale;
35
+ if (locale)
36
+ acc[locale] = loc;
37
+ return acc;
38
+ }, {});
39
+ }
40
+ export async function fetchScreenshotsForLocalization(localizationId, listScreenshotSets, listScreenshots) {
41
+ const screenshots = {};
42
+ if (!localizationId)
43
+ return screenshots;
44
+ const setsResponse = await listScreenshotSets(localizationId);
45
+ for (const set of setsResponse.data || []) {
46
+ const screenshotsResponse = await listScreenshots(set.id);
47
+ const urls = (screenshotsResponse.data || [])
48
+ .map((s) => s.attributes?.imageAsset?.templateUrl)
49
+ .filter(Boolean);
50
+ if (urls.length > 0) {
51
+ const displayType = set.attributes?.screenshotDisplayType;
52
+ if (displayType) {
53
+ const mappedType = SCREENSHOT_TYPE_MAP[displayType];
54
+ if (mappedType)
55
+ screenshots[mappedType] = urls;
56
+ }
57
+ }
58
+ }
59
+ return screenshots;
60
+ }
61
+ export function convertToAsoData(params) {
62
+ const { app, appInfoLocalization, localization, screenshots, locale, bundleId, } = params;
63
+ return {
64
+ name: appInfoLocalization?.attributes?.name ||
65
+ app.attributes?.name ||
66
+ "Unknown",
67
+ subtitle: appInfoLocalization?.attributes?.subtitle,
68
+ description: localization?.attributes?.description || "",
69
+ keywords: localization?.attributes?.keywords,
70
+ promotionalText: localization?.attributes?.promotionalText,
71
+ screenshots,
72
+ bundleId,
73
+ locale,
74
+ supportUrl: localization?.attributes?.supportUrl,
75
+ marketingUrl: localization?.attributes?.marketingUrl,
76
+ whatsNew: localization?.attributes?.whatsNew,
77
+ };
78
+ }
79
+ export function convertToMultilingualAsoData(locales, defaultLocale) {
80
+ let finalDefaultLocale = defaultLocale;
81
+ if (!finalDefaultLocale && Object.keys(locales).length > 0) {
82
+ if (locales[DEFAULT_LOCALE]) {
83
+ finalDefaultLocale = DEFAULT_LOCALE;
84
+ }
85
+ else {
86
+ finalDefaultLocale = Object.keys(locales)[0];
87
+ }
88
+ }
89
+ return {
90
+ locales,
91
+ defaultLocale: finalDefaultLocale || DEFAULT_LOCALE,
92
+ };
93
+ }
94
+ export function convertToReleaseNote(version, localizations) {
95
+ const releaseNotesMap = {};
96
+ for (const localization of localizations) {
97
+ const locale = localization.attributes?.locale;
98
+ const whatsNew = localization.attributes?.whatsNew;
99
+ if (locale && whatsNew) {
100
+ releaseNotesMap[locale] = whatsNew;
101
+ }
102
+ }
103
+ if (Object.keys(releaseNotesMap).length === 0) {
104
+ return null;
105
+ }
106
+ const versionString = version.attributes?.versionString;
107
+ const platform = version.attributes?.platform;
108
+ if (!versionString || !platform) {
109
+ return null;
110
+ }
111
+ return {
112
+ versionString,
113
+ releaseNotes: releaseNotesMap,
114
+ platform,
115
+ };
116
+ }
117
+ export function sortReleaseNotes(releaseNotes) {
118
+ const versionMap = new Map(releaseNotes.map((note) => [note.versionString, note]));
119
+ const sortedVersions = sortVersions(releaseNotes.map((note) => ({
120
+ type: "appStoreVersions",
121
+ id: note.versionString,
122
+ attributes: {
123
+ versionString: note.versionString,
124
+ platform: note.platform,
125
+ },
126
+ links: { self: note.versionString },
127
+ })));
128
+ return sortedVersions
129
+ .map((version) => versionMap.get(version.attributes?.versionString ?? ""))
130
+ .filter((note) => !!note && !!note.versionString);
131
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * App Store Connect API Endpoints
3
+ *
4
+ * Centralized API endpoint management for App Store Connect
5
+ */
6
+ import type { ApiResponse, AppInfo, AppInfoLocalization, AppInfoLocalizationUpdateAttributes, AppStoreApp, AppStoreLocalization, AppStoreScreenshot, AppStoreScreenshotSet, AppStoreVersion, AppStoreVersionLocalizationUpdateAttributes } from "./types.js";
7
+ export declare class AppStoreApiEndpoints {
8
+ private generateToken;
9
+ private issuerId;
10
+ private keyId;
11
+ constructor(generateToken: () => Promise<string>, issuerId: string, keyId: string);
12
+ normalizeNextLink(nextLink?: string | null): string | null;
13
+ private request;
14
+ listApps(nextUrl?: string): Promise<ApiResponse<AppStoreApp[]>>;
15
+ findAppByBundleId(bundleId: string): Promise<ApiResponse<AppStoreApp[]>>;
16
+ getApp(appId: string): Promise<ApiResponse<AppStoreApp>>;
17
+ listAppInfos(appId: string): Promise<ApiResponse<AppInfo[]>>;
18
+ listAppInfoLocalizations(appInfoId: string, locale?: string): Promise<ApiResponse<AppInfoLocalization[]>>;
19
+ updateAppInfoLocalization(localizationId: string, attributes: AppInfoLocalizationUpdateAttributes): Promise<void>;
20
+ createAppInfoLocalization(appInfoId: string, locale: string, attributes: AppInfoLocalizationUpdateAttributes): Promise<ApiResponse<AppInfoLocalization>>;
21
+ listAppStoreVersions(appId: string, options?: {
22
+ platform?: string;
23
+ state?: string;
24
+ limit?: number;
25
+ }): Promise<ApiResponse<AppStoreVersion[]>>;
26
+ createAppStoreVersion(appId: string, versionString: string, platform?: string): Promise<ApiResponse<AppStoreVersion>>;
27
+ listAppStoreVersionLocalizations(versionId: string, locale?: string): Promise<ApiResponse<AppStoreLocalization[]>>;
28
+ getAppStoreVersionLocalization(localizationId: string): Promise<ApiResponse<AppStoreLocalization>>;
29
+ updateAppStoreVersionLocalization(localizationId: string, attributes: AppStoreVersionLocalizationUpdateAttributes): Promise<void>;
30
+ createAppStoreVersionLocalization(versionId: string, locale: string, attributes: AppStoreVersionLocalizationUpdateAttributes): Promise<ApiResponse<AppStoreLocalization>>;
31
+ listScreenshotSets(localizationId: string): Promise<ApiResponse<AppStoreScreenshotSet[]>>;
32
+ listScreenshots(screenshotSetId: string): Promise<ApiResponse<AppStoreScreenshot[]>>;
33
+ }