@webnumseoagent/next 0.1.12 → 0.1.14

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
@@ -262,6 +262,27 @@ async function init() {
262
262
  }
263
263
  }
264
264
 
265
+ // 4.6 Мониторинг 404: роут-приёмник на сайте (форвардит хит в платформу с СЕРВЕРНЫМ
266
+ // токеном) + not-found-репортер (клиентский маячок с путём 404). Репортер отдаём сырым
267
+ // исходником (client-компонент из dist пакета не работает — tsc ставит "use strict" первой).
268
+ {
269
+ const reqType = ext === "ts" ? ": Request" : "";
270
+ const fwd = `export async function POST(req${reqType}) {\n const origin = req.headers.get("origin"), host = req.headers.get("host");\n if (origin && host) { try { if (new URL(origin).host !== host) return new Response(null, { status: 204 }); } catch { return new Response(null, { status: 204 }); } }\n const API = process.env.SEOAGENT_API_BASE, SITE = process.env.SEOAGENT_SITE_ID, TOKEN = process.env.SEOAGENT_TOKEN;\n if (!API || !SITE || !TOKEN) return new Response(null, { status: 204 });\n let path = "";\n try { const b = await req.json(); if (typeof b?.path === "string") path = b.path; } catch {}\n if (!path) return new Response(null, { status: 204 });\n try { await fetch(\`\${API}/api/seo/\${SITE}/not-found\`, { method: "POST", headers: { "content-type": "application/json", "x-seo-agent-key": TOKEN }, body: JSON.stringify({ path }), signal: AbortSignal.timeout(5000) }); } catch {}\n return new Response(null, { status: 204 });\n}\n`;
271
+ await ensureFile(path.join(root, appDir, `api/seoagent-404/route.${ext}`), fwd);
272
+
273
+ const reporter = `"use client";\n\nimport Link from "next/link";\nimport { usePathname } from "next/navigation";\nimport { useEffect, useRef } from "react";\n\n// Страница 404 + репортер для «Мониторинг 404» (SEO Agent).\nexport default function NotFound() {\n const pathname = usePathname();\n const lastSent = useRef(null);\n useEffect(() => {\n const p = pathname || window.location.pathname;\n if (lastSent.current === p) return;\n lastSent.current = p;\n try {\n const body = JSON.stringify({ path: p });\n if (navigator.sendBeacon) navigator.sendBeacon("/api/seoagent-404", new Blob([body], { type: "application/json" }));\n else fetch("/api/seoagent-404", { method: "POST", headers: { "content-type": "application/json" }, body, keepalive: true }).catch(() => {});\n } catch {}\n }, [pathname]);\n return (\n <main style={{ minHeight: "60vh", display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: "1rem", padding: "2rem", textAlign: "center" }}>\n <h1 style={{ fontSize: "3rem", margin: 0 }}>404</h1>\n <p style={{ color: "#64748b", margin: 0 }}>Страница не найдена.</p>\n <Link href="/" style={{ color: "#2563eb" }}>На главную</Link>\n </main>\n );\n}\n`;
274
+ const nfExt = ext === "ts" ? "tsx" : "js";
275
+ const hasNotFound = await anyExists([
276
+ `${appDir}/not-found.js`, `${appDir}/not-found.jsx`, `${appDir}/not-found.ts`, `${appDir}/not-found.tsx`,
277
+ ]);
278
+ if (hasNotFound) {
279
+ log(`\n \x1b[1mnot-found уже есть\x1b[0m — добавь репортер 404 (Мониторинг 404) в свой not-found:`);
280
+ log(` \x1b[36m"use client"; ... useEffect(() => { navigator.sendBeacon?.("/api/seoagent-404", new Blob([JSON.stringify({ path: location.pathname })], { type: "application/json" })); }, []);\x1b[0m`);
281
+ } else {
282
+ await ensureFile(path.join(root, appDir, `not-found.${nfExt}`), reporter);
283
+ }
284
+ }
285
+
265
286
  // 5. .env.local
266
287
  const envPath = path.join(root, ".env.local");
267
288
  const cur = (await exists(envPath)) ? await fs.readFile(envPath, "utf8") : "";
package/client.ts CHANGED
@@ -24,6 +24,7 @@ interface SeoFields {
24
24
  og?: { title?: string; description?: string; image?: string };
25
25
  twitter?: { card?: string; image?: string };
26
26
  jsonLd?: unknown[];
27
+ verification?: Record<string, string>; // проверочные мета-теги: metaName → content
27
28
  }
28
29
 
29
30
  async function fetchJson<T>(path: string, revalidate = 300): Promise<T | null> {
@@ -75,6 +76,11 @@ function mergeIntoMetadata(fallback: Metadata, f: SeoFields): Metadata {
75
76
  ...(f.twitter.image ? { images: [f.twitter.image] } : {}),
76
77
  };
77
78
  }
79
+ // Проверочные мета-теги веб-мастерских → <meta name content> через metadata.other.
80
+ // Мержим, не затирая существующий other сайта.
81
+ if (f.verification && typeof f.verification === "object") {
82
+ m.other = { ...(m.other ?? {}), ...f.verification };
83
+ }
78
84
  return m;
79
85
  }
80
86
 
package/dist/client.js CHANGED
@@ -75,6 +75,11 @@ function mergeIntoMetadata(fallback, f) {
75
75
  ...(f.twitter.image ? { images: [f.twitter.image] } : {}),
76
76
  };
77
77
  }
78
+ // Проверочные мета-теги веб-мастерских → <meta name content> через metadata.other.
79
+ // Мержим, не затирая существующий other сайта.
80
+ if (f.verification && typeof f.verification === "object") {
81
+ m.other = { ...(m.other ?? {}), ...f.verification };
82
+ }
78
83
  return m;
79
84
  }
80
85
  // В generateMetadata: const md = await seoMeta({ route, locale, fallback: compiled });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webnumseoagent/next",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
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",