@mathrunet/masamune_firebase_workflow_marketing 3.8.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 (146) hide show
  1. package/CHANGELOG.md +361 -0
  2. package/LICENSE +21 -0
  3. package/README.md +70 -0
  4. package/assets/fonts/NotoSans-Bold.ttf +1439 -0
  5. package/assets/fonts/NotoSans-Regular.ttf +1439 -0
  6. package/assets/fonts/NotoSans.ttf +0 -0
  7. package/assets/fonts/NotoSansJP-Bold.ttf +0 -0
  8. package/assets/fonts/NotoSansJP-Regular.ttf +0 -0
  9. package/assets/fonts/NotoSansKR-Bold.ttf +1439 -0
  10. package/assets/fonts/NotoSansKR-Regular.ttf +1439 -0
  11. package/assets/fonts/NotoSansKR.ttf +0 -0
  12. package/assets/fonts/NotoSansSC-Bold.ttf +1439 -0
  13. package/assets/fonts/NotoSansSC-Regular.ttf +1439 -0
  14. package/assets/fonts/NotoSansSC.ttf +0 -0
  15. package/dist/clients/app_store_client.d.ts +54 -0
  16. package/dist/clients/app_store_client.js +280 -0
  17. package/dist/clients/app_store_client.js.map +1 -0
  18. package/dist/clients/firebase_analytics_client.d.ts +67 -0
  19. package/dist/clients/firebase_analytics_client.js +411 -0
  20. package/dist/clients/firebase_analytics_client.js.map +1 -0
  21. package/dist/clients/github_content_client.d.ts +83 -0
  22. package/dist/clients/github_content_client.js +443 -0
  23. package/dist/clients/github_content_client.js.map +1 -0
  24. package/dist/clients/google_play_client.d.ts +49 -0
  25. package/dist/clients/google_play_client.js +275 -0
  26. package/dist/clients/google_play_client.js.map +1 -0
  27. package/dist/functions/analyze_github_init.d.ts +37 -0
  28. package/dist/functions/analyze_github_init.js +299 -0
  29. package/dist/functions/analyze_github_init.js.map +1 -0
  30. package/dist/functions/analyze_github_process.d.ts +25 -0
  31. package/dist/functions/analyze_github_process.js +268 -0
  32. package/dist/functions/analyze_github_process.js.map +1 -0
  33. package/dist/functions/analyze_github_summary.d.ts +32 -0
  34. package/dist/functions/analyze_github_summary.js +270 -0
  35. package/dist/functions/analyze_github_summary.js.map +1 -0
  36. package/dist/functions/analyze_market_research.d.ts +40 -0
  37. package/dist/functions/analyze_market_research.js +346 -0
  38. package/dist/functions/analyze_market_research.js.map +1 -0
  39. package/dist/functions/analyze_marketing_data.d.ts +96 -0
  40. package/dist/functions/analyze_marketing_data.js +990 -0
  41. package/dist/functions/analyze_marketing_data.js.map +1 -0
  42. package/dist/functions/collect_from_app_store.d.ts +25 -0
  43. package/dist/functions/collect_from_app_store.js +148 -0
  44. package/dist/functions/collect_from_app_store.js.map +1 -0
  45. package/dist/functions/collect_from_firebase_analytics.d.ts +25 -0
  46. package/dist/functions/collect_from_firebase_analytics.js +142 -0
  47. package/dist/functions/collect_from_firebase_analytics.js.map +1 -0
  48. package/dist/functions/collect_from_google_play_console.d.ts +25 -0
  49. package/dist/functions/collect_from_google_play_console.js +142 -0
  50. package/dist/functions/collect_from_google_play_console.js.map +1 -0
  51. package/dist/functions/generate_marketing_markdown.d.ts +89 -0
  52. package/dist/functions/generate_marketing_markdown.js +653 -0
  53. package/dist/functions/generate_marketing_markdown.js.map +1 -0
  54. package/dist/functions/generate_marketing_pdf.d.ts +25 -0
  55. package/dist/functions/generate_marketing_pdf.js +175 -0
  56. package/dist/functions/generate_marketing_pdf.js.map +1 -0
  57. package/dist/functions/research_market.d.ts +56 -0
  58. package/dist/functions/research_market.js +434 -0
  59. package/dist/functions/research_market.js.map +1 -0
  60. package/dist/functions.d.ts +571 -0
  61. package/dist/functions.js +648 -0
  62. package/dist/functions.js.map +1 -0
  63. package/dist/index.d.ts +13 -0
  64. package/dist/index.js +30 -0
  65. package/dist/index.js.map +1 -0
  66. package/dist/locales/de_DE.d.ts +5 -0
  67. package/dist/locales/de_DE.js +111 -0
  68. package/dist/locales/de_DE.js.map +1 -0
  69. package/dist/locales/en.d.ts +5 -0
  70. package/dist/locales/en.js +111 -0
  71. package/dist/locales/en.js.map +1 -0
  72. package/dist/locales/es_ES.d.ts +5 -0
  73. package/dist/locales/es_ES.js +111 -0
  74. package/dist/locales/es_ES.js.map +1 -0
  75. package/dist/locales/fr_FR.d.ts +5 -0
  76. package/dist/locales/fr_FR.js +111 -0
  77. package/dist/locales/fr_FR.js.map +1 -0
  78. package/dist/locales/id_ID.d.ts +5 -0
  79. package/dist/locales/id_ID.js +111 -0
  80. package/dist/locales/id_ID.js.map +1 -0
  81. package/dist/locales/index.d.ts +59 -0
  82. package/dist/locales/index.js +155 -0
  83. package/dist/locales/index.js.map +1 -0
  84. package/dist/locales/ja.d.ts +5 -0
  85. package/dist/locales/ja.js +111 -0
  86. package/dist/locales/ja.js.map +1 -0
  87. package/dist/locales/ko_KR.d.ts +5 -0
  88. package/dist/locales/ko_KR.js +111 -0
  89. package/dist/locales/ko_KR.js.map +1 -0
  90. package/dist/locales/pt_PT.d.ts +5 -0
  91. package/dist/locales/pt_PT.js +111 -0
  92. package/dist/locales/pt_PT.js.map +1 -0
  93. package/dist/locales/ru_RU.d.ts +5 -0
  94. package/dist/locales/ru_RU.js +111 -0
  95. package/dist/locales/ru_RU.js.map +1 -0
  96. package/dist/locales/types.d.ts +105 -0
  97. package/dist/locales/types.js +8 -0
  98. package/dist/locales/types.js.map +1 -0
  99. package/dist/locales/zh_CN.d.ts +5 -0
  100. package/dist/locales/zh_CN.js +111 -0
  101. package/dist/locales/zh_CN.js.map +1 -0
  102. package/dist/models/app_store_data.d.ts +87 -0
  103. package/dist/models/app_store_data.js +8 -0
  104. package/dist/models/app_store_data.js.map +1 -0
  105. package/dist/models/chart_data.d.ts +104 -0
  106. package/dist/models/chart_data.js +8 -0
  107. package/dist/models/chart_data.js.map +1 -0
  108. package/dist/models/common.d.ts +42 -0
  109. package/dist/models/common.js +8 -0
  110. package/dist/models/common.js.map +1 -0
  111. package/dist/models/firebase_analytics_data.d.ts +134 -0
  112. package/dist/models/firebase_analytics_data.js +8 -0
  113. package/dist/models/firebase_analytics_data.js.map +1 -0
  114. package/dist/models/github_analysis_data.d.ts +266 -0
  115. package/dist/models/github_analysis_data.js +101 -0
  116. package/dist/models/github_analysis_data.js.map +1 -0
  117. package/dist/models/google_play_data.d.ts +115 -0
  118. package/dist/models/google_play_data.js +8 -0
  119. package/dist/models/google_play_data.js.map +1 -0
  120. package/dist/models/index.d.ts +14 -0
  121. package/dist/models/index.js +36 -0
  122. package/dist/models/index.js.map +1 -0
  123. package/dist/models/market_research_data.d.ts +259 -0
  124. package/dist/models/market_research_data.js +8 -0
  125. package/dist/models/market_research_data.js.map +1 -0
  126. package/dist/models/marketing_analysis_data.d.ts +200 -0
  127. package/dist/models/marketing_analysis_data.js +8 -0
  128. package/dist/models/marketing_analysis_data.js.map +1 -0
  129. package/dist/models/pdf_data.d.ts +112 -0
  130. package/dist/models/pdf_data.js +8 -0
  131. package/dist/models/pdf_data.js.map +1 -0
  132. package/dist/services/chart_service.d.ts +54 -0
  133. package/dist/services/chart_service.js +436 -0
  134. package/dist/services/chart_service.js.map +1 -0
  135. package/dist/services/github_analysis_service.d.ts +73 -0
  136. package/dist/services/github_analysis_service.js +488 -0
  137. package/dist/services/github_analysis_service.js.map +1 -0
  138. package/dist/services/pdf_service.d.ts +112 -0
  139. package/dist/services/pdf_service.js +1397 -0
  140. package/dist/services/pdf_service.js.map +1 -0
  141. package/dist/utils/github_analysis_storage.d.ts +88 -0
  142. package/dist/utils/github_analysis_storage.js +154 -0
  143. package/dist/utils/github_analysis_storage.js.map +1 -0
  144. package/package.json +64 -0
  145. package/scripts/cleanup_test_data.ts +304 -0
  146. package/scripts/list_test_data.ts +85 -0
@@ -0,0 +1,653 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GenerateMarketingMarkdown = void 0;
4
+ const masamune_firebase_workflow_1 = require("@mathrunet/masamune_firebase_workflow");
5
+ require("@mathrunet/masamune");
6
+ const locales_1 = require("../locales");
7
+ /**
8
+ * A function for generating marketing analytics Markdown report.
9
+ *
10
+ * マーケティング分析Markdownレポートを生成するためのFunction。
11
+ */
12
+ class GenerateMarketingMarkdown extends masamune_firebase_workflow_1.WorkflowProcessFunctionBase {
13
+ /**
14
+ * The ID of the function.
15
+ *
16
+ * 関数のID。
17
+ */
18
+ id = "generate_marketing_markdown";
19
+ /**
20
+ * Translations for the current locale.
21
+ */
22
+ translations = (0, locales_1.getTranslations)("en");
23
+ /**
24
+ * The process of the function.
25
+ *
26
+ * @param context
27
+ * The context of the function.
28
+ *
29
+ * @returns
30
+ * The action of the function.
31
+ */
32
+ async process(context) {
33
+ const action = context.action;
34
+ const task = context.task;
35
+ // 1. task.results から各データを取得
36
+ const googlePlayConsole = task.results?.googlePlayConsole;
37
+ const appStore = task.results?.appStore;
38
+ const firebaseAnalytics = task.results?.firebaseAnalytics;
39
+ const marketingAnalytics = task.results?.marketingAnalytics;
40
+ const githubRepository = task.results?.githubRepository;
41
+ const githubImprovements = task.results?.githubImprovements;
42
+ // 2. いずれのデータも無ければ空データを返却
43
+ if (!googlePlayConsole && !appStore && !firebaseAnalytics && !marketingAnalytics) {
44
+ console.log("GenerateMarketingMarkdown: No marketing data found in task.results");
45
+ return {
46
+ ...action,
47
+ results: {
48
+ marketingAnalyticsMarkdown: "",
49
+ }
50
+ };
51
+ }
52
+ try {
53
+ // 3. Markdownレポートを生成
54
+ console.log("GenerateMarketingMarkdown: Generating Markdown report...");
55
+ const inputData = {
56
+ googlePlayConsole,
57
+ appStore,
58
+ firebaseAnalytics,
59
+ marketingAnalytics,
60
+ githubRepository,
61
+ githubImprovements,
62
+ };
63
+ // Determine app name from available data
64
+ const appName = appStore?.appName ||
65
+ googlePlayConsole?.packageName?.split(".")?.pop() ||
66
+ "Marketing Report";
67
+ // Determine date range from action command or use defaults
68
+ const command = action.command;
69
+ const commandData = command?.data;
70
+ const dateRange = commandData?.startDate && commandData?.endDate
71
+ ? { startDate: commandData.startDate, endDate: commandData.endDate }
72
+ : undefined;
73
+ const markdownContent = this.generateMarkdownReport({
74
+ data: inputData,
75
+ appName,
76
+ reportType: commandData?.reportType || "weekly",
77
+ dateRange,
78
+ locale: action.locale,
79
+ });
80
+ console.log("GenerateMarketingMarkdown: Markdown report generated, length:", markdownContent.length, "characters");
81
+ // 4. results.marketingAnalyticsMarkdown に文字列として格納
82
+ return {
83
+ ...action,
84
+ results: {
85
+ marketingAnalyticsMarkdown: markdownContent,
86
+ }
87
+ };
88
+ }
89
+ catch (error) {
90
+ console.error("GenerateMarketingMarkdown: Failed to generate Markdown report", error);
91
+ return {
92
+ ...action,
93
+ results: {
94
+ marketingAnalyticsMarkdown: "",
95
+ markdownError: error.message,
96
+ }
97
+ };
98
+ }
99
+ }
100
+ /**
101
+ * Generate the complete Markdown report.
102
+ */
103
+ generateMarkdownReport(options) {
104
+ // Initialize translations based on locale
105
+ const locale = typeof options.locale === "object"
106
+ ? options.locale["@language"]
107
+ : options.locale;
108
+ this.translations = (0, locales_1.getTranslations)(locale);
109
+ const sections = [];
110
+ // Header section
111
+ sections.push(this.generateHeader(options));
112
+ // Executive Summary
113
+ if (options.data.marketingAnalytics?.overallAnalysis) {
114
+ sections.push(this.generateExecutiveSummary(options));
115
+ }
116
+ // Highlights & Concerns
117
+ const analysis = options.data.marketingAnalytics?.overallAnalysis;
118
+ if ((analysis?.highlights?.length || 0) > 0 || (analysis?.concerns?.length || 0) > 0) {
119
+ sections.push(this.generateHighlightsConcerns(options));
120
+ }
121
+ // User Analytics
122
+ if (options.data.firebaseAnalytics) {
123
+ sections.push(this.generateUserAnalytics(options));
124
+ }
125
+ // Ratings & Reviews
126
+ if (options.data.googlePlayConsole || options.data.appStore || options.data.marketingAnalytics?.reviewAnalysis) {
127
+ sections.push(this.generateRatingsReviews(options));
128
+ }
129
+ // Competitive Positioning (market research data)
130
+ if (options.data.marketingAnalytics?.competitivePositioning) {
131
+ sections.push(this.generateCompetitivePositioning(options));
132
+ }
133
+ // Market Opportunity Priority (market research data)
134
+ if (options.data.marketingAnalytics?.marketOpportunityPriority) {
135
+ sections.push(this.generateMarketOpportunityPriority(options));
136
+ }
137
+ // Trend Analysis
138
+ if (options.data.marketingAnalytics?.trendAnalysis) {
139
+ sections.push(this.generateTrendAnalysis(options));
140
+ }
141
+ // Improvement Suggestions
142
+ if (options.data.marketingAnalytics?.improvementSuggestions?.length) {
143
+ sections.push(this.generateImprovements(options));
144
+ }
145
+ // GitHub Code Improvements
146
+ if (options.data.githubImprovements?.improvements?.length) {
147
+ sections.push(this.generateGitHubImprovements(options));
148
+ }
149
+ // Footer
150
+ sections.push(this.generateFooter());
151
+ return sections.join("\n\n---\n\n");
152
+ }
153
+ /**
154
+ * Generate header section.
155
+ */
156
+ generateHeader(options) {
157
+ const t = this.translations;
158
+ const appName = options.appName || "Marketing Report";
159
+ const reportTypeLabel = options.reportType === "daily"
160
+ ? t.dailyReport
161
+ : options.reportType === "weekly"
162
+ ? t.weeklyReport
163
+ : t.monthlyReport;
164
+ const lines = [
165
+ `# ${appName}`,
166
+ "",
167
+ `**${t.reportType}:** ${reportTypeLabel}`,
168
+ ];
169
+ if (options.dateRange) {
170
+ lines.push(`**${t.period}:** ${options.dateRange.startDate} - ${options.dateRange.endDate}`);
171
+ }
172
+ lines.push(`**${t.generated}:** ${new Date().toISOString().split("T")[0]}`);
173
+ // Data sources
174
+ const sources = [];
175
+ if (options.data.googlePlayConsole)
176
+ sources.push("Google Play");
177
+ if (options.data.appStore)
178
+ sources.push("App Store");
179
+ if (options.data.firebaseAnalytics)
180
+ sources.push("Firebase Analytics");
181
+ if (options.data.marketingAnalytics)
182
+ sources.push(t.aiAnalysis);
183
+ if (sources.length > 0) {
184
+ lines.push(`**${t.dataSources}:** ${sources.join(" | ")}`);
185
+ }
186
+ return lines.join("\n");
187
+ }
188
+ /**
189
+ * Generate executive summary section.
190
+ */
191
+ generateExecutiveSummary(options) {
192
+ const t = this.translations;
193
+ const analysis = options.data.marketingAnalytics?.overallAnalysis;
194
+ const lines = [`## ${t.executiveSummary}`];
195
+ // Summary text
196
+ if (analysis?.summary) {
197
+ lines.push("", analysis.summary);
198
+ }
199
+ // Key Metrics table
200
+ if (analysis?.keyMetrics?.length) {
201
+ lines.push("", `### ${t.metric}`, "");
202
+ lines.push(`| ${t.metric} | ${t.value} | ${t.trend} |`);
203
+ lines.push("|--------|-------|-------|");
204
+ for (const metric of analysis.keyMetrics) {
205
+ const trendIcon = metric.trend === "up" ? "↑" : metric.trend === "down" ? "↓" : "→";
206
+ lines.push(`| ${metric.metric} | ${metric.value} | ${trendIcon} |`);
207
+ }
208
+ }
209
+ return lines.join("\n");
210
+ }
211
+ /**
212
+ * Generate highlights and concerns section.
213
+ */
214
+ generateHighlightsConcerns(options) {
215
+ const t = this.translations;
216
+ const analysis = options.data.marketingAnalytics?.overallAnalysis;
217
+ const lines = [`## ${t.highlightsAndConcerns}`];
218
+ // Highlights
219
+ if (analysis?.highlights?.length) {
220
+ lines.push("", `### ${t.highlights}`);
221
+ for (const highlight of analysis.highlights) {
222
+ lines.push(`- ${highlight}`);
223
+ }
224
+ }
225
+ // Concerns
226
+ if (analysis?.concerns?.length) {
227
+ lines.push("", `### ${t.concerns}`);
228
+ for (const concern of analysis.concerns) {
229
+ lines.push(`- ${concern}`);
230
+ }
231
+ }
232
+ return lines.join("\n");
233
+ }
234
+ /**
235
+ * Generate user analytics section.
236
+ */
237
+ generateUserAnalytics(options) {
238
+ const t = this.translations;
239
+ const firebase = options.data.firebaseAnalytics;
240
+ const lines = [`## ${t.userAnalytics}`];
241
+ if (!firebase) {
242
+ return lines.join("\n");
243
+ }
244
+ // Main metrics table
245
+ lines.push("", `### ${t.activeUsers}`, "");
246
+ lines.push(`| ${t.metric} | ${t.value} |`);
247
+ lines.push("|--------|-------|");
248
+ if (firebase.dau !== undefined) {
249
+ lines.push(`| DAU | ${firebase.dau.toLocaleString()} |`);
250
+ }
251
+ if (firebase.wau !== undefined) {
252
+ lines.push(`| WAU | ${firebase.wau.toLocaleString()} |`);
253
+ }
254
+ if (firebase.mau !== undefined) {
255
+ lines.push(`| MAU | ${firebase.mau.toLocaleString()} |`);
256
+ }
257
+ if (firebase.newUsers !== undefined) {
258
+ lines.push(`| ${t.newUsers} | ${firebase.newUsers.toLocaleString()} |`);
259
+ }
260
+ // Retention ratio
261
+ if (firebase.dau && firebase.mau && firebase.mau > 0) {
262
+ const retentionRatio = ((firebase.dau / firebase.mau) * 100).toFixed(1);
263
+ lines.push(`| ${t.retention} (DAU/MAU) | ${retentionRatio}% |`);
264
+ }
265
+ // Session stats
266
+ if (firebase.averageSessionDuration !== undefined || firebase.sessionsPerUser !== undefined) {
267
+ lines.push("", `### ${t.sessionStatistics}`, "");
268
+ lines.push(`| ${t.metric} | ${t.value} |`);
269
+ lines.push("|--------|-------|");
270
+ if (firebase.averageSessionDuration !== undefined) {
271
+ const minutes = Math.floor(firebase.averageSessionDuration / 60);
272
+ const seconds = Math.floor(firebase.averageSessionDuration % 60);
273
+ lines.push(`| ${t.avgSessionDuration} | ${minutes}m ${seconds}s |`);
274
+ }
275
+ if (firebase.sessionsPerUser !== undefined) {
276
+ lines.push(`| ${t.sessionsPerUser} | ${firebase.sessionsPerUser.toFixed(1)} |`);
277
+ }
278
+ }
279
+ // Demographics
280
+ if (firebase.demographics?.ageGroups && Object.keys(firebase.demographics.ageGroups).length > 0) {
281
+ lines.push("", `### ${t.ageDemographics}`, "");
282
+ lines.push(`| Age Group | ${t.percentage} |`);
283
+ lines.push("|-----------|------------|");
284
+ const sortedAgeGroups = Object.entries(firebase.demographics.ageGroups)
285
+ .sort((a, b) => b[1] - a[1]);
286
+ for (const [ageGroup, percentage] of sortedAgeGroups) {
287
+ const bar = this.generateProgressBar(percentage);
288
+ lines.push(`| ${ageGroup} | ${bar} ${percentage}% |`);
289
+ }
290
+ }
291
+ if (firebase.demographics?.countryDistribution && Object.keys(firebase.demographics.countryDistribution).length > 0) {
292
+ lines.push("", `### ${t.countryDistribution}`, "");
293
+ lines.push(`| Country | ${t.percentage} |`);
294
+ lines.push("|---------|------------|");
295
+ const sortedCountries = Object.entries(firebase.demographics.countryDistribution)
296
+ .sort((a, b) => b[1] - a[1])
297
+ .slice(0, 10); // Top 10 countries
298
+ for (const [country, percentage] of sortedCountries) {
299
+ const bar = this.generateProgressBar(percentage);
300
+ lines.push(`| ${country} | ${bar} ${percentage}% |`);
301
+ }
302
+ }
303
+ return lines.join("\n");
304
+ }
305
+ /**
306
+ * Generate ratings and reviews section.
307
+ */
308
+ generateRatingsReviews(options) {
309
+ const t = this.translations;
310
+ const googlePlay = options.data.googlePlayConsole;
311
+ const appStore = options.data.appStore;
312
+ const reviewAnalysis = options.data.marketingAnalytics?.reviewAnalysis;
313
+ const lines = [`## ${t.ratingsAndReviews}`];
314
+ // Rating summary
315
+ if (googlePlay?.averageRating || appStore?.averageRating) {
316
+ lines.push("", `### ${t.overallRatings}`, "");
317
+ lines.push(`| ${t.platform} | ${t.rating} | ${t.totalRatings} |`);
318
+ lines.push("|----------|--------|---------------|");
319
+ if (googlePlay?.averageRating) {
320
+ const stars = this.generateStars(googlePlay.averageRating);
321
+ lines.push(`| Google Play | ${stars} ${googlePlay.averageRating.toFixed(1)} | ${(googlePlay.totalRatings || 0).toLocaleString()} |`);
322
+ }
323
+ if (appStore?.averageRating) {
324
+ const stars = this.generateStars(appStore.averageRating);
325
+ lines.push(`| App Store | ${stars} ${appStore.averageRating.toFixed(1)} | ${(appStore.totalRatings || 0).toLocaleString()} |`);
326
+ }
327
+ }
328
+ // Rating distribution
329
+ if (googlePlay?.ratingDistribution || appStore?.ratingDistribution) {
330
+ lines.push("", `### ${t.ratingDistribution}`, "");
331
+ const hasGooglePlay = googlePlay?.ratingDistribution;
332
+ const hasAppStore = appStore?.ratingDistribution;
333
+ if (hasGooglePlay && hasAppStore) {
334
+ lines.push(`| ${t.rating} | Google Play | App Store |`);
335
+ lines.push("|--------|-------------|-----------|");
336
+ }
337
+ else if (hasGooglePlay) {
338
+ lines.push(`| ${t.rating} | Google Play |`);
339
+ lines.push("|--------|-------------|");
340
+ }
341
+ else {
342
+ lines.push(`| ${t.rating} | App Store |`);
343
+ lines.push("|--------|-----------|");
344
+ }
345
+ for (let i = 5; i >= 1; i--) {
346
+ const starLabel = "★".repeat(i) + "☆".repeat(5 - i);
347
+ const gpValue = googlePlay?.ratingDistribution?.[i.toString()] || googlePlay?.ratingDistribution?.[`star${i}`] || 0;
348
+ const asValue = appStore?.ratingDistribution?.[i.toString()] || appStore?.ratingDistribution?.[`star${i}`] || 0;
349
+ if (hasGooglePlay && hasAppStore) {
350
+ const gpBar = this.generateProgressBar(gpValue);
351
+ const asBar = this.generateProgressBar(asValue);
352
+ lines.push(`| ${starLabel} | ${gpBar} ${gpValue}% | ${asBar} ${asValue}% |`);
353
+ }
354
+ else if (hasGooglePlay) {
355
+ const gpBar = this.generateProgressBar(gpValue);
356
+ lines.push(`| ${starLabel} | ${gpBar} ${gpValue}% |`);
357
+ }
358
+ else {
359
+ const asBar = this.generateProgressBar(asValue);
360
+ lines.push(`| ${starLabel} | ${asBar} ${asValue}% |`);
361
+ }
362
+ }
363
+ }
364
+ // Review Analysis
365
+ if (reviewAnalysis) {
366
+ // Sentiment
367
+ if (reviewAnalysis.sentiment) {
368
+ lines.push("", `### ${t.sentimentAnalysis}`, "");
369
+ lines.push(`| Sentiment | ${t.percentage} |`);
370
+ lines.push("|-----------|------------|");
371
+ const posBar = this.generateProgressBar(reviewAnalysis.sentiment.positive);
372
+ const neuBar = this.generateProgressBar(reviewAnalysis.sentiment.neutral);
373
+ const negBar = this.generateProgressBar(reviewAnalysis.sentiment.negative);
374
+ lines.push(`| ${t.positive} | ${posBar} ${reviewAnalysis.sentiment.positive}% |`);
375
+ lines.push(`| ${t.neutral} | ${neuBar} ${reviewAnalysis.sentiment.neutral}% |`);
376
+ lines.push(`| ${t.negative} | ${negBar} ${reviewAnalysis.sentiment.negative}% |`);
377
+ }
378
+ // Common themes
379
+ if (reviewAnalysis.commonThemes?.length) {
380
+ lines.push("", `### ${t.commonThemes}`);
381
+ for (const theme of reviewAnalysis.commonThemes) {
382
+ lines.push(`- ${theme}`);
383
+ }
384
+ }
385
+ // Actionable insights
386
+ if (reviewAnalysis.actionableInsights?.length) {
387
+ lines.push("", `### ${t.actionableInsights}`);
388
+ for (const insight of reviewAnalysis.actionableInsights) {
389
+ lines.push(`- ${insight}`);
390
+ }
391
+ }
392
+ }
393
+ return lines.join("\n");
394
+ }
395
+ /**
396
+ * Generate improvement suggestions section.
397
+ */
398
+ generateImprovements(options) {
399
+ const t = this.translations;
400
+ const suggestions = options.data.marketingAnalytics?.improvementSuggestions || [];
401
+ const lines = [`## ${t.improvementSuggestions}`];
402
+ for (const suggestion of suggestions) {
403
+ const priorityEmoji = suggestion.priority === "high" ? "🔴" : suggestion.priority === "medium" ? "🟡" : "🟢";
404
+ lines.push("");
405
+ lines.push(`### ${priorityEmoji} ${suggestion.priority.toUpperCase()}: [${suggestion.category}] ${suggestion.title}`);
406
+ lines.push("");
407
+ lines.push(suggestion.description);
408
+ if (suggestion.expectedImpact) {
409
+ lines.push("");
410
+ lines.push(`**${t.expectedImpact}:** ${suggestion.expectedImpact}`);
411
+ }
412
+ }
413
+ return lines.join("\n");
414
+ }
415
+ /**
416
+ * Generate competitive positioning section.
417
+ *
418
+ * 競合ポジショニング分析セクションを生成。
419
+ */
420
+ generateCompetitivePositioning(options) {
421
+ const t = this.translations;
422
+ const positioning = options.data.marketingAnalytics?.competitivePositioning;
423
+ const lines = [`## ${t.competitivePositioning}`];
424
+ // Market Position
425
+ if (positioning?.marketPosition) {
426
+ lines.push("", `### ${t.marketPosition}`);
427
+ lines.push("", positioning.marketPosition);
428
+ }
429
+ // Competitor Comparison
430
+ if (positioning?.competitorComparison?.length) {
431
+ lines.push("", `### ${t.competitorComparison}`);
432
+ for (const comp of positioning.competitorComparison) {
433
+ lines.push("");
434
+ lines.push(`#### vs ${comp.competitor}`);
435
+ lines.push("");
436
+ if (comp.ourStrengths?.length) {
437
+ lines.push(`**${t.ourStrengths}:**`);
438
+ for (const strength of comp.ourStrengths) {
439
+ lines.push(`- ✅ ${strength}`);
440
+ }
441
+ }
442
+ if (comp.ourWeaknesses?.length) {
443
+ lines.push("");
444
+ lines.push(`**${t.ourWeaknesses}:**`);
445
+ for (const weakness of comp.ourWeaknesses) {
446
+ lines.push(`- ⚠️ ${weakness}`);
447
+ }
448
+ }
449
+ if (comp.battleStrategy) {
450
+ lines.push("");
451
+ lines.push(`**${t.battleStrategy}:** ${comp.battleStrategy}`);
452
+ }
453
+ }
454
+ }
455
+ // Differentiation Strategy
456
+ if (positioning?.differentiationStrategy) {
457
+ lines.push("", `### ${t.differentiationStrategy}`);
458
+ lines.push("", positioning.differentiationStrategy);
459
+ }
460
+ // Quick Wins
461
+ if (positioning?.quickWins?.length) {
462
+ lines.push("", `### ${t.quickWins}`);
463
+ for (const quickWin of positioning.quickWins) {
464
+ lines.push(`- 🚀 ${quickWin}`);
465
+ }
466
+ }
467
+ return lines.join("\n");
468
+ }
469
+ /**
470
+ * Generate market opportunity priority section.
471
+ *
472
+ * 市場機会優先度分析セクションを生成。
473
+ */
474
+ generateMarketOpportunityPriority(options) {
475
+ const t = this.translations;
476
+ const priority = options.data.marketingAnalytics?.marketOpportunityPriority;
477
+ const lines = [`## ${t.marketOpportunityPriority}`];
478
+ // Prioritized Opportunities
479
+ if (priority?.prioritizedOpportunities?.length) {
480
+ lines.push("", `### ${t.prioritizedOpportunities}`, "");
481
+ // Summary table
482
+ lines.push(`| ${t.opportunity} | ${t.fitScore} | ${t.effort} |`);
483
+ lines.push("|------|--------|------|");
484
+ const fitScoreEmoji = {
485
+ excellent: "🟢",
486
+ good: "🔵",
487
+ moderate: "🟡",
488
+ poor: "🔴",
489
+ };
490
+ const effortLabel = {
491
+ low: t.low,
492
+ medium: t.medium,
493
+ high: t.high,
494
+ };
495
+ for (const opp of priority.prioritizedOpportunities) {
496
+ const emoji = fitScoreEmoji[opp.fitScore] || "⚪";
497
+ const effort = effortLabel[opp.estimatedEffort] || opp.estimatedEffort;
498
+ lines.push(`| ${opp.opportunity} | ${emoji} ${opp.fitScore} | ${effort} |`);
499
+ }
500
+ // Detailed view for each opportunity
501
+ lines.push("");
502
+ for (const opp of priority.prioritizedOpportunities) {
503
+ const emoji = fitScoreEmoji[opp.fitScore] || "⚪";
504
+ lines.push(`#### ${emoji} ${opp.opportunity}`);
505
+ lines.push("");
506
+ lines.push(`**${t.fitScore}:** ${opp.fitScore}`);
507
+ if (opp.fitReason) {
508
+ lines.push(`**${t.reason}:** ${opp.fitReason}`);
509
+ }
510
+ if (opp.requiredChanges?.length) {
511
+ lines.push("");
512
+ lines.push(`**${t.requiredChanges}:**`);
513
+ for (const change of opp.requiredChanges) {
514
+ lines.push(`- ${change}`);
515
+ }
516
+ }
517
+ if (opp.recommendedAction) {
518
+ lines.push("");
519
+ lines.push(`**${t.recommendedAction}:** ${opp.recommendedAction}`);
520
+ }
521
+ lines.push("");
522
+ }
523
+ }
524
+ // Strategic Recommendation
525
+ if (priority?.strategicRecommendation) {
526
+ lines.push(`### ${t.strategicRecommendations}`);
527
+ lines.push("", priority.strategicRecommendation);
528
+ }
529
+ return lines.join("\n");
530
+ }
531
+ /**
532
+ * Generate GitHub code improvements section.
533
+ */
534
+ generateGitHubImprovements(options) {
535
+ const t = this.translations;
536
+ const githubImprovements = options.data.githubImprovements;
537
+ const improvements = githubImprovements?.improvements || [];
538
+ const lines = [`## ${t.codebaseImprovements}`];
539
+ // Repository info
540
+ const repo = githubImprovements?.repository || "";
541
+ const framework = githubImprovements?.framework || "";
542
+ if (repo || framework) {
543
+ lines.push("");
544
+ lines.push(`**${t.repository}:** ${repo} | **${t.framework}:** ${framework}`);
545
+ }
546
+ // Summary
547
+ if (githubImprovements?.improvementSummary) {
548
+ lines.push("");
549
+ lines.push(githubImprovements.improvementSummary);
550
+ }
551
+ // Each improvement
552
+ for (const improvement of improvements) {
553
+ const priorityEmoji = improvement.priority === "high" ? "🔴" : improvement.priority === "medium" ? "🟡" : "🟢";
554
+ lines.push("");
555
+ lines.push(`### ${priorityEmoji} ${(improvement.priority || "medium").toUpperCase()} [${improvement.category || ""}]: ${improvement.title || ""}`);
556
+ lines.push("");
557
+ if (improvement.description) {
558
+ lines.push(improvement.description);
559
+ }
560
+ if (improvement.relatedFeature) {
561
+ lines.push("");
562
+ lines.push(`**${t.relatedFeature}:** ${improvement.relatedFeature}`);
563
+ }
564
+ // Code References
565
+ const codeRefs = improvement.codeReferences || [];
566
+ if (codeRefs.length > 0) {
567
+ lines.push("");
568
+ lines.push(`**${t.fileModifications}:**`);
569
+ lines.push("");
570
+ lines.push(`| ${t.type} | ${t.file} | ${t.current} | ${t.proposed} |`);
571
+ lines.push("|------|------|---------|----------|");
572
+ const modIcon = {
573
+ add: "+",
574
+ modify: "~",
575
+ refactor: "R",
576
+ optimize: "O",
577
+ };
578
+ for (const ref of codeRefs) {
579
+ const icon = modIcon[ref.modificationType || ""] || "?";
580
+ const filePath = ref.filePath || "";
581
+ const current = (ref.currentFunctionality || "").replace(/\|/g, "\\|").replace(/\n/g, " ");
582
+ const proposed = (ref.proposedChange || "").replace(/\|/g, "\\|").replace(/\n/g, " ");
583
+ lines.push(`| ${icon} | \`${filePath}\` | ${current} | ${proposed} |`);
584
+ }
585
+ }
586
+ if (improvement.expectedImpact) {
587
+ lines.push("");
588
+ lines.push(`**${t.expectedImpact}:** ${improvement.expectedImpact}`);
589
+ }
590
+ }
591
+ return lines.join("\n");
592
+ }
593
+ /**
594
+ * Generate trend analysis section.
595
+ */
596
+ generateTrendAnalysis(options) {
597
+ const t = this.translations;
598
+ const trendAnalysis = options.data.marketingAnalytics?.trendAnalysis;
599
+ const lines = [`## ${t.trendAnalysisAndPredictions}`];
600
+ if (trendAnalysis?.userGrowthTrend) {
601
+ lines.push("", `### ${t.userGrowth}`);
602
+ lines.push(trendAnalysis.userGrowthTrend);
603
+ }
604
+ if (trendAnalysis?.engagementTrend) {
605
+ lines.push("", `### ${t.engagement}`);
606
+ lines.push(trendAnalysis.engagementTrend);
607
+ }
608
+ if (trendAnalysis?.ratingTrend) {
609
+ lines.push("", `### ${t.ratings}`);
610
+ lines.push(trendAnalysis.ratingTrend);
611
+ }
612
+ if (trendAnalysis?.predictions?.length) {
613
+ lines.push("", `### ${t.predictions}`);
614
+ for (const prediction of trendAnalysis.predictions) {
615
+ lines.push(`- ${prediction}`);
616
+ }
617
+ }
618
+ return lines.join("\n");
619
+ }
620
+ /**
621
+ * Generate footer section.
622
+ */
623
+ generateFooter() {
624
+ const t = this.translations;
625
+ return `*${t.generatedBy} - ${new Date().toISOString().split("T")[0]}*`;
626
+ }
627
+ /**
628
+ * Generate a progress bar using block characters.
629
+ */
630
+ generateProgressBar(percentage, maxBlocks = 10) {
631
+ // Clamp percentage to 0-100 range
632
+ const clampedPercentage = Math.max(0, Math.min(100, percentage));
633
+ const filledBlocks = Math.round((clampedPercentage / 100) * maxBlocks);
634
+ const emptyBlocks = maxBlocks - filledBlocks;
635
+ return "█".repeat(filledBlocks) + "░".repeat(emptyBlocks);
636
+ }
637
+ /**
638
+ * Generate star rating display.
639
+ */
640
+ generateStars(rating) {
641
+ // Clamp rating to 0-5 range
642
+ const clampedRating = Math.max(0, Math.min(5, rating));
643
+ const fullStars = Math.floor(clampedRating);
644
+ const hasHalfStar = clampedRating - fullStars >= 0.5;
645
+ const emptyStars = Math.max(0, 5 - fullStars - (hasHalfStar ? 1 : 0));
646
+ return "★".repeat(fullStars) + (hasHalfStar ? "☆" : "") + "☆".repeat(emptyStars);
647
+ }
648
+ }
649
+ exports.GenerateMarketingMarkdown = GenerateMarketingMarkdown;
650
+ module.exports = (regions, options, data) => new GenerateMarketingMarkdown(options).build(regions);
651
+ // Export class for testing
652
+ module.exports.GenerateMarketingMarkdown = GenerateMarketingMarkdown;
653
+ //# sourceMappingURL=generate_marketing_markdown.js.map