@webnumseoagent/next 0.1.24 → 0.1.25

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/bin/cli.mjs CHANGED
@@ -97,13 +97,17 @@ async function init() {
97
97
  ok("Пакет @webnumseoagent/next установлен — импорт из пакета.");
98
98
  } else {
99
99
  const vendor = path.join(root, "seoagent");
100
+ await fs.mkdir(vendor, { recursive: true });
101
+ // Вендор-файлы адаптера — генерируемые (их не правят руками): при повторном init ОБНОВЛЯЕМ,
102
+ // если содержимое изменилось. Иначе апгрейд оставит старый client.ts без новых экспортов
103
+ // (напр. seoMerchantFeedXml), а свежесозданный роут импортнёт несуществующий символ → build падёт.
100
104
  for (const f of ["client.ts", "jsonld.tsx", "sitemap-xsl.ts", "sitemap-build.ts", "redirects.ts"]) {
101
105
  const dest = path.join(vendor, f);
102
- if (!(await exists(dest))) {
103
- await fs.mkdir(vendor, { recursive: true });
104
- await fs.copyFile(path.join(pkgRoot, f), dest);
105
- ok(`seoagent/${f}`);
106
- } else skip(`seoagent/${f}`);
106
+ const src = await fs.readFile(path.join(pkgRoot, f));
107
+ const cur = await fs.readFile(dest).catch(() => null);
108
+ if (cur && cur.equals(src)) { skip(`seoagent/${f}`); continue; }
109
+ await fs.writeFile(dest, src);
110
+ ok(`seoagent/${f}${cur ? " (обновлён)" : ""}`);
107
111
  }
108
112
  const imp = importPath(appDir, "sitemap.ts");
109
113
  importClient = `${imp}/client`;
@@ -231,6 +235,14 @@ async function init() {
231
235
  `import { seoIndexNowKey } from "${clientFor("seoagent-indexnow.txt/route.ts")}";\nexport const dynamic = "force-dynamic";\nexport async function GET() {\n return new Response(await seoIndexNowKey(), { headers: { "content-type": "text/plain; charset=utf-8" } });\n}\n`,
232
236
  );
233
237
 
238
+ // 4.3 Google Merchant Center — товарный фид /seoagent-merchant-feed.xml (RSS 2.0 + g:).
239
+ // Адаптер лишь проксирует XML с платформы (та собирает его из индекса товаров сайта).
240
+ // Пустой валидный RSS, пока фид не включён в настройках — роут никогда не отдаёт 404.
241
+ await ensureFile(
242
+ path.join(root, appDir, `seoagent-merchant-feed.xml/route.${ext}`),
243
+ `import { seoMerchantFeedXml } from "${clientFor("seoagent-merchant-feed.xml/route.ts")}";\nexport const revalidate = 300;\nexport async function GET() {\n return new Response(await seoMerchantFeedXml("/merchant-feed.xml"), { headers: { "content-type": "application/xml; charset=utf-8", "x-seoagent-merchant": "1" } });\n}\n`,
244
+ );
245
+
234
246
  // 4.5 middleware (переадресации). Next разрешает ОДИН middleware, а авто-слияние чужого
235
247
  // небезопасно → создаём свой, только если своего нет; иначе печатаем ручную инструкцию.
236
248
  {
package/client.ts CHANGED
@@ -254,6 +254,27 @@ export async function seoSitemapXml(path: string): Promise<string> {
254
254
  }
255
255
  }
256
256
 
257
+ // Товарный фид Google Merchant Center (RSS 2.0 + g:-namespace): сырой XML с платформы.
258
+ // Платформа собирает его из закэшированного индекса товаров (быстро, без живого краула).
259
+ // app/seoagent-merchant-feed.xml/route.ts:
260
+ // export async function GET() { return new Response(await seoMerchantFeedXml("/merchant-feed.xml"), {headers:{"content-type":"application/xml"}}) }
261
+ export async function seoMerchantFeedXml(path: string): Promise<string> {
262
+ // Валидный RSS-канал (title/link/description обязательны в RSS 2.0) с пустым списком товаров.
263
+ const FALLBACK = `<?xml version="1.0" encoding="UTF-8"?>\n<rss version="2.0" xmlns:g="http://base.google.com/ns/1.0">\n<channel>\n <title>Merchant feed</title>\n <link>https://seonum.uz</link>\n <description>Merchant feed temporarily unavailable</description>\n</channel>\n</rss>\n`;
264
+ if (!API || !SITE || !TOKEN) return FALLBACK;
265
+ try {
266
+ const r = await fetch(`${API}/api/seo/${SITE}${path}`, {
267
+ headers: { "x-seo-agent-key": TOKEN },
268
+ next: { revalidate: 300, tags: [`seo-${SITE}`] },
269
+ signal: AbortSignal.timeout(8000),
270
+ });
271
+ if (!r.ok) return FALLBACK;
272
+ return await r.text();
273
+ } catch {
274
+ return FALLBACK;
275
+ }
276
+ }
277
+
257
278
  export { SITEMAP_XSL } from "./sitemap-xsl";
258
279
  export { seoWrapSitemap } from "./sitemap-build";
259
280
 
package/dist/client.d.ts CHANGED
@@ -5,6 +5,7 @@ export declare function seoMeta(opts: {
5
5
  fallback?: Metadata;
6
6
  }): Promise<Metadata>;
7
7
  export declare function seoSitemapXml(path: string): Promise<string>;
8
+ export declare function seoMerchantFeedXml(path: string): Promise<string>;
8
9
  export { SITEMAP_XSL } from "./sitemap-xsl";
9
10
  export { seoWrapSitemap } from "./sitemap-build";
10
11
  export declare function seoSitemap(): Promise<MetadataRoute.Sitemap>;
package/dist/client.js CHANGED
@@ -14,6 +14,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
14
14
  exports.seoWrapSitemap = exports.SITEMAP_XSL = void 0;
15
15
  exports.seoMeta = seoMeta;
16
16
  exports.seoSitemapXml = seoSitemapXml;
17
+ exports.seoMerchantFeedXml = seoMerchantFeedXml;
17
18
  exports.seoSitemap = seoSitemap;
18
19
  exports.seoRobots = seoRobots;
19
20
  exports.seoRobotsTxt = seoRobotsTxt;
@@ -255,6 +256,29 @@ async function seoSitemapXml(path) {
255
256
  return FALLBACK;
256
257
  }
257
258
  }
259
+ // Товарный фид Google Merchant Center (RSS 2.0 + g:-namespace): сырой XML с платформы.
260
+ // Платформа собирает его из закэшированного индекса товаров (быстро, без живого краула).
261
+ // app/seoagent-merchant-feed.xml/route.ts:
262
+ // export async function GET() { return new Response(await seoMerchantFeedXml("/merchant-feed.xml"), {headers:{"content-type":"application/xml"}}) }
263
+ async function seoMerchantFeedXml(path) {
264
+ // Валидный RSS-канал (title/link/description обязательны в RSS 2.0) с пустым списком товаров.
265
+ const FALLBACK = `<?xml version="1.0" encoding="UTF-8"?>\n<rss version="2.0" xmlns:g="http://base.google.com/ns/1.0">\n<channel>\n <title>Merchant feed</title>\n <link>https://seonum.uz</link>\n <description>Merchant feed temporarily unavailable</description>\n</channel>\n</rss>\n`;
266
+ if (!API || !SITE || !TOKEN)
267
+ return FALLBACK;
268
+ try {
269
+ const r = await fetch(`${API}/api/seo/${SITE}${path}`, {
270
+ headers: { "x-seo-agent-key": TOKEN },
271
+ next: { revalidate: 300, tags: [`seo-${SITE}`] },
272
+ signal: AbortSignal.timeout(8000),
273
+ });
274
+ if (!r.ok)
275
+ return FALLBACK;
276
+ return await r.text();
277
+ }
278
+ catch {
279
+ return FALLBACK;
280
+ }
281
+ }
258
282
  var sitemap_xsl_1 = require("./sitemap-xsl");
259
283
  Object.defineProperty(exports, "SITEMAP_XSL", { enumerable: true, get: function () { return sitemap_xsl_1.SITEMAP_XSL; } });
260
284
  var sitemap_build_1 = require("./sitemap-build");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webnumseoagent/next",
3
- "version": "0.1.24",
3
+ "version": "0.1.25",
4
4
  "description": "Runtime SEO adapter + installer for Next.js App Router sites (SEO Agent platform)",
5
5
  "main": "dist/client.js",
6
6
  "types": "dist/client.d.ts",