pabal-web-mcp 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/mcp-server.js +147 -166
- package/dist/index.d.ts +115 -5
- package/package.json +1 -1
package/dist/bin/mcp-server.js
CHANGED
|
@@ -199,7 +199,6 @@ var asoToPublicInputSchema = z.object({
|
|
|
199
199
|
});
|
|
200
200
|
var jsonSchema = toJsonSchema(asoToPublicInputSchema, {
|
|
201
201
|
name: "AsoToPublicInput",
|
|
202
|
-
target: "openApi3",
|
|
203
202
|
$refStrategy: "none"
|
|
204
203
|
});
|
|
205
204
|
var inputSchema = jsonSchema.definitions?.AsoToPublicInput || jsonSchema;
|
|
@@ -304,9 +303,11 @@ async function handleAsoToPublic(input) {
|
|
|
304
303
|
prompt
|
|
305
304
|
});
|
|
306
305
|
const sourcesText = sources.join(" + ");
|
|
307
|
-
conversionPrompts.push(
|
|
306
|
+
conversionPrompts.push(
|
|
307
|
+
`
|
|
308
308
|
--- ${unifiedLocale} (${sourcesText}) ---
|
|
309
|
-
${prompt}`
|
|
309
|
+
${prompt}`
|
|
310
|
+
);
|
|
310
311
|
}
|
|
311
312
|
const pullDataDir = getPullDataDir();
|
|
312
313
|
let responseText = `Converting ASO data from pullData to public/products/${slug}/ structure.
|
|
@@ -517,7 +518,6 @@ var publicToAsoInputSchema = z2.object({
|
|
|
517
518
|
});
|
|
518
519
|
var jsonSchema2 = toJsonSchema2(publicToAsoInputSchema, {
|
|
519
520
|
name: "PublicToAsoInput",
|
|
520
|
-
target: "openApi3",
|
|
521
521
|
$refStrategy: "none"
|
|
522
522
|
});
|
|
523
523
|
var inputSchema2 = jsonSchema2.definitions?.PublicToAsoInput || jsonSchema2;
|
|
@@ -1610,7 +1610,6 @@ var improvePublicInputSchema = z3.object({
|
|
|
1610
1610
|
});
|
|
1611
1611
|
var jsonSchema3 = toJsonSchema3(improvePublicInputSchema, {
|
|
1612
1612
|
name: "ImprovePublicInput",
|
|
1613
|
-
target: "openApi3",
|
|
1614
1613
|
$refStrategy: "none"
|
|
1615
1614
|
});
|
|
1616
1615
|
var inputSchema3 = jsonSchema3.definitions?.ImprovePublicInput || jsonSchema3;
|
|
@@ -1796,7 +1795,6 @@ var initProjectInputSchema = z4.object({
|
|
|
1796
1795
|
});
|
|
1797
1796
|
var jsonSchema4 = zodToJsonSchema4(initProjectInputSchema, {
|
|
1798
1797
|
name: "InitProjectInput",
|
|
1799
|
-
target: "openApi3",
|
|
1800
1798
|
$refStrategy: "none"
|
|
1801
1799
|
});
|
|
1802
1800
|
var inputSchema4 = jsonSchema4.definitions?.InitProjectInput || jsonSchema4;
|
|
@@ -1891,12 +1889,13 @@ async function handleInitProject(input) {
|
|
|
1891
1889
|
}
|
|
1892
1890
|
|
|
1893
1891
|
// src/tools/create-blog-html.ts
|
|
1894
|
-
import
|
|
1892
|
+
import fs8 from "fs";
|
|
1895
1893
|
import path8 from "path";
|
|
1896
1894
|
import { z as z5 } from "zod";
|
|
1897
1895
|
import { zodToJsonSchema as zodToJsonSchema5 } from "zod-to-json-schema";
|
|
1898
1896
|
|
|
1899
1897
|
// src/utils/blog.util.ts
|
|
1898
|
+
import fs7 from "fs";
|
|
1900
1899
|
import path7 from "path";
|
|
1901
1900
|
var DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/;
|
|
1902
1901
|
var BLOG_ROOT = "blogs";
|
|
@@ -1917,7 +1916,6 @@ function normalizeDate(date) {
|
|
|
1917
1916
|
}
|
|
1918
1917
|
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
1919
1918
|
}
|
|
1920
|
-
var isKoreanLocale = (locale) => locale.trim().toLowerCase().startsWith("ko");
|
|
1921
1919
|
var toPublicBlogBase = (appSlug, slug) => `/${BLOG_ROOT}/${appSlug}/${slug}`;
|
|
1922
1920
|
function resolveCoverImagePath(appSlug, slug, coverImage) {
|
|
1923
1921
|
if (!coverImage || !coverImage.trim()) {
|
|
@@ -1933,20 +1931,6 @@ function resolveCoverImagePath(appSlug, slug, coverImage) {
|
|
|
1933
1931
|
}
|
|
1934
1932
|
return cleaned;
|
|
1935
1933
|
}
|
|
1936
|
-
function resolveRelativeImagePath(appSlug, slug, relativePath) {
|
|
1937
|
-
const raw = relativePath?.trim() || "./images/hero.png";
|
|
1938
|
-
const normalized = raw.replace(/^\.\//, "");
|
|
1939
|
-
return {
|
|
1940
|
-
raw,
|
|
1941
|
-
absolute: `${toPublicBlogBase(appSlug, slug)}/${normalized}`
|
|
1942
|
-
};
|
|
1943
|
-
}
|
|
1944
|
-
function buildDescription(locale, topic, appSlug) {
|
|
1945
|
-
if (isKoreanLocale(locale)) {
|
|
1946
|
-
return `${topic}\uB97C \uC8FC\uC81C\uB85C ${appSlug}\uAC00 ASO\uC640 SEO\uB97C \uC5B4\uB5BB\uAC8C \uC5F0\uACB0\uD558\uACE0 \uBE14\uB85C\uADF8 \uD2B8\uB798\uD53D\uC744 \uC81C\uD488 \uD398\uC774\uC9C0\uB85C \uC774\uC5B4\uC8FC\uB294\uC9C0 \uC815\uB9AC\uD588\uC2B5\uB2C8\uB2E4.`;
|
|
1947
|
-
}
|
|
1948
|
-
return `How ${appSlug} teams turn "${topic}" into a bridge between ASO pages and SEO blogs without losing consistency.`;
|
|
1949
|
-
}
|
|
1950
1934
|
function deriveTags(topic, appSlug) {
|
|
1951
1935
|
const topicParts = topic.toLowerCase().split(/[^a-z0-9+]+/).filter(Boolean).slice(0, 6);
|
|
1952
1936
|
const set = /* @__PURE__ */ new Set([...topicParts, appSlug.toLowerCase(), "blog"]);
|
|
@@ -1960,9 +1944,14 @@ function buildBlogMeta(options) {
|
|
|
1960
1944
|
options.slug,
|
|
1961
1945
|
options.coverImage
|
|
1962
1946
|
);
|
|
1947
|
+
if (!options.description || !options.description.trim()) {
|
|
1948
|
+
throw new Error(
|
|
1949
|
+
"Description is required. The LLM must generate a meta description based on the topic and locale."
|
|
1950
|
+
);
|
|
1951
|
+
}
|
|
1963
1952
|
return {
|
|
1964
1953
|
title: options.title,
|
|
1965
|
-
description: options.description
|
|
1954
|
+
description: options.description.trim(),
|
|
1966
1955
|
appSlug: options.appSlug,
|
|
1967
1956
|
slug: options.slug,
|
|
1968
1957
|
locale: options.locale,
|
|
@@ -1980,128 +1969,18 @@ function renderBlogMetaBlock(meta) {
|
|
|
1980
1969
|
${serialized}
|
|
1981
1970
|
-->`;
|
|
1982
1971
|
}
|
|
1983
|
-
function renderEnglishBody(args) {
|
|
1984
|
-
const { meta, topic, appSlug, includeRelativeImageExample, relativeImagePath } = args;
|
|
1985
|
-
const lines = [];
|
|
1986
|
-
lines.push(`<h1>${meta.title}</h1>`);
|
|
1987
|
-
lines.push(
|
|
1988
|
-
`<p>${appSlug} keeps product pages and blogs aligned. This article shows how to use "${topic}" as a shared story so ASO and SEO stay in sync.</p>`
|
|
1989
|
-
);
|
|
1990
|
-
if (includeRelativeImageExample && relativeImagePath) {
|
|
1991
|
-
lines.push(
|
|
1992
|
-
`<img src="${relativeImagePath.raw}" alt="${appSlug} ${topic} cover" />`
|
|
1993
|
-
);
|
|
1994
|
-
lines.push(
|
|
1995
|
-
`<p>The image above is stored next to this file and resolves to <code>${relativeImagePath.absolute}</code> when published.</p>`
|
|
1996
|
-
);
|
|
1997
|
-
}
|
|
1998
|
-
lines.push(`<h2>Why the gap appears</h2>`);
|
|
1999
|
-
lines.push(
|
|
2000
|
-
`<p>ASO pages are tuned for storefronts while SEO posts speak to search crawlers. Teams often duplicate work, drift on messaging, and miss internal links back to /products/${appSlug}.</p>`
|
|
2001
|
-
);
|
|
2002
|
-
lines.push(`<h3>Signals that drift</h3>`);
|
|
2003
|
-
lines.push(
|
|
2004
|
-
`<p>Different headlines, mismatched screenshots, and stale dates make ranking harder. "${topic}" is a strong bridge topic because it touches both acquisition paths.</p>`
|
|
2005
|
-
);
|
|
2006
|
-
lines.push(`<h2>How to bridge with ${appSlug}</h2>`);
|
|
2007
|
-
lines.push(
|
|
2008
|
-
`<p>Start with the product story, then reuse it in blog form. Keep the same core claim, swap storefront keywords for search intent, and reference the canonical product slug.</p>`
|
|
2009
|
-
);
|
|
2010
|
-
lines.push(`<h3>Mini playbook</h3>`);
|
|
2011
|
-
lines.push(
|
|
2012
|
-
`<ul>
|
|
2013
|
-
<li>Reuse the app store hero claim inside the intro.</li>
|
|
2014
|
-
<li>Map ASO keywords to SEO phrases for the "${topic}" angle.</li>
|
|
2015
|
-
<li>Link feature blurbs to product screenshots and changelog notes.</li>
|
|
2016
|
-
<li>Close with a CTA back to <code>/products/${appSlug}</code>.</li>
|
|
2017
|
-
</ul>`
|
|
2018
|
-
);
|
|
2019
|
-
lines.push(`<h2>Example flow to copy</h2>`);
|
|
2020
|
-
lines.push(
|
|
2021
|
-
`<p>Pick one release, outline how it helps with "${topic}", then add a short proof (metric, quote, or screenshot). Keep h2/h3 hierarchy stable so translations stay predictable.</p>`
|
|
2022
|
-
);
|
|
2023
|
-
lines.push(`<h2>Wrap up</h2>`);
|
|
2024
|
-
lines.push(
|
|
2025
|
-
`<p>${appSlug} keeps ASO and SEO talking to each other. Publish this HTML under <code>/blogs/${appSlug}/${meta.slug}/${meta.locale}.html</code> and link it from the product page so traffic flows both ways.</p>`
|
|
2026
|
-
);
|
|
2027
|
-
lines.push(
|
|
2028
|
-
`<p><strong>CTA:</strong> Explore the product page at <a href="/products/${appSlug}">/products/${appSlug}</a>.</p>`
|
|
2029
|
-
);
|
|
2030
|
-
return lines.join("\n\n");
|
|
2031
|
-
}
|
|
2032
|
-
function renderKoreanBody(args) {
|
|
2033
|
-
const { meta, topic, appSlug, includeRelativeImageExample, relativeImagePath } = args;
|
|
2034
|
-
const lines = [];
|
|
2035
|
-
lines.push(`<h1>${meta.title}</h1>`);
|
|
2036
|
-
lines.push(
|
|
2037
|
-
`<p>${appSlug}\uB294 \uC81C\uD488 \uD398\uC774\uC9C0\uC640 \uBE14\uB85C\uADF8\uC758 \uD750\uB984\uC774 \uB04A\uAE30\uC9C0 \uC54A\uB3C4\uB85D "${topic}"\uC744 \uAC19\uC740 \uC774\uC57C\uAE30\uB85C \uBB36\uC5B4\uB0C5\uB2C8\uB2E4. \uC774 \uAE00\uC740 ASO \uC2E0\uD638\uC640 SEO \uCF58\uD150\uCE20\uB97C \uD558\uB098\uC758 \uBA54\uC2DC\uC9C0\uB85C \uC5F0\uACB0\uD558\uB294 \uBC29\uBC95\uC744 \uC124\uBA85\uD569\uB2C8\uB2E4.</p>`
|
|
2038
|
-
);
|
|
2039
|
-
if (includeRelativeImageExample && relativeImagePath) {
|
|
2040
|
-
lines.push(
|
|
2041
|
-
`<img src="${relativeImagePath.raw}" alt="${appSlug} ${topic} \uD45C\uC9C0 \uC774\uBBF8\uC9C0" />`
|
|
2042
|
-
);
|
|
2043
|
-
lines.push(
|
|
2044
|
-
`<p>\uC704 \uC774\uBBF8\uC9C0\uB294 \uAE00\uACFC \uAC19\uC740 \uD3F4\uB354\uC5D0 \uC800\uC7A5\uB418\uC5B4 \uD37C\uBE14\uB9AC\uC2DC \uC2DC <code>${relativeImagePath.absolute}</code> \uACBD\uB85C\uB85C \uB178\uCD9C\uB429\uB2C8\uB2E4.</p>`
|
|
2045
|
-
);
|
|
2046
|
-
}
|
|
2047
|
-
lines.push(`<h2>ASO\uC640 SEO\uAC00 \uAC08\uB77C\uC9C0\uB294 \uC9C0\uC810</h2>`);
|
|
2048
|
-
lines.push(
|
|
2049
|
-
`<p>\uC2A4\uD1A0\uC5B4 \uCD5C\uC801\uD654\uB294 \uC804\uD658\uC5D0 \uC9D1\uC911\uD558\uACE0, \uBE14\uB85C\uADF8\uB294 \uAC80\uC0C9 \uB178\uCD9C\uC744 \uB178\uB9BD\uB2C8\uB2E4. \uAC19\uC740 \uC81C\uD488\uC774\uB77C\uB3C4 \uC81C\uBAA9, \uC2A4\uD06C\uB9B0\uC0F7, \uB0A0\uC9DC\uAC00 \uB2EC\uB77C\uC9C0\uBA74 \uC2E0\uB8B0\uB3C4\uAC00 \uB5A8\uC5B4\uC9C0\uACE0 /products/${appSlug}\uB85C \uC774\uC5B4\uC9C0\uB294 \uB9C1\uD06C\uB3C4 \uC57D\uD574\uC9D1\uB2C8\uB2E4.</p>`
|
|
2050
|
-
);
|
|
2051
|
-
lines.push(`<h3>\uD754\uD788 \uB193\uCE58\uB294 \uC2E0\uD638</h3>`);
|
|
2052
|
-
lines.push(
|
|
2053
|
-
`<p>\uC2A4\uD1A0\uC5B4 \uBA54\uC2DC\uC9C0\uC640 \uBE14\uB85C\uADF8 \uCE74\uD53C\uAC00 \uB530\uB85C \uB180\uAC70\uB098, \uCD9C\uC2DC \uB9E5\uB77D\uC774 \uBE60\uC9C0\uB294 \uACBD\uC6B0\uC785\uB2C8\uB2E4. "${topic}" \uAC19\uC740 \uC8FC\uC81C\uB294 \uB450 \uCC44\uB110\uC744 \uBAA8\uB450 \uAC74\uB4DC\uB9AC\uAE30 \uB54C\uBB38\uC5D0 \uC77C\uAD00\uC131\uC774 \uB354 \uC911\uC694\uD569\uB2C8\uB2E4.</p>`
|
|
2054
|
-
);
|
|
2055
|
-
lines.push(`<h2>${appSlug}\uB85C \uB2E4\uB9AC \uB193\uAE30</h2>`);
|
|
2056
|
-
lines.push(
|
|
2057
|
-
`<p>\uC81C\uD488 \uC774\uC57C\uAE30\uB97C \uBA3C\uC800 \uC815\uB9AC\uD558\uACE0 \uBE14\uB85C\uADF8 \uBC84\uC804\uC73C\uB85C \uC7AC\uC0AC\uC6A9\uD569\uB2C8\uB2E4. \uD575\uC2EC \uC8FC\uC7A5\uACFC \uC99D\uAC70\uB294 \uC720\uC9C0\uD558\uB418, \uAC80\uC0C9 \uC758\uB3C4\uC5D0 \uB9DE\uCDB0 \uD0A4\uC6CC\uB4DC\uC640 \uC608\uC2DC\uB97C \uC870\uC815\uD558\uACE0 \uC81C\uD488 \uC2AC\uB7EC\uADF8\uB97C \uD568\uAED8 \uB178\uCD9C\uD569\uB2C8\uB2E4.</p>`
|
|
2058
|
-
);
|
|
2059
|
-
lines.push(`<h3>\uC801\uC6A9 \uCCB4\uD06C\uB9AC\uC2A4\uD2B8</h3>`);
|
|
2060
|
-
lines.push(
|
|
2061
|
-
`<ul>
|
|
2062
|
-
<li>\uC2A4\uD1A0\uC5B4 \uD5E4\uB4DC\uB77C\uC778\uC744 \uC778\uD2B8\uB85C\uC5D0 \uC7AC\uC0AC\uC6A9\uD558\uACE0 \uB3D9\uC77C\uD55C \uC8FC\uC7A5\uC73C\uB85C \uD480\uC5B4\uAC00\uAE30</li>
|
|
2063
|
-
<li>"${topic}"\uB97C \uC704\uD55C SEO \uD0A4\uC6CC\uB4DC\uB97C ASO \uD0A4\uC6CC\uB4DC\uC640 \uB9E4\uD551\uD558\uAE30</li>
|
|
2064
|
-
<li>\uC2E0\uAE30\uB2A5/\uC2A4\uD06C\uB9B0\uC0F7/\uBCC0\uACBD \uC0AC\uD56D\uC744 \uBE14\uB85C\uADF8 \uBCF8\uBB38\uC5D0 \uC9E7\uAC8C \uC5F0\uACB0\uD558\uAE30</li>
|
|
2065
|
-
<li>\uB9C8\uC9C0\uB9C9 \uBB38\uB2E8\uC5D0\uC11C <code>/products/${appSlug}</code>\uB85C \uC790\uC5F0\uC2A4\uB7EC\uC6B4 CTA \uBC30\uCE58</li>
|
|
2066
|
-
</ul>`
|
|
2067
|
-
);
|
|
2068
|
-
lines.push(`<h2>\uC0AC\uB840 \uD750\uB984 \uC608\uC2DC</h2>`);
|
|
2069
|
-
lines.push(
|
|
2070
|
-
`<p>\uCD5C\uADFC \uB9B4\uB9AC\uC2A4\uB97C \uD558\uB098 \uACE8\uB77C "${topic}"\uACFC \uC5B4\uB5BB\uAC8C \uB9DE\uBB3C\uB9AC\uB294\uC9C0 \uC124\uBA85\uD558\uACE0, \uC22B\uC790\xB7\uC778\uC6A9\xB7\uC2A4\uD06C\uB9B0\uC0F7 \uC911 \uD558\uB098\uB85C \uC99D\uAC70\uB97C \uB0A8\uAE30\uC138\uC694. h2/h3 \uAD6C\uC870\uB97C \uACE0\uC815\uD558\uBA74 \uB2E4\uAD6D\uC5B4 \uD655\uC7A5\uB3C4 \uC218\uC6D4\uD574\uC9D1\uB2C8\uB2E4.</p>`
|
|
2071
|
-
);
|
|
2072
|
-
lines.push(`<h2>\uB9C8\uBB34\uB9AC</h2>`);
|
|
2073
|
-
lines.push(
|
|
2074
|
-
`<p>${appSlug}\uB294 \uBE14\uB85C\uADF8\uC640 \uC81C\uD488 \uD398\uC774\uC9C0\uAC00 \uC11C\uB85C \uD2B8\uB798\uD53D\uC744 \uC8FC\uACE0\uBC1B\uB3C4\uB85D \uC124\uACC4\uD588\uC2B5\uB2C8\uB2E4. \uC774 HTML\uC744 <code>/blogs/${appSlug}/${meta.slug}/${meta.locale}.html</code> \uC704\uCE58\uC5D0 \uC800\uC7A5\uD558\uACE0 \uC81C\uD488 \uC0C1\uC138\uC5D0\uC11C \uB9C1\uD06C\uB97C \uAC78\uC5B4\uB450\uC138\uC694.</p>`
|
|
2075
|
-
);
|
|
2076
|
-
lines.push(
|
|
2077
|
-
`<p><strong>CTA:</strong> \uC81C\uD488 \uD398\uC774\uC9C0 <a href="/products/${appSlug}">/products/${appSlug}</a>\uC5D0\uC11C \uB354 \uC790\uC138\uD788 \uC0B4\uD3B4\uBCF4\uC138\uC694.</p>`
|
|
2078
|
-
);
|
|
2079
|
-
return lines.join("\n\n");
|
|
2080
|
-
}
|
|
2081
|
-
function renderBlogBody(options) {
|
|
2082
|
-
if (isKoreanLocale(options.meta.locale)) {
|
|
2083
|
-
return renderKoreanBody(options);
|
|
2084
|
-
}
|
|
2085
|
-
return renderEnglishBody(options);
|
|
2086
|
-
}
|
|
2087
1972
|
function buildBlogHtmlDocument(options) {
|
|
2088
1973
|
const metaBlock = renderBlogMetaBlock(options.meta);
|
|
2089
|
-
const body =
|
|
2090
|
-
meta: options.meta,
|
|
2091
|
-
topic: options.topic,
|
|
2092
|
-
appSlug: options.appSlug,
|
|
2093
|
-
includeRelativeImageExample: options.includeRelativeImageExample,
|
|
2094
|
-
relativeImagePath: options.relativeImagePath
|
|
2095
|
-
});
|
|
1974
|
+
const body = options.content.trim();
|
|
2096
1975
|
return `${metaBlock}
|
|
2097
1976
|
${body}`;
|
|
2098
1977
|
}
|
|
2099
|
-
function resolveTargetLocales(input
|
|
1978
|
+
function resolveTargetLocales(input) {
|
|
2100
1979
|
if (input.locales?.length) {
|
|
2101
1980
|
const locales = input.locales.map((loc) => loc.trim()).filter(Boolean);
|
|
2102
1981
|
return Array.from(new Set(locales));
|
|
2103
1982
|
}
|
|
2104
|
-
const fallback = input.locale?.trim()
|
|
1983
|
+
const fallback = input.locale?.trim();
|
|
2105
1984
|
return fallback ? [fallback] : [];
|
|
2106
1985
|
}
|
|
2107
1986
|
function getBlogOutputPaths(options) {
|
|
@@ -2115,6 +1994,63 @@ function getBlogOutputPaths(options) {
|
|
|
2115
1994
|
const publicBasePath = toPublicBlogBase(options.appSlug, options.slug);
|
|
2116
1995
|
return { baseDir, filePath, publicBasePath };
|
|
2117
1996
|
}
|
|
1997
|
+
function parseBlogHtml(htmlContent) {
|
|
1998
|
+
const metaBlockRegex = /<!--BLOG_META\s*\n([\s\S]*?)\n-->/;
|
|
1999
|
+
const match = htmlContent.match(metaBlockRegex);
|
|
2000
|
+
if (!match) {
|
|
2001
|
+
return { meta: null, body: htmlContent.trim() };
|
|
2002
|
+
}
|
|
2003
|
+
try {
|
|
2004
|
+
const metaJson = match[1].trim();
|
|
2005
|
+
const meta = JSON.parse(metaJson);
|
|
2006
|
+
const body = htmlContent.replace(metaBlockRegex, "").trim();
|
|
2007
|
+
return { meta, body };
|
|
2008
|
+
} catch (error) {
|
|
2009
|
+
return { meta: null, body: htmlContent.trim() };
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
function findExistingBlogPosts({
|
|
2013
|
+
appSlug,
|
|
2014
|
+
locale,
|
|
2015
|
+
publicDir,
|
|
2016
|
+
limit = 2
|
|
2017
|
+
}) {
|
|
2018
|
+
const blogAppDir = path7.join(publicDir, BLOG_ROOT, appSlug);
|
|
2019
|
+
if (!fs7.existsSync(blogAppDir)) {
|
|
2020
|
+
return [];
|
|
2021
|
+
}
|
|
2022
|
+
const posts = [];
|
|
2023
|
+
const subdirs = fs7.readdirSync(blogAppDir, { withFileTypes: true });
|
|
2024
|
+
for (const subdir of subdirs) {
|
|
2025
|
+
if (!subdir.isDirectory()) continue;
|
|
2026
|
+
const localeFile = path7.join(blogAppDir, subdir.name, `${locale}.html`);
|
|
2027
|
+
if (!fs7.existsSync(localeFile)) continue;
|
|
2028
|
+
try {
|
|
2029
|
+
const htmlContent = fs7.readFileSync(localeFile, "utf-8");
|
|
2030
|
+
const { meta, body } = parseBlogHtml(htmlContent);
|
|
2031
|
+
if (meta && meta.locale === locale) {
|
|
2032
|
+
posts.push({
|
|
2033
|
+
filePath: localeFile,
|
|
2034
|
+
meta,
|
|
2035
|
+
body,
|
|
2036
|
+
publishedAt: meta.publishedAt
|
|
2037
|
+
});
|
|
2038
|
+
}
|
|
2039
|
+
} catch (error) {
|
|
2040
|
+
continue;
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
posts.sort((a, b) => {
|
|
2044
|
+
const dateA = new Date(a.publishedAt).getTime();
|
|
2045
|
+
const dateB = new Date(b.publishedAt).getTime();
|
|
2046
|
+
return dateB - dateA;
|
|
2047
|
+
});
|
|
2048
|
+
return posts.slice(0, limit).map(({ filePath, meta, body }) => ({
|
|
2049
|
+
filePath,
|
|
2050
|
+
meta,
|
|
2051
|
+
body
|
|
2052
|
+
}));
|
|
2053
|
+
}
|
|
2118
2054
|
|
|
2119
2055
|
// src/tools/create-blog-html.ts
|
|
2120
2056
|
var toJsonSchema4 = zodToJsonSchema5;
|
|
@@ -2125,28 +2061,30 @@ var createBlogHtmlInputSchema = z5.object({
|
|
|
2125
2061
|
"English title used for slug (kebab-case). Falls back to topic when omitted."
|
|
2126
2062
|
),
|
|
2127
2063
|
topic: z5.string().trim().min(1, "topic is required").describe("Topic/angle to write about in the blog body"),
|
|
2128
|
-
locale: z5.string().trim().
|
|
2064
|
+
locale: z5.string().trim().min(1, "locale is required").describe(
|
|
2065
|
+
"Primary locale (e.g., 'en-US', 'ko-KR'). Required to determine the language for blog content generation."
|
|
2066
|
+
),
|
|
2129
2067
|
locales: z5.array(z5.string().trim().min(1)).optional().describe(
|
|
2130
|
-
"Optional list of locales to generate. Each locale gets its own HTML file."
|
|
2068
|
+
"Optional list of locales to generate. Each locale gets its own HTML file. If provided, locale parameter is ignored."
|
|
2131
2069
|
),
|
|
2132
|
-
|
|
2133
|
-
"
|
|
2070
|
+
content: z5.string().trim().min(1, "content is required").describe(
|
|
2071
|
+
"HTML content for the blog body. You (the LLM) must generate this HTML content based on the topic and locale. Structure should follow the pattern in public/en-US.html: paragraphs (<p>), headings (<h2>, <h3>), images (<img>), lists (<ul>, <li>), horizontal rules (<hr>), etc. The content should be written in the language corresponding to the locale."
|
|
2072
|
+
),
|
|
2073
|
+
description: z5.string().trim().min(1, "description is required").describe(
|
|
2074
|
+
"Meta description for the blog post. You (the LLM) must generate this based on the topic and locale. Should be a concise summary of the blog content in the language corresponding to the locale."
|
|
2075
|
+
),
|
|
2076
|
+
tags: z5.array(z5.string().trim().min(1)).optional().describe(
|
|
2077
|
+
"Optional tags for BLOG_META. Defaults to tags derived from topic."
|
|
2134
2078
|
),
|
|
2135
|
-
tags: z5.array(z5.string().trim().min(1)).optional().describe("Optional tags for BLOG_META. Defaults to tags derived from topic."),
|
|
2136
2079
|
coverImage: z5.string().trim().optional().describe(
|
|
2137
2080
|
"Cover image path. Relative paths rewrite to /blogs/<app>/<slug>/..., default is /products/<appSlug>/og-image.png."
|
|
2138
2081
|
),
|
|
2139
|
-
includeRelativeImageExample: z5.boolean().optional().default(false).describe(
|
|
2140
|
-
"Inject a relative image example (./images/hero.png) into the body to demonstrate path rewriting."
|
|
2141
|
-
),
|
|
2142
|
-
relativeImagePath: z5.string().trim().optional().describe("Override the relative image path (default ./images/hero.png)."),
|
|
2143
2082
|
publishedAt: z5.string().trim().regex(DATE_REGEX2, "publishedAt must use YYYY-MM-DD").optional().describe("Publish date (YYYY-MM-DD). Defaults to today."),
|
|
2144
2083
|
modifiedAt: z5.string().trim().regex(DATE_REGEX2, "modifiedAt must use YYYY-MM-DD").optional().describe("Last modified date (YYYY-MM-DD). Defaults to publishedAt."),
|
|
2145
2084
|
overwrite: z5.boolean().optional().default(false).describe("Overwrite existing files when true (default: false).")
|
|
2146
2085
|
}).describe("Generate static HTML blog posts with BLOG_META headers.");
|
|
2147
2086
|
var jsonSchema5 = toJsonSchema4(createBlogHtmlInputSchema, {
|
|
2148
2087
|
name: "CreateBlogHtmlInput",
|
|
2149
|
-
target: "openApi3",
|
|
2150
2088
|
$refStrategy: "none"
|
|
2151
2089
|
});
|
|
2152
2090
|
var inputSchema5 = jsonSchema5.definitions?.CreateBlogHtmlInput || jsonSchema5;
|
|
@@ -2154,19 +2092,33 @@ var createBlogHtmlTool = {
|
|
|
2154
2092
|
name: "create-blog-html",
|
|
2155
2093
|
description: `Generate HTML blog posts under public/blogs/<appSlug>/<slug>/<locale>.html with a BLOG_META block.
|
|
2156
2094
|
|
|
2095
|
+
CRITICAL: WRITING STYLE CONSISTENCY
|
|
2096
|
+
Before generating content, you MUST:
|
|
2097
|
+
1. Read existing blog posts from public/blogs/<appSlug>/*/<locale>.html (use findExistingBlogPosts utility or read files directly)
|
|
2098
|
+
2. Analyze the writing style, tone, and format from 2 existing posts in the same locale
|
|
2099
|
+
3. Match that exact writing style when generating the new blog post content and description
|
|
2100
|
+
4. Maintain consistency in: paragraph structure, heading usage, tone, formality level, and overall format
|
|
2101
|
+
|
|
2102
|
+
IMPORTANT REQUIREMENTS:
|
|
2103
|
+
1. The 'locale' parameter is REQUIRED. If the user does not provide a locale, you MUST ask them to specify which language/locale they want to write the blog in (e.g., 'en-US', 'ko-KR', 'ja-JP', etc.).
|
|
2104
|
+
2. The 'content' parameter is REQUIRED. You (the LLM) must generate the HTML content based on the 'topic' and 'locale' provided by the user. The content should be written in the language corresponding to the locale AND match the writing style of existing blog posts for that locale.
|
|
2105
|
+
3. The 'description' parameter is REQUIRED. You (the LLM) must generate this based on the topic, locale, AND the writing style of existing blog posts.
|
|
2106
|
+
|
|
2157
2107
|
Slug rules:
|
|
2158
2108
|
- slug = slugify(English title, kebab-case ASCII)
|
|
2159
2109
|
- path: public/blogs/<appSlug>/<slug>/<locale>.html
|
|
2160
2110
|
- coverImage default: /products/<appSlug>/og-image.png (relative paths are rewritten under /blogs/<app>/<slug>/)
|
|
2161
2111
|
- overwrite defaults to false (throws when file exists)
|
|
2162
2112
|
|
|
2163
|
-
|
|
2164
|
-
-
|
|
2165
|
-
-
|
|
2166
|
-
-
|
|
2167
|
-
- Conclusion + CTA linking to /products/<appSlug>
|
|
2113
|
+
HTML Structure (follows public/en-US.html pattern):
|
|
2114
|
+
- BLOG_META block at the top with JSON metadata
|
|
2115
|
+
- HTML body content: paragraphs (<p>), headings (<h2>, <h3>), images (<img>), lists (<ul>, <li>), horizontal rules (<hr>), etc.
|
|
2116
|
+
- You must generate the HTML content based on the topic, making it relevant and engaging for the target locale's language, while maintaining consistency with existing blog posts.
|
|
2168
2117
|
|
|
2169
|
-
Supports multiple locales when locales[] is provided
|
|
2118
|
+
Supports multiple locales when locales[] is provided. Each locale gets its own HTML file. For each locale, you must:
|
|
2119
|
+
1. Read existing posts in that locale to understand the writing style
|
|
2120
|
+
2. Generate appropriate content in that locale's language
|
|
2121
|
+
3. Match the writing style and format of existing posts`,
|
|
2170
2122
|
inputSchema: inputSchema5
|
|
2171
2123
|
};
|
|
2172
2124
|
async function handleCreateBlogHtml(input) {
|
|
@@ -2178,20 +2130,36 @@ async function handleCreateBlogHtml(input) {
|
|
|
2178
2130
|
description,
|
|
2179
2131
|
tags,
|
|
2180
2132
|
coverImage,
|
|
2181
|
-
includeRelativeImageExample = false,
|
|
2182
|
-
relativeImagePath,
|
|
2183
2133
|
publishedAt,
|
|
2184
2134
|
modifiedAt,
|
|
2185
|
-
overwrite = false
|
|
2135
|
+
overwrite = false,
|
|
2136
|
+
content
|
|
2186
2137
|
} = input;
|
|
2138
|
+
if (!content || !content.trim()) {
|
|
2139
|
+
throw new Error(
|
|
2140
|
+
"Content is required. Please provide HTML content for the blog body based on the topic and locale."
|
|
2141
|
+
);
|
|
2142
|
+
}
|
|
2187
2143
|
const resolvedTitle = title && title.trim() || topic.trim();
|
|
2188
2144
|
const slug = slugifyTitle(resolvedTitle);
|
|
2189
2145
|
const targetLocales = resolveTargetLocales(input);
|
|
2190
2146
|
if (!targetLocales.length) {
|
|
2191
|
-
throw new Error(
|
|
2147
|
+
throw new Error(
|
|
2148
|
+
"Locale is required. Please specify which language/locale you want to write the blog in (e.g., 'en-US', 'ko-KR', 'ja-JP')."
|
|
2149
|
+
);
|
|
2150
|
+
}
|
|
2151
|
+
const existingPostsByLocale = {};
|
|
2152
|
+
for (const locale of targetLocales) {
|
|
2153
|
+
const existingPosts = findExistingBlogPosts({
|
|
2154
|
+
appSlug,
|
|
2155
|
+
locale,
|
|
2156
|
+
publicDir,
|
|
2157
|
+
limit: 2
|
|
2158
|
+
});
|
|
2159
|
+
if (existingPosts.length > 0) {
|
|
2160
|
+
existingPostsByLocale[locale] = existingPosts;
|
|
2161
|
+
}
|
|
2192
2162
|
}
|
|
2193
|
-
const shouldIncludeRelativeImage = includeRelativeImageExample || Boolean(relativeImagePath);
|
|
2194
|
-
const relativeImage = shouldIncludeRelativeImage ? resolveRelativeImagePath(appSlug, slug, relativeImagePath) : void 0;
|
|
2195
2163
|
const output = {
|
|
2196
2164
|
slug,
|
|
2197
2165
|
baseDir: path8.join(publicDir, "blogs", appSlug, slug),
|
|
@@ -2207,7 +2175,9 @@ async function handleCreateBlogHtml(input) {
|
|
|
2207
2175
|
publicDir
|
|
2208
2176
|
})
|
|
2209
2177
|
);
|
|
2210
|
-
const existing = plannedFiles.filter(
|
|
2178
|
+
const existing = plannedFiles.filter(
|
|
2179
|
+
({ filePath }) => fs8.existsSync(filePath)
|
|
2180
|
+
);
|
|
2211
2181
|
if (existing.length > 0 && !overwrite) {
|
|
2212
2182
|
const existingList = existing.map((f) => f.filePath).join("\n- ");
|
|
2213
2183
|
throw new Error(
|
|
@@ -2215,7 +2185,7 @@ async function handleCreateBlogHtml(input) {
|
|
|
2215
2185
|
- ${existingList}`
|
|
2216
2186
|
);
|
|
2217
2187
|
}
|
|
2218
|
-
|
|
2188
|
+
fs8.mkdirSync(output.baseDir, { recursive: true });
|
|
2219
2189
|
for (const locale of targetLocales) {
|
|
2220
2190
|
const { filePath } = getBlogOutputPaths({
|
|
2221
2191
|
appSlug,
|
|
@@ -2239,12 +2209,9 @@ async function handleCreateBlogHtml(input) {
|
|
|
2239
2209
|
output.metaByLocale[locale] = meta;
|
|
2240
2210
|
const html = buildBlogHtmlDocument({
|
|
2241
2211
|
meta,
|
|
2242
|
-
|
|
2243
|
-
appSlug,
|
|
2244
|
-
includeRelativeImageExample: shouldIncludeRelativeImage,
|
|
2245
|
-
relativeImagePath: relativeImage
|
|
2212
|
+
content
|
|
2246
2213
|
});
|
|
2247
|
-
|
|
2214
|
+
fs8.writeFileSync(filePath, html, "utf-8");
|
|
2248
2215
|
output.files.push({ locale, path: filePath });
|
|
2249
2216
|
}
|
|
2250
2217
|
const summaryLines = [
|
|
@@ -2256,11 +2223,25 @@ async function handleCreateBlogHtml(input) {
|
|
|
2256
2223
|
"Files:",
|
|
2257
2224
|
...output.files.map((file) => `- ${file.locale}: ${file.path}`)
|
|
2258
2225
|
];
|
|
2226
|
+
const styleReferenceInfo = [];
|
|
2227
|
+
for (const [locale, posts] of Object.entries(existingPostsByLocale)) {
|
|
2228
|
+
if (posts.length > 0) {
|
|
2229
|
+
styleReferenceInfo.push(
|
|
2230
|
+
`
|
|
2231
|
+
Writing style reference for ${locale}: Found ${posts.length} existing post(s) used for style consistency.`
|
|
2232
|
+
);
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
if (styleReferenceInfo.length === 0) {
|
|
2236
|
+
styleReferenceInfo.push(
|
|
2237
|
+
"\nNote: No existing blog posts found for style reference. This is the first post for this app/locale combination."
|
|
2238
|
+
);
|
|
2239
|
+
}
|
|
2259
2240
|
return {
|
|
2260
2241
|
content: [
|
|
2261
2242
|
{
|
|
2262
2243
|
type: "text",
|
|
2263
|
-
text: summaryLines.join("\n")
|
|
2244
|
+
text: summaryLines.join("\n") + styleReferenceInfo.join("")
|
|
2264
2245
|
}
|
|
2265
2246
|
]
|
|
2266
2247
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -204,6 +204,61 @@ declare function isGooglePlayMultilingual(data: GooglePlayAsoData | GooglePlayMu
|
|
|
204
204
|
*/
|
|
205
205
|
declare function isAppStoreMultilingual(data: AppStoreAsoData | AppStoreMultilingualAsoData | undefined): data is AppStoreMultilingualAsoData;
|
|
206
206
|
|
|
207
|
+
/**
|
|
208
|
+
* Blog type definitions
|
|
209
|
+
* Used by both MCP tools and web application
|
|
210
|
+
*/
|
|
211
|
+
/**
|
|
212
|
+
* Blog Meta Block (raw from HTML comment)
|
|
213
|
+
* All fields are optional as they may be parsed from HTML or defaults
|
|
214
|
+
*/
|
|
215
|
+
interface BlogMetaBlock {
|
|
216
|
+
title: string;
|
|
217
|
+
description: string;
|
|
218
|
+
appSlug: string;
|
|
219
|
+
slug: string;
|
|
220
|
+
locale?: string;
|
|
221
|
+
coverImage?: string;
|
|
222
|
+
publishedAt?: string;
|
|
223
|
+
modifiedAt?: string;
|
|
224
|
+
tags?: string[];
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Blog Meta (resolved with UnifiedLocale)
|
|
228
|
+
* Used by web application after normalization
|
|
229
|
+
*/
|
|
230
|
+
interface BlogMeta extends BlogMetaBlock {
|
|
231
|
+
locale: UnifiedLocale;
|
|
232
|
+
coverImage?: string;
|
|
233
|
+
publishedAt?: string;
|
|
234
|
+
modifiedAt?: string;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Blog Article (complete parsed article with HTML content)
|
|
238
|
+
*/
|
|
239
|
+
interface BlogArticle {
|
|
240
|
+
appSlug: string;
|
|
241
|
+
slug: string;
|
|
242
|
+
locale: UnifiedLocale;
|
|
243
|
+
html: string;
|
|
244
|
+
meta: BlogMeta;
|
|
245
|
+
filePath: string;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Blog Summary (aggregated metadata across locales)
|
|
249
|
+
*/
|
|
250
|
+
interface BlogSummary {
|
|
251
|
+
appSlug: string;
|
|
252
|
+
slug: string;
|
|
253
|
+
title: string;
|
|
254
|
+
description: string;
|
|
255
|
+
coverImage?: string;
|
|
256
|
+
publishedAt?: string;
|
|
257
|
+
modifiedAt?: string;
|
|
258
|
+
locales: UnifiedLocale[];
|
|
259
|
+
defaultLocale: UnifiedLocale;
|
|
260
|
+
}
|
|
261
|
+
|
|
207
262
|
interface ImageAsset {
|
|
208
263
|
src: string;
|
|
209
264
|
alt: string;
|
|
@@ -366,10 +421,60 @@ interface ProductLocale {
|
|
|
366
421
|
landing?: LandingPageLocale;
|
|
367
422
|
}
|
|
368
423
|
|
|
424
|
+
/**
|
|
425
|
+
* Site data type definitions
|
|
426
|
+
* Structure for public/site/ directory
|
|
427
|
+
* - contacts.json: Contact information
|
|
428
|
+
* - locales/en-US.json: Localized content (meta, hero, developer)
|
|
429
|
+
*/
|
|
430
|
+
interface SiteData {
|
|
431
|
+
meta: {
|
|
432
|
+
title: string;
|
|
433
|
+
description: string;
|
|
434
|
+
};
|
|
435
|
+
hero: {
|
|
436
|
+
brand: string;
|
|
437
|
+
tagline: string;
|
|
438
|
+
subtitle: string;
|
|
439
|
+
};
|
|
440
|
+
contacts: {
|
|
441
|
+
id: string;
|
|
442
|
+
label: string;
|
|
443
|
+
icon: string;
|
|
444
|
+
value: string;
|
|
445
|
+
}[];
|
|
446
|
+
developer: {
|
|
447
|
+
name: string;
|
|
448
|
+
heading: string;
|
|
449
|
+
role: string;
|
|
450
|
+
bio: string;
|
|
451
|
+
principles: string[];
|
|
452
|
+
stack: string[];
|
|
453
|
+
now: string;
|
|
454
|
+
availability: string;
|
|
455
|
+
likes: string[];
|
|
456
|
+
location?: string;
|
|
457
|
+
timezone?: string;
|
|
458
|
+
visited: {
|
|
459
|
+
countries: {
|
|
460
|
+
code: string;
|
|
461
|
+
name: string;
|
|
462
|
+
flagEmoji: string;
|
|
463
|
+
cities: string[];
|
|
464
|
+
}[];
|
|
465
|
+
};
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
|
|
369
469
|
/**
|
|
370
470
|
* Types for the create-blog-html MCP tool
|
|
371
471
|
*/
|
|
372
|
-
|
|
472
|
+
/**
|
|
473
|
+
* BlogMetaOutput for MCP tool output
|
|
474
|
+
* All fields are required (resolved values)
|
|
475
|
+
* Note: This is separate from blog.types.ts BlogMeta which uses UnifiedLocale
|
|
476
|
+
*/
|
|
477
|
+
interface BlogMetaOutput {
|
|
373
478
|
title: string;
|
|
374
479
|
description: string;
|
|
375
480
|
appSlug: string;
|
|
@@ -394,13 +499,18 @@ interface CreateBlogHtmlInput {
|
|
|
394
499
|
*/
|
|
395
500
|
topic: string;
|
|
396
501
|
/**
|
|
397
|
-
* Single locale to generate (
|
|
502
|
+
* Single locale to generate (REQUIRED). Ignored when locales[] is provided.
|
|
398
503
|
*/
|
|
399
|
-
locale
|
|
504
|
+
locale: string;
|
|
400
505
|
/**
|
|
401
506
|
* Optional list of locales to generate. Each gets its own HTML file.
|
|
402
507
|
*/
|
|
403
508
|
locales?: string[];
|
|
509
|
+
/**
|
|
510
|
+
* HTML content for the blog body. REQUIRED. The LLM must generate this based on the topic and locale.
|
|
511
|
+
* Structure should follow public/en-US.html pattern.
|
|
512
|
+
*/
|
|
513
|
+
content: string;
|
|
404
514
|
/**
|
|
405
515
|
* Meta description override. If absent, a locale-aware summary is generated from topic/appSlug.
|
|
406
516
|
*/
|
|
@@ -443,7 +553,7 @@ interface CreateBlogHtmlResult {
|
|
|
443
553
|
baseDir: string;
|
|
444
554
|
files: GeneratedBlogFile[];
|
|
445
555
|
coverImage: string;
|
|
446
|
-
metaByLocale: Record<string,
|
|
556
|
+
metaByLocale: Record<string, BlogMetaOutput>;
|
|
447
557
|
}
|
|
448
558
|
|
|
449
559
|
/**
|
|
@@ -701,4 +811,4 @@ declare function getPublicDir(): string;
|
|
|
701
811
|
*/
|
|
702
812
|
declare function getProductsDir(): string;
|
|
703
813
|
|
|
704
|
-
export { APP_STORE_TO_UNIFIED, type AppMetaLinks, type AppPageData, type AppStoreAsoData, type AppStoreInfoLocalization, type AppStoreLocale, type AppStoreMultilingualAsoData, type AppStoreReleaseNote, type AppStoreScreenshotDisplayType, type AppStoreScreenshots, type AppStoreVersionLocalization, type AsoData, type AsoLocaleContent, type AsoTemplate, type BlogMeta, type CreateBlogHtmlInput, type CreateBlogHtmlResult, DEFAULT_LOCALE, type DeepPartial, type FeatureItem, GOOGLE_PLAY_TO_UNIFIED, type GeneratedBlogFile, type GooglePlayAsoData, type GooglePlayImageType, type GooglePlayListing, type GooglePlayLocale, type GooglePlayMultilingualAsoData, type GooglePlayReleaseNote, type GooglePlayScreenshotType, type GooglePlayScreenshots, type ImageAsset, type LandingCta, type LandingFeatures, type LandingHero, type LandingPage, type LandingPageLocale, type LandingReviews, type LandingScreenshots, type LayoutColors, type ProductConfig, type ProductContent, type ProductLocale, type ProductMetadata, type ProductScreenshots, type SupportedLocale, type Testimonial, UNIFIED_LOCALES, UNIFIED_TO_APP_STORE, UNIFIED_TO_GOOGLE_PLAY, type UnifiedLocale, appStoreToGooglePlay, appStoreToUnified, appStoreToUnifiedBatch, convertObjectFromAppStore, convertObjectFromGooglePlay, convertObjectToAppStore, convertObjectToGooglePlay, getAsoDataDir, getProductsDir, getPublicDir, getPullDataDir, getPushDataDir, googlePlayToAppStore, googlePlayToUnified, googlePlayToUnifiedBatch, isAppStoreLocale, isAppStoreMultilingual, isGooglePlayLocale, isGooglePlayMultilingual, isSupportedLocale, loadAsoFromConfig, saveAsoToAsoDir, saveAsoToConfig, unifiedToAppStore, unifiedToAppStoreBatch, unifiedToBothPlatforms, unifiedToGooglePlay, unifiedToGooglePlayBatch };
|
|
814
|
+
export { APP_STORE_TO_UNIFIED, type AppMetaLinks, type AppPageData, type AppStoreAsoData, type AppStoreInfoLocalization, type AppStoreLocale, type AppStoreMultilingualAsoData, type AppStoreReleaseNote, type AppStoreScreenshotDisplayType, type AppStoreScreenshots, type AppStoreVersionLocalization, type AsoData, type AsoLocaleContent, type AsoTemplate, type BlogArticle, type BlogMeta, type BlogMetaBlock, type BlogMetaOutput, type BlogSummary, type CreateBlogHtmlInput, type CreateBlogHtmlResult, DEFAULT_LOCALE, type DeepPartial, type FeatureItem, GOOGLE_PLAY_TO_UNIFIED, type GeneratedBlogFile, type GooglePlayAsoData, type GooglePlayImageType, type GooglePlayListing, type GooglePlayLocale, type GooglePlayMultilingualAsoData, type GooglePlayReleaseNote, type GooglePlayScreenshotType, type GooglePlayScreenshots, type ImageAsset, type LandingCta, type LandingFeatures, type LandingHero, type LandingPage, type LandingPageLocale, type LandingReviews, type LandingScreenshots, type LayoutColors, type ProductConfig, type ProductContent, type ProductLocale, type ProductMetadata, type ProductScreenshots, type SiteData, type SupportedLocale, type Testimonial, UNIFIED_LOCALES, UNIFIED_TO_APP_STORE, UNIFIED_TO_GOOGLE_PLAY, type UnifiedLocale, appStoreToGooglePlay, appStoreToUnified, appStoreToUnifiedBatch, convertObjectFromAppStore, convertObjectFromGooglePlay, convertObjectToAppStore, convertObjectToGooglePlay, getAsoDataDir, getProductsDir, getPublicDir, getPullDataDir, getPushDataDir, googlePlayToAppStore, googlePlayToUnified, googlePlayToUnifiedBatch, isAppStoreLocale, isAppStoreMultilingual, isGooglePlayLocale, isGooglePlayMultilingual, isSupportedLocale, loadAsoFromConfig, saveAsoToAsoDir, saveAsoToConfig, unifiedToAppStore, unifiedToAppStoreBatch, unifiedToBothPlatforms, unifiedToGooglePlay, unifiedToGooglePlayBatch };
|