@consilioweb/payload-seo-analyzer 1.13.0 → 1.14.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/client.cjs CHANGED
@@ -1068,6 +1068,8 @@ var fr = {
1068
1068
  bulkOptimizeMeta: "Optimiser m\xE9ta (IA)",
1069
1069
  bulkOptimizing: "Analyse\u2026",
1070
1070
  bulkConfirm: "Confirmer ?",
1071
+ optimizeSite: "Optimiser le site",
1072
+ optimizeSiteHint: "Cible automatiquement les pages \xE0 probl\xE8me (m\xE9ta manquante, sans mot-cl\xE9, score faible) \u2192 aper\xE7u \u2192 appliquer",
1071
1073
  bulkPreviewTitle: "Corrections m\xE9ta propos\xE9es",
1072
1074
  bulkApply: "Appliquer",
1073
1075
  bulkApplying: "Application\u2026",
@@ -1670,6 +1672,8 @@ var en = {
1670
1672
  bulkOptimizeMeta: "Optimize meta (AI)",
1671
1673
  bulkOptimizing: "Analyzing\u2026",
1672
1674
  bulkConfirm: "Confirm?",
1675
+ optimizeSite: "Optimize site",
1676
+ optimizeSiteHint: "Auto-targets pages that need work (missing meta, no keyword, low score) \u2192 preview \u2192 apply",
1673
1677
  bulkPreviewTitle: "Proposed meta corrections",
1674
1678
  bulkApply: "Apply",
1675
1679
  bulkApplying: "Applying\u2026",
@@ -9544,9 +9548,9 @@ function SeoView() {
9544
9548
  },
9545
9549
  [selectedIds, fetchAudit]
9546
9550
  );
9547
- const handleBulkOptimizeMeta = React4.useCallback(async () => {
9548
- const ids = Array.from(selectedIds).filter((k) => !k.startsWith("global:"));
9549
- if (ids.length === 0) return;
9551
+ const runBulkPreview = React4.useCallback(async (ids) => {
9552
+ const clean = ids.filter((k) => !k.startsWith("global:"));
9553
+ if (clean.length === 0) return;
9550
9554
  setBulkOptimizing(true);
9551
9555
  setBulkPreview(null);
9552
9556
  try {
@@ -9554,7 +9558,7 @@ function SeoView() {
9554
9558
  method: "POST",
9555
9559
  headers: { "Content-Type": "application/json" },
9556
9560
  credentials: "include",
9557
- body: JSON.stringify({ ids, apply: false, limit: 100 })
9561
+ body: JSON.stringify({ ids: clean, apply: false, limit: 100 })
9558
9562
  });
9559
9563
  if (res.ok) {
9560
9564
  const data = await res.json();
@@ -9564,7 +9568,15 @@ function SeoView() {
9564
9568
  } catch {
9565
9569
  }
9566
9570
  setBulkOptimizing(false);
9567
- }, [selectedIds]);
9571
+ }, []);
9572
+ const handleBulkOptimizeMeta = React4.useCallback(
9573
+ () => runBulkPreview(Array.from(selectedIds)),
9574
+ [selectedIds, runBulkPreview]
9575
+ );
9576
+ const handleOptimizeSite = React4.useCallback(() => {
9577
+ const targets = items.filter((i) => !i.collection.startsWith("global:")).filter((i) => !i.metaTitle || !i.metaDescription || !i.focusKeyword || i.score < 70).slice(0, 100).map(itemKey);
9578
+ runBulkPreview(targets);
9579
+ }, [items, runBulkPreview]);
9568
9580
  const handleBulkApplyPreview = React4.useCallback(async () => {
9569
9581
  if (!bulkPreview || bulkPreview.results.length === 0) return;
9570
9582
  setBulkApplying(true);
@@ -9931,6 +9943,16 @@ function SeoView() {
9931
9943
  ] })
9932
9944
  ] }),
9933
9945
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 8 }, children: [
9946
+ /* @__PURE__ */ jsxRuntime.jsx(
9947
+ "button",
9948
+ {
9949
+ onClick: handleOptimizeSite,
9950
+ disabled: bulkOptimizing || items.length === 0,
9951
+ title: t.seoView.optimizeSiteHint,
9952
+ style: { ...btnBase, backgroundColor: "#7c3aed", color: "#fff", opacity: bulkOptimizing || items.length === 0 ? 0.6 : 1 },
9953
+ children: bulkOptimizing ? t.seoView.bulkOptimizing : `\u2728 ${t.seoView.optimizeSite}`
9954
+ }
9955
+ ),
9934
9956
  /* @__PURE__ */ jsxRuntime.jsxs(
9935
9957
  "button",
9936
9958
  {
package/dist/client.js CHANGED
@@ -1062,6 +1062,8 @@ var fr = {
1062
1062
  bulkOptimizeMeta: "Optimiser m\xE9ta (IA)",
1063
1063
  bulkOptimizing: "Analyse\u2026",
1064
1064
  bulkConfirm: "Confirmer ?",
1065
+ optimizeSite: "Optimiser le site",
1066
+ optimizeSiteHint: "Cible automatiquement les pages \xE0 probl\xE8me (m\xE9ta manquante, sans mot-cl\xE9, score faible) \u2192 aper\xE7u \u2192 appliquer",
1065
1067
  bulkPreviewTitle: "Corrections m\xE9ta propos\xE9es",
1066
1068
  bulkApply: "Appliquer",
1067
1069
  bulkApplying: "Application\u2026",
@@ -1664,6 +1666,8 @@ var en = {
1664
1666
  bulkOptimizeMeta: "Optimize meta (AI)",
1665
1667
  bulkOptimizing: "Analyzing\u2026",
1666
1668
  bulkConfirm: "Confirm?",
1669
+ optimizeSite: "Optimize site",
1670
+ optimizeSiteHint: "Auto-targets pages that need work (missing meta, no keyword, low score) \u2192 preview \u2192 apply",
1667
1671
  bulkPreviewTitle: "Proposed meta corrections",
1668
1672
  bulkApply: "Apply",
1669
1673
  bulkApplying: "Applying\u2026",
@@ -9538,9 +9542,9 @@ function SeoView() {
9538
9542
  },
9539
9543
  [selectedIds, fetchAudit]
9540
9544
  );
9541
- const handleBulkOptimizeMeta = useCallback(async () => {
9542
- const ids = Array.from(selectedIds).filter((k) => !k.startsWith("global:"));
9543
- if (ids.length === 0) return;
9545
+ const runBulkPreview = useCallback(async (ids) => {
9546
+ const clean = ids.filter((k) => !k.startsWith("global:"));
9547
+ if (clean.length === 0) return;
9544
9548
  setBulkOptimizing(true);
9545
9549
  setBulkPreview(null);
9546
9550
  try {
@@ -9548,7 +9552,7 @@ function SeoView() {
9548
9552
  method: "POST",
9549
9553
  headers: { "Content-Type": "application/json" },
9550
9554
  credentials: "include",
9551
- body: JSON.stringify({ ids, apply: false, limit: 100 })
9555
+ body: JSON.stringify({ ids: clean, apply: false, limit: 100 })
9552
9556
  });
9553
9557
  if (res.ok) {
9554
9558
  const data = await res.json();
@@ -9558,7 +9562,15 @@ function SeoView() {
9558
9562
  } catch {
9559
9563
  }
9560
9564
  setBulkOptimizing(false);
9561
- }, [selectedIds]);
9565
+ }, []);
9566
+ const handleBulkOptimizeMeta = useCallback(
9567
+ () => runBulkPreview(Array.from(selectedIds)),
9568
+ [selectedIds, runBulkPreview]
9569
+ );
9570
+ const handleOptimizeSite = useCallback(() => {
9571
+ const targets = items.filter((i) => !i.collection.startsWith("global:")).filter((i) => !i.metaTitle || !i.metaDescription || !i.focusKeyword || i.score < 70).slice(0, 100).map(itemKey);
9572
+ runBulkPreview(targets);
9573
+ }, [items, runBulkPreview]);
9562
9574
  const handleBulkApplyPreview = useCallback(async () => {
9563
9575
  if (!bulkPreview || bulkPreview.results.length === 0) return;
9564
9576
  setBulkApplying(true);
@@ -9925,6 +9937,16 @@ function SeoView() {
9925
9937
  ] })
9926
9938
  ] }),
9927
9939
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8 }, children: [
9940
+ /* @__PURE__ */ jsx(
9941
+ "button",
9942
+ {
9943
+ onClick: handleOptimizeSite,
9944
+ disabled: bulkOptimizing || items.length === 0,
9945
+ title: t.seoView.optimizeSiteHint,
9946
+ style: { ...btnBase, backgroundColor: "#7c3aed", color: "#fff", opacity: bulkOptimizing || items.length === 0 ? 0.6 : 1 },
9947
+ children: bulkOptimizing ? t.seoView.bulkOptimizing : `\u2728 ${t.seoView.optimizeSite}`
9948
+ }
9949
+ ),
9928
9950
  /* @__PURE__ */ jsxs(
9929
9951
  "button",
9930
9952
  {
package/dist/index.cjs CHANGED
@@ -1683,9 +1683,9 @@ function analyzeDoc(doc, collection, seoConfig) {
1683
1683
  };
1684
1684
  }
1685
1685
  var CACHE_KEY = "audit";
1686
- var auditBuildInFlight = false;
1687
- async function buildAuditCache(payload, collections, globals, seoConfig) {
1688
- const { config: mergedConfig, ignoredSlugs } = await loadMergedConfig(payload, seoConfig);
1686
+ var auditBuildsInFlight = /* @__PURE__ */ new Set();
1687
+ async function buildAuditCache(payload, collections, globals, seoConfig, reqLocale) {
1688
+ const { config: mergedConfig, ignoredSlugs } = await loadMergedConfig(payload, seoConfig, { reqLocale });
1689
1689
  const BATCH_SIZE = Math.min(100, Math.max(1, parseInt(process.env.SEO_AUDIT_BATCH_SIZE || "15", 10) || 15));
1690
1690
  const MAX_DOCS2 = Math.max(1, parseInt(process.env.SEO_AUDIT_MAX_DOCS || "1500", 10) || 1500);
1691
1691
  const allResults = [];
@@ -1793,15 +1793,15 @@ async function buildAuditCache(payload, collections, globals, seoConfig) {
1793
1793
  };
1794
1794
  return { enrichedResults, stats, capped };
1795
1795
  }
1796
- function ensureAuditBuild(payload, collections, globals, seoConfig) {
1797
- if (auditBuildInFlight) return;
1798
- auditBuildInFlight = true;
1799
- void buildAuditCache(payload, collections, globals, seoConfig).then((result) => {
1800
- seoCache.set(CACHE_KEY, result);
1796
+ function ensureAuditBuild(payload, collections, globals, seoConfig, cacheKey, reqLocale) {
1797
+ if (auditBuildsInFlight.has(cacheKey)) return;
1798
+ auditBuildsInFlight.add(cacheKey);
1799
+ void buildAuditCache(payload, collections, globals, seoConfig, reqLocale).then((result) => {
1800
+ seoCache.set(cacheKey, result);
1801
1801
  }).catch((e) => {
1802
1802
  payload.logger.error(`[seo] audit build failed: ${e instanceof Error ? e.message : "unknown"}`);
1803
1803
  }).finally(() => {
1804
- auditBuildInFlight = false;
1804
+ auditBuildsInFlight.delete(cacheKey);
1805
1805
  });
1806
1806
  }
1807
1807
  function createAuditHandler(collections, seoConfig, globals = []) {
@@ -1814,12 +1814,14 @@ function createAuditHandler(collections, seoConfig, globals = []) {
1814
1814
  const page = Math.max(1, parseInt(url.searchParams.get("page") || "1", 10));
1815
1815
  const limit = Math.min(500, Math.max(1, parseInt(url.searchParams.get("limit") || "300", 10)));
1816
1816
  const noCache = url.searchParams.get("nocache") === "1";
1817
- if (noCache && !auditBuildInFlight) {
1818
- seoCache.invalidateKey(CACHE_KEY);
1817
+ const reqLocale = typeof req.locale === "string" && req.locale ? req.locale : void 0;
1818
+ const cacheKey = reqLocale ? `${CACHE_KEY}:${reqLocale}` : CACHE_KEY;
1819
+ if (noCache && !auditBuildsInFlight.has(cacheKey)) {
1820
+ seoCache.invalidateKey(cacheKey);
1819
1821
  }
1820
- const cached = seoCache.get(CACHE_KEY);
1822
+ const cached = seoCache.get(cacheKey);
1821
1823
  if (!cached) {
1822
- ensureAuditBuild(req.payload, collections, globals, seoConfig);
1824
+ ensureAuditBuild(req.payload, collections, globals, seoConfig, cacheKey, reqLocale);
1823
1825
  return Response.json(
1824
1826
  { building: true, results: [], stats: null },
1825
1827
  { status: 202, headers: { "Cache-Control": "no-store" } }
@@ -9154,6 +9156,8 @@ var fr = {
9154
9156
  bulkOptimizeMeta: "Optimiser m\xE9ta (IA)",
9155
9157
  bulkOptimizing: "Analyse\u2026",
9156
9158
  bulkConfirm: "Confirmer ?",
9159
+ optimizeSite: "Optimiser le site",
9160
+ optimizeSiteHint: "Cible automatiquement les pages \xE0 probl\xE8me (m\xE9ta manquante, sans mot-cl\xE9, score faible) \u2192 aper\xE7u \u2192 appliquer",
9157
9161
  bulkPreviewTitle: "Corrections m\xE9ta propos\xE9es",
9158
9162
  bulkApply: "Appliquer",
9159
9163
  bulkApplying: "Application\u2026",
@@ -9756,6 +9760,8 @@ var en = {
9756
9760
  bulkOptimizeMeta: "Optimize meta (AI)",
9757
9761
  bulkOptimizing: "Analyzing\u2026",
9758
9762
  bulkConfirm: "Confirm?",
9763
+ optimizeSite: "Optimize site",
9764
+ optimizeSiteHint: "Auto-targets pages that need work (missing meta, no keyword, low score) \u2192 preview \u2192 apply",
9759
9765
  bulkPreviewTitle: "Proposed meta corrections",
9760
9766
  bulkApply: "Apply",
9761
9767
  bulkApplying: "Applying\u2026",
package/dist/index.d.cts CHANGED
@@ -470,6 +470,8 @@ interface DashboardTranslations {
470
470
  bulkOptimizeMeta: string;
471
471
  bulkOptimizing: string;
472
472
  bulkConfirm: string;
473
+ optimizeSite: string;
474
+ optimizeSiteHint: string;
473
475
  bulkPreviewTitle: string;
474
476
  bulkApply: string;
475
477
  bulkApplying: string;
package/dist/index.d.ts CHANGED
@@ -470,6 +470,8 @@ interface DashboardTranslations {
470
470
  bulkOptimizeMeta: string;
471
471
  bulkOptimizing: string;
472
472
  bulkConfirm: string;
473
+ optimizeSite: string;
474
+ optimizeSiteHint: string;
473
475
  bulkPreviewTitle: string;
474
476
  bulkApply: string;
475
477
  bulkApplying: string;
package/dist/index.js CHANGED
@@ -1681,9 +1681,9 @@ function analyzeDoc(doc, collection, seoConfig) {
1681
1681
  };
1682
1682
  }
1683
1683
  var CACHE_KEY = "audit";
1684
- var auditBuildInFlight = false;
1685
- async function buildAuditCache(payload, collections, globals, seoConfig) {
1686
- const { config: mergedConfig, ignoredSlugs } = await loadMergedConfig(payload, seoConfig);
1684
+ var auditBuildsInFlight = /* @__PURE__ */ new Set();
1685
+ async function buildAuditCache(payload, collections, globals, seoConfig, reqLocale) {
1686
+ const { config: mergedConfig, ignoredSlugs } = await loadMergedConfig(payload, seoConfig, { reqLocale });
1687
1687
  const BATCH_SIZE = Math.min(100, Math.max(1, parseInt(process.env.SEO_AUDIT_BATCH_SIZE || "15", 10) || 15));
1688
1688
  const MAX_DOCS2 = Math.max(1, parseInt(process.env.SEO_AUDIT_MAX_DOCS || "1500", 10) || 1500);
1689
1689
  const allResults = [];
@@ -1791,15 +1791,15 @@ async function buildAuditCache(payload, collections, globals, seoConfig) {
1791
1791
  };
1792
1792
  return { enrichedResults, stats, capped };
1793
1793
  }
1794
- function ensureAuditBuild(payload, collections, globals, seoConfig) {
1795
- if (auditBuildInFlight) return;
1796
- auditBuildInFlight = true;
1797
- void buildAuditCache(payload, collections, globals, seoConfig).then((result) => {
1798
- seoCache.set(CACHE_KEY, result);
1794
+ function ensureAuditBuild(payload, collections, globals, seoConfig, cacheKey, reqLocale) {
1795
+ if (auditBuildsInFlight.has(cacheKey)) return;
1796
+ auditBuildsInFlight.add(cacheKey);
1797
+ void buildAuditCache(payload, collections, globals, seoConfig, reqLocale).then((result) => {
1798
+ seoCache.set(cacheKey, result);
1799
1799
  }).catch((e) => {
1800
1800
  payload.logger.error(`[seo] audit build failed: ${e instanceof Error ? e.message : "unknown"}`);
1801
1801
  }).finally(() => {
1802
- auditBuildInFlight = false;
1802
+ auditBuildsInFlight.delete(cacheKey);
1803
1803
  });
1804
1804
  }
1805
1805
  function createAuditHandler(collections, seoConfig, globals = []) {
@@ -1812,12 +1812,14 @@ function createAuditHandler(collections, seoConfig, globals = []) {
1812
1812
  const page = Math.max(1, parseInt(url.searchParams.get("page") || "1", 10));
1813
1813
  const limit = Math.min(500, Math.max(1, parseInt(url.searchParams.get("limit") || "300", 10)));
1814
1814
  const noCache = url.searchParams.get("nocache") === "1";
1815
- if (noCache && !auditBuildInFlight) {
1816
- seoCache.invalidateKey(CACHE_KEY);
1815
+ const reqLocale = typeof req.locale === "string" && req.locale ? req.locale : void 0;
1816
+ const cacheKey = reqLocale ? `${CACHE_KEY}:${reqLocale}` : CACHE_KEY;
1817
+ if (noCache && !auditBuildsInFlight.has(cacheKey)) {
1818
+ seoCache.invalidateKey(cacheKey);
1817
1819
  }
1818
- const cached = seoCache.get(CACHE_KEY);
1820
+ const cached = seoCache.get(cacheKey);
1819
1821
  if (!cached) {
1820
- ensureAuditBuild(req.payload, collections, globals, seoConfig);
1822
+ ensureAuditBuild(req.payload, collections, globals, seoConfig, cacheKey, reqLocale);
1821
1823
  return Response.json(
1822
1824
  { building: true, results: [], stats: null },
1823
1825
  { status: 202, headers: { "Cache-Control": "no-store" } }
@@ -9152,6 +9154,8 @@ var fr = {
9152
9154
  bulkOptimizeMeta: "Optimiser m\xE9ta (IA)",
9153
9155
  bulkOptimizing: "Analyse\u2026",
9154
9156
  bulkConfirm: "Confirmer ?",
9157
+ optimizeSite: "Optimiser le site",
9158
+ optimizeSiteHint: "Cible automatiquement les pages \xE0 probl\xE8me (m\xE9ta manquante, sans mot-cl\xE9, score faible) \u2192 aper\xE7u \u2192 appliquer",
9155
9159
  bulkPreviewTitle: "Corrections m\xE9ta propos\xE9es",
9156
9160
  bulkApply: "Appliquer",
9157
9161
  bulkApplying: "Application\u2026",
@@ -9754,6 +9758,8 @@ var en = {
9754
9758
  bulkOptimizeMeta: "Optimize meta (AI)",
9755
9759
  bulkOptimizing: "Analyzing\u2026",
9756
9760
  bulkConfirm: "Confirm?",
9761
+ optimizeSite: "Optimize site",
9762
+ optimizeSiteHint: "Auto-targets pages that need work (missing meta, no keyword, low score) \u2192 preview \u2192 apply",
9757
9763
  bulkPreviewTitle: "Proposed meta corrections",
9758
9764
  bulkApply: "Apply",
9759
9765
  bulkApplying: "Applying\u2026",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@consilioweb/payload-seo-analyzer",
3
- "version": "1.13.0",
3
+ "version": "1.14.0",
4
4
  "description": "Payload CMS SEO plugin — 50+ checks, dashboard, Lexical JSON support, Flesch FR/EN readability, i18n",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",