pabal-web-mcp 1.2.3 → 1.3.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.
@@ -0,0 +1,351 @@
1
+ import {
2
+ DEFAULT_LOCALE,
3
+ isAppStoreLocale,
4
+ isGooglePlayLocale,
5
+ isSupportedLocale
6
+ } from "./chunk-BOWRBVVV.js";
7
+
8
+ // src/utils/config.util.ts
9
+ import fs from "fs";
10
+ import path from "path";
11
+ import os from "os";
12
+ function getAsoDataDir() {
13
+ const configPath = path.join(
14
+ os.homedir(),
15
+ ".config",
16
+ "pabal-mcp",
17
+ "config.json"
18
+ );
19
+ if (!fs.existsSync(configPath)) {
20
+ throw new Error(
21
+ `Config file not found at ${configPath}. Please create the config file and set the 'dataDir' property to specify the ASO data directory.`
22
+ );
23
+ }
24
+ try {
25
+ const configContent = fs.readFileSync(configPath, "utf-8");
26
+ const config = JSON.parse(configContent);
27
+ if (!config.dataDir) {
28
+ throw new Error(
29
+ `'dataDir' property is not set in ${configPath}. Please set 'dataDir' to specify the ASO data directory.`
30
+ );
31
+ }
32
+ if (path.isAbsolute(config.dataDir)) {
33
+ return config.dataDir;
34
+ }
35
+ return path.resolve(os.homedir(), config.dataDir);
36
+ } catch (error) {
37
+ if (error instanceof Error && error.message.includes("dataDir")) {
38
+ throw error;
39
+ }
40
+ throw new Error(
41
+ `Failed to read config from ${configPath}: ${error instanceof Error ? error.message : String(error)}`
42
+ );
43
+ }
44
+ }
45
+ function getPullDataDir() {
46
+ return path.join(getAsoDataDir(), ".aso", "pullData");
47
+ }
48
+ function getPushDataDir() {
49
+ return path.join(getAsoDataDir(), ".aso", "pushData");
50
+ }
51
+ function getPublicDir() {
52
+ return path.join(getAsoDataDir(), "public");
53
+ }
54
+ function getProductsDir() {
55
+ return path.join(getPublicDir(), "products");
56
+ }
57
+
58
+ // src/utils/aso-converter.ts
59
+ import fs2 from "fs";
60
+ import path2 from "path";
61
+ function generateFullDescription(localeData, metadata = {}) {
62
+ const { aso, landing } = localeData;
63
+ const template = aso?.template;
64
+ if (!template) {
65
+ return "";
66
+ }
67
+ const landingFeatures = landing?.features?.items || [];
68
+ const landingScreenshots = landing?.screenshots?.images || [];
69
+ const keyHeading = template.keyFeaturesHeading || "Key Features";
70
+ const featuresHeading = template.featuresHeading || "Additional Features";
71
+ const parts = [template.intro];
72
+ if (landingFeatures.length > 0) {
73
+ parts.push(
74
+ "",
75
+ keyHeading,
76
+ "",
77
+ ...landingFeatures.map(
78
+ (feature) => [`\u25B6\uFE0E ${feature.title}`, feature.body || ""].filter(Boolean).join("\n")
79
+ )
80
+ );
81
+ }
82
+ if (landingScreenshots.length > 0) {
83
+ parts.push("", featuresHeading, "");
84
+ parts.push(
85
+ ...landingScreenshots.map(
86
+ (screenshot) => [`\u25B6\uFE0E ${screenshot.title}`, screenshot.description || ""].filter(Boolean).join("\n")
87
+ )
88
+ );
89
+ }
90
+ parts.push("", template.outro);
91
+ const includeSupport = template.includeSupportLinks ?? true;
92
+ if (includeSupport) {
93
+ const contactLines = [
94
+ metadata.instagram ? `Instagram: ${metadata.instagram}` : null,
95
+ metadata.contactEmail ? `Email: ${metadata.contactEmail}` : null,
96
+ metadata.termsUrl ? `- Terms of Use: ${metadata.termsUrl}` : null,
97
+ metadata.privacyUrl ? `- Privacy Policy: ${metadata.privacyUrl}` : null
98
+ ].filter((line) => line !== null);
99
+ if (contactLines.length > 0) {
100
+ parts.push("", "[Contact & Support]", "", ...contactLines);
101
+ }
102
+ }
103
+ return parts.join("\n");
104
+ }
105
+ function loadAsoFromConfig(slug) {
106
+ const productsDir = getProductsDir();
107
+ const configPath = path2.join(productsDir, slug, "config.json");
108
+ console.debug(`[loadAsoFromConfig] Looking for ${slug}:`);
109
+ console.debug(` - productsDir: ${productsDir}`);
110
+ console.debug(` - configPath: ${configPath}`);
111
+ console.debug(` - configPath exists: ${fs2.existsSync(configPath)}`);
112
+ if (!fs2.existsSync(configPath)) {
113
+ console.warn(`[loadAsoFromConfig] Config file not found at ${configPath}`);
114
+ return {};
115
+ }
116
+ try {
117
+ const configContent = fs2.readFileSync(configPath, "utf-8");
118
+ const config = JSON.parse(configContent);
119
+ const localesDir = path2.join(productsDir, slug, "locales");
120
+ console.debug(` - localesDir: ${localesDir}`);
121
+ console.debug(` - localesDir exists: ${fs2.existsSync(localesDir)}`);
122
+ if (!fs2.existsSync(localesDir)) {
123
+ console.warn(
124
+ `[loadAsoFromConfig] Locales directory not found at ${localesDir}`
125
+ );
126
+ return {};
127
+ }
128
+ const localeFiles = fs2.readdirSync(localesDir).filter((f) => f.endsWith(".json"));
129
+ const locales = {};
130
+ for (const file of localeFiles) {
131
+ const localeCode = file.replace(".json", "");
132
+ const localePath = path2.join(localesDir, file);
133
+ const localeContent = fs2.readFileSync(localePath, "utf-8");
134
+ locales[localeCode] = JSON.parse(localeContent);
135
+ }
136
+ console.debug(
137
+ ` - Found ${Object.keys(locales).length} locale file(s): ${Object.keys(
138
+ locales
139
+ ).join(", ")}`
140
+ );
141
+ if (Object.keys(locales).length === 0) {
142
+ console.warn(
143
+ `[loadAsoFromConfig] No locale files found in ${localesDir}`
144
+ );
145
+ }
146
+ const defaultLocale = config.content?.defaultLocale || DEFAULT_LOCALE;
147
+ const asoData = {};
148
+ if (config.packageName) {
149
+ const googlePlayLocales = {};
150
+ const metadata = config.metadata || {};
151
+ const screenshots = metadata.screenshots || {};
152
+ for (const [locale, localeData] of Object.entries(locales)) {
153
+ if (!isSupportedLocale(locale)) {
154
+ console.debug(
155
+ `Skipping locale ${locale} - not a valid unified locale`
156
+ );
157
+ continue;
158
+ }
159
+ if (!isGooglePlayLocale(locale)) {
160
+ console.debug(
161
+ `Skipping locale ${locale} - not supported by Google Play`
162
+ );
163
+ continue;
164
+ }
165
+ const aso = localeData.aso || {};
166
+ if (!aso || !aso.title && !aso.shortDescription) {
167
+ console.warn(
168
+ `Locale ${locale} has no ASO data (title or shortDescription)`
169
+ );
170
+ }
171
+ const localeScreenshots = {
172
+ phone: screenshots.phone?.map(
173
+ (p) => p.replace("/screenshots/", `/screenshots/${locale}/`)
174
+ ),
175
+ tablet: screenshots.tablet?.map(
176
+ (p) => p.replace("/screenshots/", `/screenshots/${locale}/`)
177
+ )
178
+ };
179
+ googlePlayLocales[locale] = {
180
+ title: aso.title || "",
181
+ shortDescription: aso.shortDescription || "",
182
+ fullDescription: generateFullDescription(localeData, metadata),
183
+ packageName: config.packageName,
184
+ defaultLanguage: locale,
185
+ screenshots: {
186
+ phone: localeScreenshots.phone || [],
187
+ tablet: localeScreenshots.tablet
188
+ },
189
+ contactEmail: metadata.contactEmail
190
+ };
191
+ }
192
+ const googleLocaleKeys = Object.keys(googlePlayLocales);
193
+ if (googleLocaleKeys.length > 0) {
194
+ const hasConfigDefault = isGooglePlayLocale(defaultLocale) && Boolean(googlePlayLocales[defaultLocale]);
195
+ const resolvedDefault = hasConfigDefault ? defaultLocale : googlePlayLocales[DEFAULT_LOCALE] ? DEFAULT_LOCALE : googleLocaleKeys[0];
196
+ asoData.googlePlay = {
197
+ locales: googlePlayLocales,
198
+ defaultLocale: resolvedDefault
199
+ };
200
+ }
201
+ }
202
+ if (config.bundleId) {
203
+ const appStoreLocales = {};
204
+ const metadata = config.metadata || {};
205
+ const screenshots = metadata.screenshots || {};
206
+ for (const [locale, localeData] of Object.entries(locales)) {
207
+ if (!isSupportedLocale(locale)) {
208
+ console.debug(
209
+ `Skipping locale ${locale} - not a valid unified locale`
210
+ );
211
+ continue;
212
+ }
213
+ if (!isAppStoreLocale(locale)) {
214
+ console.debug(
215
+ `Skipping locale ${locale} - not supported by App Store`
216
+ );
217
+ continue;
218
+ }
219
+ const aso = localeData.aso || {};
220
+ if (!aso || !aso.title && !aso.shortDescription) {
221
+ console.warn(
222
+ `Locale ${locale} has no ASO data (title or shortDescription)`
223
+ );
224
+ }
225
+ const localeScreenshots = {
226
+ phone: screenshots.phone?.map(
227
+ (p) => p.replace("/screenshots/", `/screenshots/${locale}/`)
228
+ ),
229
+ tablet: screenshots.tablet?.map(
230
+ (p) => p.replace("/screenshots/", `/screenshots/${locale}/`)
231
+ )
232
+ };
233
+ appStoreLocales[locale] = {
234
+ name: aso.title || "",
235
+ subtitle: aso.subtitle,
236
+ description: generateFullDescription(localeData, metadata),
237
+ keywords: Array.isArray(aso.keywords) ? aso.keywords.join(", ") : aso.keywords,
238
+ promotionalText: void 0,
239
+ bundleId: config.bundleId,
240
+ locale,
241
+ supportUrl: metadata.supportUrl,
242
+ marketingUrl: metadata.marketingUrl,
243
+ privacyPolicyUrl: metadata.privacyUrl,
244
+ screenshots: {
245
+ // 폰 스크린샷을 iphone65로 매핑
246
+ iphone65: localeScreenshots.phone || [],
247
+ // 태블릿 스크린샷을 ipadPro129로 매핑
248
+ ipadPro129: localeScreenshots.tablet
249
+ }
250
+ };
251
+ }
252
+ const appStoreLocaleKeys = Object.keys(appStoreLocales);
253
+ if (appStoreLocaleKeys.length > 0) {
254
+ const hasConfigDefault = isAppStoreLocale(defaultLocale) && Boolean(appStoreLocales[defaultLocale]);
255
+ const resolvedDefault = hasConfigDefault ? defaultLocale : appStoreLocales[DEFAULT_LOCALE] ? DEFAULT_LOCALE : appStoreLocaleKeys[0];
256
+ asoData.appStore = {
257
+ locales: appStoreLocales,
258
+ defaultLocale: resolvedDefault
259
+ };
260
+ }
261
+ }
262
+ const hasGooglePlay = !!asoData.googlePlay;
263
+ const hasAppStore = !!asoData.appStore;
264
+ console.debug(`[loadAsoFromConfig] Result for ${slug}:`);
265
+ console.debug(
266
+ ` - Google Play data: ${hasGooglePlay ? "found" : "not found"}`
267
+ );
268
+ console.debug(` - App Store data: ${hasAppStore ? "found" : "not found"}`);
269
+ if (!hasGooglePlay && !hasAppStore) {
270
+ console.warn(`[loadAsoFromConfig] No ASO data generated for ${slug}`);
271
+ }
272
+ return asoData;
273
+ } catch (error) {
274
+ console.error(
275
+ `[loadAsoFromConfig] Failed to load ASO data from config for ${slug}:`,
276
+ error
277
+ );
278
+ return {};
279
+ }
280
+ }
281
+ function saveAsoToConfig(slug, config) {
282
+ const productsDir = getProductsDir();
283
+ const configPath = path2.join(productsDir, slug, "config.json");
284
+ fs2.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
285
+ }
286
+ function saveAsoToAsoDir(slug, asoData) {
287
+ const rootDir = getPushDataDir();
288
+ if (asoData.googlePlay) {
289
+ const asoPath = path2.join(
290
+ rootDir,
291
+ "products",
292
+ slug,
293
+ "store",
294
+ "google-play",
295
+ "aso-data.json"
296
+ );
297
+ const dir = path2.dirname(asoPath);
298
+ if (!fs2.existsSync(dir)) {
299
+ fs2.mkdirSync(dir, { recursive: true });
300
+ }
301
+ const googlePlayData = asoData.googlePlay;
302
+ const multilingualData = "locales" in googlePlayData ? googlePlayData : {
303
+ locales: {
304
+ [googlePlayData.defaultLanguage || DEFAULT_LOCALE]: googlePlayData
305
+ },
306
+ defaultLocale: googlePlayData.defaultLanguage || DEFAULT_LOCALE
307
+ };
308
+ fs2.writeFileSync(
309
+ asoPath,
310
+ JSON.stringify({ googlePlay: multilingualData }, null, 2) + "\n",
311
+ "utf-8"
312
+ );
313
+ }
314
+ if (asoData.appStore) {
315
+ const asoPath = path2.join(
316
+ rootDir,
317
+ "products",
318
+ slug,
319
+ "store",
320
+ "app-store",
321
+ "aso-data.json"
322
+ );
323
+ const dir = path2.dirname(asoPath);
324
+ if (!fs2.existsSync(dir)) {
325
+ fs2.mkdirSync(dir, { recursive: true });
326
+ }
327
+ const appStoreData = asoData.appStore;
328
+ const multilingualData = "locales" in appStoreData ? appStoreData : {
329
+ locales: {
330
+ [appStoreData.locale || DEFAULT_LOCALE]: appStoreData
331
+ },
332
+ defaultLocale: appStoreData.locale || DEFAULT_LOCALE
333
+ };
334
+ fs2.writeFileSync(
335
+ asoPath,
336
+ JSON.stringify({ appStore: multilingualData }, null, 2) + "\n",
337
+ "utf-8"
338
+ );
339
+ }
340
+ }
341
+
342
+ export {
343
+ getAsoDataDir,
344
+ getPullDataDir,
345
+ getPushDataDir,
346
+ getPublicDir,
347
+ getProductsDir,
348
+ loadAsoFromConfig,
349
+ saveAsoToConfig,
350
+ saveAsoToAsoDir
351
+ };
@@ -0,0 +1,355 @@
1
+ import {
2
+ DEFAULT_LOCALE,
3
+ isAppStoreLocale,
4
+ isGooglePlayLocale,
5
+ isSupportedLocale
6
+ } from "./chunk-BOWRBVVV.js";
7
+
8
+ // src/utils/config.util.ts
9
+ import fs from "fs";
10
+ import path from "path";
11
+ import os from "os";
12
+ function getAsoDataDir() {
13
+ const configPath = path.join(
14
+ os.homedir(),
15
+ ".config",
16
+ "pabal-mcp",
17
+ "config.json"
18
+ );
19
+ if (!fs.existsSync(configPath)) {
20
+ throw new Error(
21
+ `Config file not found at ${configPath}. Please create the config file and set the 'dataDir' property to specify the ASO data directory.`
22
+ );
23
+ }
24
+ try {
25
+ const configContent = fs.readFileSync(configPath, "utf-8");
26
+ const config = JSON.parse(configContent);
27
+ if (!config.dataDir) {
28
+ throw new Error(
29
+ `'dataDir' property is not set in ${configPath}. Please set 'dataDir' to specify the ASO data directory.`
30
+ );
31
+ }
32
+ if (path.isAbsolute(config.dataDir)) {
33
+ return config.dataDir;
34
+ }
35
+ return path.resolve(os.homedir(), config.dataDir);
36
+ } catch (error) {
37
+ if (error instanceof Error && error.message.includes("dataDir")) {
38
+ throw error;
39
+ }
40
+ throw new Error(
41
+ `Failed to read config from ${configPath}: ${error instanceof Error ? error.message : String(error)}`
42
+ );
43
+ }
44
+ }
45
+ function getPullDataDir() {
46
+ return path.join(getAsoDataDir(), ".aso", "pullData");
47
+ }
48
+ function getPushDataDir() {
49
+ return path.join(getAsoDataDir(), ".aso", "pushData");
50
+ }
51
+ function getPublicDir() {
52
+ return path.join(getAsoDataDir(), "public");
53
+ }
54
+ function getKeywordResearchDir() {
55
+ return path.join(getAsoDataDir(), ".aso", "keywordResearch");
56
+ }
57
+ function getProductsDir() {
58
+ return path.join(getPublicDir(), "products");
59
+ }
60
+
61
+ // src/utils/aso-converter.ts
62
+ import fs2 from "fs";
63
+ import path2 from "path";
64
+ function generateFullDescription(localeData, metadata = {}) {
65
+ const { aso, landing } = localeData;
66
+ const template = aso?.template;
67
+ if (!template) {
68
+ return "";
69
+ }
70
+ const landingFeatures = landing?.features?.items || [];
71
+ const landingScreenshots = landing?.screenshots?.images || [];
72
+ const keyHeading = template.keyFeaturesHeading || "Key Features";
73
+ const featuresHeading = template.featuresHeading || "Additional Features";
74
+ const parts = [template.intro];
75
+ if (landingFeatures.length > 0) {
76
+ parts.push(
77
+ "",
78
+ keyHeading,
79
+ "",
80
+ ...landingFeatures.map(
81
+ (feature) => [`\u25B6\uFE0E ${feature.title}`, feature.body || ""].filter(Boolean).join("\n")
82
+ )
83
+ );
84
+ }
85
+ if (landingScreenshots.length > 0) {
86
+ parts.push("", featuresHeading, "");
87
+ parts.push(
88
+ ...landingScreenshots.map(
89
+ (screenshot) => [`\u25B6\uFE0E ${screenshot.title}`, screenshot.description || ""].filter(Boolean).join("\n")
90
+ )
91
+ );
92
+ }
93
+ parts.push("", template.outro);
94
+ const includeSupport = template.includeSupportLinks ?? true;
95
+ if (includeSupport) {
96
+ const contactLines = [
97
+ metadata.instagram ? `Instagram: ${metadata.instagram}` : null,
98
+ metadata.contactEmail ? `Email: ${metadata.contactEmail}` : null,
99
+ metadata.termsUrl ? `- Terms of Use: ${metadata.termsUrl}` : null,
100
+ metadata.privacyUrl ? `- Privacy Policy: ${metadata.privacyUrl}` : null
101
+ ].filter((line) => line !== null);
102
+ if (contactLines.length > 0) {
103
+ parts.push("", "[Contact & Support]", "", ...contactLines);
104
+ }
105
+ }
106
+ return parts.join("\n");
107
+ }
108
+ function loadAsoFromConfig(slug) {
109
+ const productsDir = getProductsDir();
110
+ const configPath = path2.join(productsDir, slug, "config.json");
111
+ console.debug(`[loadAsoFromConfig] Looking for ${slug}:`);
112
+ console.debug(` - productsDir: ${productsDir}`);
113
+ console.debug(` - configPath: ${configPath}`);
114
+ console.debug(` - configPath exists: ${fs2.existsSync(configPath)}`);
115
+ if (!fs2.existsSync(configPath)) {
116
+ console.warn(`[loadAsoFromConfig] Config file not found at ${configPath}`);
117
+ return {};
118
+ }
119
+ try {
120
+ const configContent = fs2.readFileSync(configPath, "utf-8");
121
+ const config = JSON.parse(configContent);
122
+ const localesDir = path2.join(productsDir, slug, "locales");
123
+ console.debug(` - localesDir: ${localesDir}`);
124
+ console.debug(` - localesDir exists: ${fs2.existsSync(localesDir)}`);
125
+ if (!fs2.existsSync(localesDir)) {
126
+ console.warn(
127
+ `[loadAsoFromConfig] Locales directory not found at ${localesDir}`
128
+ );
129
+ return {};
130
+ }
131
+ const localeFiles = fs2.readdirSync(localesDir).filter((f) => f.endsWith(".json"));
132
+ const locales = {};
133
+ for (const file of localeFiles) {
134
+ const localeCode = file.replace(".json", "");
135
+ const localePath = path2.join(localesDir, file);
136
+ const localeContent = fs2.readFileSync(localePath, "utf-8");
137
+ locales[localeCode] = JSON.parse(localeContent);
138
+ }
139
+ console.debug(
140
+ ` - Found ${Object.keys(locales).length} locale file(s): ${Object.keys(
141
+ locales
142
+ ).join(", ")}`
143
+ );
144
+ if (Object.keys(locales).length === 0) {
145
+ console.warn(
146
+ `[loadAsoFromConfig] No locale files found in ${localesDir}`
147
+ );
148
+ }
149
+ const defaultLocale = config.content?.defaultLocale || DEFAULT_LOCALE;
150
+ const asoData = {};
151
+ if (config.packageName) {
152
+ const googlePlayLocales = {};
153
+ const metadata = config.metadata || {};
154
+ const screenshots = metadata.screenshots || {};
155
+ for (const [locale, localeData] of Object.entries(locales)) {
156
+ if (!isSupportedLocale(locale)) {
157
+ console.debug(
158
+ `Skipping locale ${locale} - not a valid unified locale`
159
+ );
160
+ continue;
161
+ }
162
+ if (!isGooglePlayLocale(locale)) {
163
+ console.debug(
164
+ `Skipping locale ${locale} - not supported by Google Play`
165
+ );
166
+ continue;
167
+ }
168
+ const aso = localeData.aso || {};
169
+ if (!aso || !aso.title && !aso.shortDescription) {
170
+ console.warn(
171
+ `Locale ${locale} has no ASO data (title or shortDescription)`
172
+ );
173
+ }
174
+ const localeScreenshots = {
175
+ phone: screenshots.phone?.map(
176
+ (p) => p.replace("/screenshots/", `/screenshots/${locale}/`)
177
+ ),
178
+ tablet: screenshots.tablet?.map(
179
+ (p) => p.replace("/screenshots/", `/screenshots/${locale}/`)
180
+ )
181
+ };
182
+ googlePlayLocales[locale] = {
183
+ title: aso.title || "",
184
+ shortDescription: aso.shortDescription || "",
185
+ fullDescription: generateFullDescription(localeData, metadata),
186
+ packageName: config.packageName,
187
+ defaultLanguage: locale,
188
+ screenshots: {
189
+ phone: localeScreenshots.phone || [],
190
+ tablet: localeScreenshots.tablet
191
+ },
192
+ contactEmail: metadata.contactEmail
193
+ };
194
+ }
195
+ const googleLocaleKeys = Object.keys(googlePlayLocales);
196
+ if (googleLocaleKeys.length > 0) {
197
+ const hasConfigDefault = isGooglePlayLocale(defaultLocale) && Boolean(googlePlayLocales[defaultLocale]);
198
+ const resolvedDefault = hasConfigDefault ? defaultLocale : googlePlayLocales[DEFAULT_LOCALE] ? DEFAULT_LOCALE : googleLocaleKeys[0];
199
+ asoData.googlePlay = {
200
+ locales: googlePlayLocales,
201
+ defaultLocale: resolvedDefault
202
+ };
203
+ }
204
+ }
205
+ if (config.bundleId) {
206
+ const appStoreLocales = {};
207
+ const metadata = config.metadata || {};
208
+ const screenshots = metadata.screenshots || {};
209
+ for (const [locale, localeData] of Object.entries(locales)) {
210
+ if (!isSupportedLocale(locale)) {
211
+ console.debug(
212
+ `Skipping locale ${locale} - not a valid unified locale`
213
+ );
214
+ continue;
215
+ }
216
+ if (!isAppStoreLocale(locale)) {
217
+ console.debug(
218
+ `Skipping locale ${locale} - not supported by App Store`
219
+ );
220
+ continue;
221
+ }
222
+ const aso = localeData.aso || {};
223
+ if (!aso || !aso.title && !aso.shortDescription) {
224
+ console.warn(
225
+ `Locale ${locale} has no ASO data (title or shortDescription)`
226
+ );
227
+ }
228
+ const localeScreenshots = {
229
+ phone: screenshots.phone?.map(
230
+ (p) => p.replace("/screenshots/", `/screenshots/${locale}/`)
231
+ ),
232
+ tablet: screenshots.tablet?.map(
233
+ (p) => p.replace("/screenshots/", `/screenshots/${locale}/`)
234
+ )
235
+ };
236
+ appStoreLocales[locale] = {
237
+ name: aso.title || "",
238
+ subtitle: aso.subtitle,
239
+ description: generateFullDescription(localeData, metadata),
240
+ keywords: Array.isArray(aso.keywords) ? aso.keywords.join(", ") : aso.keywords,
241
+ promotionalText: void 0,
242
+ bundleId: config.bundleId,
243
+ locale,
244
+ supportUrl: metadata.supportUrl,
245
+ marketingUrl: metadata.marketingUrl,
246
+ privacyPolicyUrl: metadata.privacyUrl,
247
+ screenshots: {
248
+ // 폰 스크린샷을 iphone65로 매핑
249
+ iphone65: localeScreenshots.phone || [],
250
+ // 태블릿 스크린샷을 ipadPro129로 매핑
251
+ ipadPro129: localeScreenshots.tablet
252
+ }
253
+ };
254
+ }
255
+ const appStoreLocaleKeys = Object.keys(appStoreLocales);
256
+ if (appStoreLocaleKeys.length > 0) {
257
+ const hasConfigDefault = isAppStoreLocale(defaultLocale) && Boolean(appStoreLocales[defaultLocale]);
258
+ const resolvedDefault = hasConfigDefault ? defaultLocale : appStoreLocales[DEFAULT_LOCALE] ? DEFAULT_LOCALE : appStoreLocaleKeys[0];
259
+ asoData.appStore = {
260
+ locales: appStoreLocales,
261
+ defaultLocale: resolvedDefault
262
+ };
263
+ }
264
+ }
265
+ const hasGooglePlay = !!asoData.googlePlay;
266
+ const hasAppStore = !!asoData.appStore;
267
+ console.debug(`[loadAsoFromConfig] Result for ${slug}:`);
268
+ console.debug(
269
+ ` - Google Play data: ${hasGooglePlay ? "found" : "not found"}`
270
+ );
271
+ console.debug(` - App Store data: ${hasAppStore ? "found" : "not found"}`);
272
+ if (!hasGooglePlay && !hasAppStore) {
273
+ console.warn(`[loadAsoFromConfig] No ASO data generated for ${slug}`);
274
+ }
275
+ return asoData;
276
+ } catch (error) {
277
+ console.error(
278
+ `[loadAsoFromConfig] Failed to load ASO data from config for ${slug}:`,
279
+ error
280
+ );
281
+ return {};
282
+ }
283
+ }
284
+ function saveAsoToConfig(slug, config) {
285
+ const productsDir = getProductsDir();
286
+ const configPath = path2.join(productsDir, slug, "config.json");
287
+ fs2.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
288
+ }
289
+ function saveAsoToAsoDir(slug, asoData) {
290
+ const rootDir = getPushDataDir();
291
+ if (asoData.googlePlay) {
292
+ const asoPath = path2.join(
293
+ rootDir,
294
+ "products",
295
+ slug,
296
+ "store",
297
+ "google-play",
298
+ "aso-data.json"
299
+ );
300
+ const dir = path2.dirname(asoPath);
301
+ if (!fs2.existsSync(dir)) {
302
+ fs2.mkdirSync(dir, { recursive: true });
303
+ }
304
+ const googlePlayData = asoData.googlePlay;
305
+ const multilingualData = "locales" in googlePlayData ? googlePlayData : {
306
+ locales: {
307
+ [googlePlayData.defaultLanguage || DEFAULT_LOCALE]: googlePlayData
308
+ },
309
+ defaultLocale: googlePlayData.defaultLanguage || DEFAULT_LOCALE
310
+ };
311
+ fs2.writeFileSync(
312
+ asoPath,
313
+ JSON.stringify({ googlePlay: multilingualData }, null, 2) + "\n",
314
+ "utf-8"
315
+ );
316
+ }
317
+ if (asoData.appStore) {
318
+ const asoPath = path2.join(
319
+ rootDir,
320
+ "products",
321
+ slug,
322
+ "store",
323
+ "app-store",
324
+ "aso-data.json"
325
+ );
326
+ const dir = path2.dirname(asoPath);
327
+ if (!fs2.existsSync(dir)) {
328
+ fs2.mkdirSync(dir, { recursive: true });
329
+ }
330
+ const appStoreData = asoData.appStore;
331
+ const multilingualData = "locales" in appStoreData ? appStoreData : {
332
+ locales: {
333
+ [appStoreData.locale || DEFAULT_LOCALE]: appStoreData
334
+ },
335
+ defaultLocale: appStoreData.locale || DEFAULT_LOCALE
336
+ };
337
+ fs2.writeFileSync(
338
+ asoPath,
339
+ JSON.stringify({ appStore: multilingualData }, null, 2) + "\n",
340
+ "utf-8"
341
+ );
342
+ }
343
+ }
344
+
345
+ export {
346
+ getAsoDataDir,
347
+ getPullDataDir,
348
+ getPushDataDir,
349
+ getPublicDir,
350
+ getKeywordResearchDir,
351
+ getProductsDir,
352
+ loadAsoFromConfig,
353
+ saveAsoToConfig,
354
+ saveAsoToAsoDir
355
+ };