sanity-plugin-seofields 1.9.0 → 1.10.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 (46) hide show
  1. package/README.md +71 -0
  2. package/dist/chunk-CRCXC45D.js +56 -0
  3. package/dist/chunk-CRCXC45D.js.map +1 -0
  4. package/dist/{chunk-3OK3S2F7.cjs → chunk-EUG4MNRC.cjs} +60 -20
  5. package/dist/chunk-EUG4MNRC.cjs.map +1 -0
  6. package/dist/{chunk-Z5AOUU4H.js → chunk-NLEB47UV.js} +60 -20
  7. package/dist/chunk-NLEB47UV.js.map +1 -0
  8. package/dist/{chunk-FUUWBQE2.cjs → chunk-OULUDJPI.cjs} +66 -7
  9. package/dist/chunk-OULUDJPI.cjs.map +1 -0
  10. package/dist/{chunk-KIEO6QG3.js → chunk-XD4HKLEA.js} +66 -7
  11. package/dist/chunk-XD4HKLEA.js.map +1 -0
  12. package/dist/chunk-XYAZJ3WX.cjs +56 -0
  13. package/dist/chunk-XYAZJ3WX.cjs.map +1 -0
  14. package/dist/cli.js +1 -1
  15. package/dist/head.cjs +10 -2
  16. package/dist/head.cjs.map +1 -1
  17. package/dist/head.d.cts +61 -1
  18. package/dist/head.d.ts +61 -1
  19. package/dist/head.js +9 -1
  20. package/dist/hreflang-_VbSuWYD.d.cts +29 -0
  21. package/dist/hreflang-_VbSuWYD.d.ts +29 -0
  22. package/dist/index.cjs +101 -8
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.d.cts +3 -2
  25. package/dist/index.d.ts +3 -2
  26. package/dist/index.js +273 -180
  27. package/dist/index.js.map +1 -1
  28. package/dist/next.cjs +11 -3
  29. package/dist/next.cjs.map +1 -1
  30. package/dist/next.d.cts +2 -1
  31. package/dist/next.d.ts +2 -1
  32. package/dist/next.js +9 -1
  33. package/dist/next.js.map +1 -1
  34. package/dist/{plugin-DoGeZP79.d.cts → plugin-HFuWZsJu.d.cts} +68 -1
  35. package/dist/{plugin-DQdThyxG.d.ts → plugin-oQUCim56.d.ts} +68 -1
  36. package/dist/server.cjs +2 -2
  37. package/dist/server.cjs.map +1 -1
  38. package/dist/server.d.cts +6 -8
  39. package/dist/server.d.ts +6 -8
  40. package/dist/server.js +1 -1
  41. package/dist/server.js.map +1 -1
  42. package/package.json +2 -2
  43. package/dist/chunk-3OK3S2F7.cjs.map +0 -1
  44. package/dist/chunk-FUUWBQE2.cjs.map +0 -1
  45. package/dist/chunk-KIEO6QG3.js.map +0 -1
  46. package/dist/chunk-Z5AOUU4H.js.map +0 -1
package/dist/cli.js CHANGED
@@ -61,7 +61,7 @@ ${e}`}function xt(e,t,o,n){a.success(`Updated ${e}`),a.dim(""),a.dim(" What was
61
61
  title,
62
62
  slug,
63
63
  seo
64
- } | order(_updatedAt desc)`;n.text="Fetching documents...";let l;try{l=await s.fetch(f);}catch(g){n.fail(`Failed to fetch: ${g.message}`),process.exit(1);}if(n.succeed(`Analysed ${l.length} document(s)`),l.length===0){a.warn("No documents with SEO fields found."),o&&$e(i);return}let u=l.map(g=>({doc:g,health:U(g)}));await kt(u,l,e),o&&$e(i);}async function kt(e,t,o){let n={excellent:0,good:0,fair:0,poor:0,missing:0},s=0;for(let{health:l}of e)n[l.status]=(n[l.status]||0)+1,s+=l.score;let i=Math.round(s/e.length);if(vt(t,i,n),o.format==="summary")return;o.format!=="table"&&(a.info(""),a.warn(`Unknown format "${o.format}", defaulting to table output.`)),It(e);let r={};for(let{health:l}of e)for(let u of l.issues)r[u]=(r[u]||0)+1;let f=Object.entries(r).sort(([,l],[,u])=>u-l).slice(0,10);f.length>0&&jt(f,t.length),a.info("");}function vt(e,t,o){a.heading("\u{1F4CA} Summary"),a.info(""),a.info(` Total documents: ${c.bold(String(e.length))}`),a.info(` Average score: ${c.bold(Te(t))}%`),a.info(""),a.info(` ${R.excellent} Excellent (80+): ${o.excellent}`),a.info(` ${R.good} Good (60-79): ${o.good}`),a.info(` ${R.fair} Fair (40-59): ${o.fair}`),a.info(` ${R.poor} Poor (1-39): ${o.poor}`),a.info(` ${R.missing} Missing (0): ${o.missing}`);}var Tt={excellent:"Excellent",good:"Good",fair:"Fair",poor:"Poor",missing:"Missing"},Ct={excellent:c.green,good:c.blue,fair:c.yellow,poor:c.red,missing:c.dim};function Ot(e,t=11){var s,i;let o=((s=Tt[e])!=null?s:e).padEnd(t);return ((i=Ct[e])!=null?i:(r=>r))(o)}function It(e){a.heading("\u{1F4CB} Documents"),a.info("");let t=Math.min(44,Math.max(5,...e.map(({doc:r})=>(r.title||r._id).length))),o=11,n=14,s=5,i=c.dim("\u2500".repeat(s+2+o+2+n+2+t));a.info(` ${c.dim("Score".padEnd(s))} ${c.dim("Status".padEnd(o))} ${c.dim("Type".padEnd(n))} ${c.dim("Title")}`),a.info(` ${i}`);for(let{doc:r,health:f}of e){let l=ke(r.title||r._id,t),u=String(f.score).padStart(s),d=Ot(f.status,o),g=c.dim(ke(r._type,n).padEnd(n));a.info(` ${Te(f.score,u)} ${d} ${g} ${l}`);}}function jt(e,t){a.heading("\u{1F50D} Top Issues"),a.info("");for(let[o,n]of e){let s=Math.round(n/t*100);a.info(` ${c.red(String(n).padStart(4))} docs (${s}%) ${o}`);}}function $e(e){a.info(""),a.info(` ${c.dim("\u2500".repeat(52))}`),a.info(` ${c.dim("Connection info")}`),a.info(` ${c.dim("Project ID:")} ${c.white(e.projectId)} ${c.dim(`\u2190 ${e.sources.projectId}`)}`),a.info(` ${c.dim("Dataset: ")} ${c.white(e.dataset)} ${c.dim(`\u2190 ${e.sources.dataset}`)}`),a.info(` ${c.dim("Token: ")} ${c.dim(e.sources.token)}`),a.info("");}function Te(e,t){let o=t!=null?t:String(e);return e>=80?c.green(o):e>=60?c.blue(o):e>=40?c.yellow(o):e>0?c.red(o):c.dim(o)}function ke(e,t){return e.length>t?`${e.slice(0,t-1)}\u2026`:e}H();var oe="1.9.0",Ce=[c.cyan,e=>c.bold(c.blue(e)),c.blue,c.magenta,c.red,c.yellow],x=Ce[Math.floor(Math.random()*Ce.length)],Oe=["Run `seofields doctor` first to verify your setup is correct.","Use `seofields report --format summary` for a quick overview.","Export your SEO data before making bulk changes: `seofields export -o backup.json`.","Add `seoFields` type to every document schema to track SEO health.","A meta title between 50\u201360 characters gets the best click-through rate.","Meta descriptions should be 140\u2013160 characters for optimal display.","Open Graph images should be 1200\xD7630px for best social sharing.","Use `seofields config --baseUrl=https://yoursite.com` to enable canonical URLs.","Twitter Card images require a separate `twitterImage` field for best results.","Schema.org markup helps search engines understand your content structure.","Use `seofields report --types post,page` to filter by document type.","The `healthDashboard` tool gives a live view of SEO scores inside Sanity Studio.","Keywords should appear naturally \u2014 avoid keyword stuffing.","Every page needs a unique meta title. Duplicates hurt rankings.","Use `seofields export --format csv` to open SEO data in a spreadsheet.","Set `noIndex: true`, `noFollow: true`, `noTranslate: true`, or `noImageIndex: true` when a page needs stricter crawler directives.","OG title and meta title can differ \u2014 OG is optimised for social, meta for search.","Run `seofields report` after content migrations to catch regressions.","Use `seofields init --schema-org` to add structured data support.","Structured data (Schema.org) can unlock rich results in Google Search.","A score of 80+ is excellent. Aim to get all key pages there.","The `seoPreview` option shows a live Google-style preview inside the editor.","Use `seofields report` in CI to catch SEO regressions before they ship.","Use `--dataset staging` to run reports against your staging dataset.","Keep your license key private \u2014 never commit it to version control.","Use `seofields config --healthDashboard.showDocumentId=true` for easier debugging.","Use `seofields export --format json --output report.json` to save SEO data to a file.","Alt text on images improves both accessibility and image search ranking.","Canonical URLs prevent duplicate content penalties from search engines.","A missing OG image means social shares show a blank card \u2014 always add one.","Use descriptive slugs: `/blog/seo-tips` ranks better than `/blog/post-123`.","Re-run `seofields report` monthly to track your SEO health over time.","The `types` filter helps you focus reports on high-priority content.","Use `seofields export` to back up SEO data before schema migrations.","Short meta titles (under 30 chars) waste valuable search result space.","Long meta descriptions get truncated \u2014 stay under 160 characters.","Twitter Cards need `twitter:card`, `twitter:title`, and `twitter:image` at minimum.","Use `seofields doctor` after upgrading the plugin to catch breaking changes.","JSON export includes health scores \u2014 pipe it into your analytics pipeline.","Set a `defaultHiddenFields` list to keep the SEO panel focused.","Internal links pass authority \u2014 make sure important pages are well-linked.","Page speed is a ranking factor \u2014 optimise images referenced in SEO fields.","Use consistent brand phrasing in meta titles across your site.","Structured data errors can be tested at schema.org/validator.","Focus first on pages with the most traffic, not just the lowest scores.","Add SEO review to your content publishing checklist.","Use `seofields config --healthDashboard.licenseKey=KEY` to unlock premium features.","The CSV export makes it easy to share SEO audits with non-technical stakeholders.","Every image on your page should have descriptive alt text.","Avoid duplicate meta descriptions \u2014 each page needs its own."];function Et(){return Oe[Math.floor(Math.random()*Oe.length)]}var Rt=62,q=Rt-2;function Ft(e,t){let o=e.split(" "),n=[],s="";for(let i of o)s.length+(s?1:0)+i.length>t?(s&&n.push(s),s=i):s=s?`${s} ${i}`:i;return s&&n.push(s),n}function D(e,t){let o=q-3,n=" ".repeat(Math.max(0,o-t));return ` ${x("\u2502")} ${e}${n} ${x("\u2502")}`}function P(){return D("",0)}function Ie(){let e="\u2500".repeat(q),t=Et(),o=q-3-5,n=Ft(t,o);console.log(),console.log(` ${x(`\u256D${e}\u256E`)}`),console.log(P()),console.log(D(`${c.bold(c.green("seofields"))}${c.dim(` v${oe}`)}`,`seofields v${oe}`.length)),console.log(D(c.dim("SEO tooling for Sanity CMS \u2014 manage from your terminal"),54)),console.log(P());for(let i=0;i<n.length;i++){let r=i===0?`${c.dim("Tip: ")}${c.white(n[i])}`:` ${c.white(n[i])}`,f=5+n[i].length;console.log(D(r,f));}console.log(P()),console.log(` ${x(`\u2570${e}\u256F`)}`),console.log(),console.log(` ${c.bold("Commands")}`),console.log();let s=[["init ","Add seofields() to sanity.config"],["config ","Update plugin configuration options"],["create-config ","Create seofields.cli.ts / .js interactively"],["report ","SEO health report across all documents"],["export ","Export SEO fields as JSON or CSV"],["doctor ","Check setup, deps & config"]];for(let[i,r]of s)console.log(` ${x(i)} ${c.dim(r)}`);console.log(),console.log(` ${c.dim("Run ")}${c.white("seofields <command> --help")}${c.dim(" for details")}`),console.log();}function _(e,t){let o="\u2500".repeat(q),n=`seofields \u203A ${e}`;return ["",` ${x(`\u256D${o}\u256E`)}`,P(),D(`${c.bold(c.green("seofields"))}${x(" \u203A ")}${c.bold(x(e))}`,n.length),D(c.dim(t),t.length),P(),` ${x(`\u2570${o}\u256F`)}`].join(`
64
+ } | order(_updatedAt desc)`;n.text="Fetching documents...";let l;try{l=await s.fetch(f);}catch(g){n.fail(`Failed to fetch: ${g.message}`),process.exit(1);}if(n.succeed(`Analysed ${l.length} document(s)`),l.length===0){a.warn("No documents with SEO fields found."),o&&$e(i);return}let u=l.map(g=>({doc:g,health:U(g)}));await kt(u,l,e),o&&$e(i);}async function kt(e,t,o){let n={excellent:0,good:0,fair:0,poor:0,missing:0},s=0;for(let{health:l}of e)n[l.status]=(n[l.status]||0)+1,s+=l.score;let i=Math.round(s/e.length);if(vt(t,i,n),o.format==="summary")return;o.format!=="table"&&(a.info(""),a.warn(`Unknown format "${o.format}", defaulting to table output.`)),It(e);let r={};for(let{health:l}of e)for(let u of l.issues)r[u]=(r[u]||0)+1;let f=Object.entries(r).sort(([,l],[,u])=>u-l).slice(0,10);f.length>0&&jt(f,t.length),a.info("");}function vt(e,t,o){a.heading("\u{1F4CA} Summary"),a.info(""),a.info(` Total documents: ${c.bold(String(e.length))}`),a.info(` Average score: ${c.bold(Te(t))}%`),a.info(""),a.info(` ${R.excellent} Excellent (80+): ${o.excellent}`),a.info(` ${R.good} Good (60-79): ${o.good}`),a.info(` ${R.fair} Fair (40-59): ${o.fair}`),a.info(` ${R.poor} Poor (1-39): ${o.poor}`),a.info(` ${R.missing} Missing (0): ${o.missing}`);}var Tt={excellent:"Excellent",good:"Good",fair:"Fair",poor:"Poor",missing:"Missing"},Ct={excellent:c.green,good:c.blue,fair:c.yellow,poor:c.red,missing:c.dim};function Ot(e,t=11){var s,i;let o=((s=Tt[e])!=null?s:e).padEnd(t);return ((i=Ct[e])!=null?i:(r=>r))(o)}function It(e){a.heading("\u{1F4CB} Documents"),a.info("");let t=Math.min(44,Math.max(5,...e.map(({doc:r})=>(r.title||r._id).length))),o=11,n=14,s=5,i=c.dim("\u2500".repeat(s+2+o+2+n+2+t));a.info(` ${c.dim("Score".padEnd(s))} ${c.dim("Status".padEnd(o))} ${c.dim("Type".padEnd(n))} ${c.dim("Title")}`),a.info(` ${i}`);for(let{doc:r,health:f}of e){let l=ke(r.title||r._id,t),u=String(f.score).padStart(s),d=Ot(f.status,o),g=c.dim(ke(r._type,n).padEnd(n));a.info(` ${Te(f.score,u)} ${d} ${g} ${l}`);}}function jt(e,t){a.heading("\u{1F50D} Top Issues"),a.info("");for(let[o,n]of e){let s=Math.round(n/t*100);a.info(` ${c.red(String(n).padStart(4))} docs (${s}%) ${o}`);}}function $e(e){a.info(""),a.info(` ${c.dim("\u2500".repeat(52))}`),a.info(` ${c.dim("Connection info")}`),a.info(` ${c.dim("Project ID:")} ${c.white(e.projectId)} ${c.dim(`\u2190 ${e.sources.projectId}`)}`),a.info(` ${c.dim("Dataset: ")} ${c.white(e.dataset)} ${c.dim(`\u2190 ${e.sources.dataset}`)}`),a.info(` ${c.dim("Token: ")} ${c.dim(e.sources.token)}`),a.info("");}function Te(e,t){let o=t!=null?t:String(e);return e>=80?c.green(o):e>=60?c.blue(o):e>=40?c.yellow(o):e>0?c.red(o):c.dim(o)}function ke(e,t){return e.length>t?`${e.slice(0,t-1)}\u2026`:e}H();var oe="1.10.0",Ce=[c.cyan,e=>c.bold(c.blue(e)),c.blue,c.magenta,c.red,c.yellow],x=Ce[Math.floor(Math.random()*Ce.length)],Oe=["Run `seofields doctor` first to verify your setup is correct.","Use `seofields report --format summary` for a quick overview.","Export your SEO data before making bulk changes: `seofields export -o backup.json`.","Add `seoFields` type to every document schema to track SEO health.","A meta title between 50\u201360 characters gets the best click-through rate.","Meta descriptions should be 140\u2013160 characters for optimal display.","Open Graph images should be 1200\xD7630px for best social sharing.","Use `seofields config --baseUrl=https://yoursite.com` to enable canonical URLs.","Twitter Card images require a separate `twitterImage` field for best results.","Schema.org markup helps search engines understand your content structure.","Use `seofields report --types post,page` to filter by document type.","The `healthDashboard` tool gives a live view of SEO scores inside Sanity Studio.","Keywords should appear naturally \u2014 avoid keyword stuffing.","Every page needs a unique meta title. Duplicates hurt rankings.","Use `seofields export --format csv` to open SEO data in a spreadsheet.","Set `noIndex: true`, `noFollow: true`, `noTranslate: true`, or `noImageIndex: true` when a page needs stricter crawler directives.","OG title and meta title can differ \u2014 OG is optimised for social, meta for search.","Run `seofields report` after content migrations to catch regressions.","Use `seofields init --schema-org` to add structured data support.","Structured data (Schema.org) can unlock rich results in Google Search.","A score of 80+ is excellent. Aim to get all key pages there.","The `seoPreview` option shows a live Google-style preview inside the editor.","Use `seofields report` in CI to catch SEO regressions before they ship.","Use `--dataset staging` to run reports against your staging dataset.","Keep your license key private \u2014 never commit it to version control.","Use `seofields config --healthDashboard.showDocumentId=true` for easier debugging.","Use `seofields export --format json --output report.json` to save SEO data to a file.","Alt text on images improves both accessibility and image search ranking.","Canonical URLs prevent duplicate content penalties from search engines.","A missing OG image means social shares show a blank card \u2014 always add one.","Use descriptive slugs: `/blog/seo-tips` ranks better than `/blog/post-123`.","Re-run `seofields report` monthly to track your SEO health over time.","The `types` filter helps you focus reports on high-priority content.","Use `seofields export` to back up SEO data before schema migrations.","Short meta titles (under 30 chars) waste valuable search result space.","Long meta descriptions get truncated \u2014 stay under 160 characters.","Twitter Cards need `twitter:card`, `twitter:title`, and `twitter:image` at minimum.","Use `seofields doctor` after upgrading the plugin to catch breaking changes.","JSON export includes health scores \u2014 pipe it into your analytics pipeline.","Set a `defaultHiddenFields` list to keep the SEO panel focused.","Internal links pass authority \u2014 make sure important pages are well-linked.","Page speed is a ranking factor \u2014 optimise images referenced in SEO fields.","Use consistent brand phrasing in meta titles across your site.","Structured data errors can be tested at schema.org/validator.","Focus first on pages with the most traffic, not just the lowest scores.","Add SEO review to your content publishing checklist.","Use `seofields config --healthDashboard.licenseKey=KEY` to unlock premium features.","The CSV export makes it easy to share SEO audits with non-technical stakeholders.","Every image on your page should have descriptive alt text.","Avoid duplicate meta descriptions \u2014 each page needs its own."];function Et(){return Oe[Math.floor(Math.random()*Oe.length)]}var Rt=62,q=Rt-2;function Ft(e,t){let o=e.split(" "),n=[],s="";for(let i of o)s.length+(s?1:0)+i.length>t?(s&&n.push(s),s=i):s=s?`${s} ${i}`:i;return s&&n.push(s),n}function D(e,t){let o=q-3,n=" ".repeat(Math.max(0,o-t));return ` ${x("\u2502")} ${e}${n} ${x("\u2502")}`}function P(){return D("",0)}function Ie(){let e="\u2500".repeat(q),t=Et(),o=q-3-5,n=Ft(t,o);console.log(),console.log(` ${x(`\u256D${e}\u256E`)}`),console.log(P()),console.log(D(`${c.bold(c.green("seofields"))}${c.dim(` v${oe}`)}`,`seofields v${oe}`.length)),console.log(D(c.dim("SEO tooling for Sanity CMS \u2014 manage from your terminal"),54)),console.log(P());for(let i=0;i<n.length;i++){let r=i===0?`${c.dim("Tip: ")}${c.white(n[i])}`:` ${c.white(n[i])}`,f=5+n[i].length;console.log(D(r,f));}console.log(P()),console.log(` ${x(`\u2570${e}\u256F`)}`),console.log(),console.log(` ${c.bold("Commands")}`),console.log();let s=[["init ","Add seofields() to sanity.config"],["config ","Update plugin configuration options"],["create-config ","Create seofields.cli.ts / .js interactively"],["report ","SEO health report across all documents"],["export ","Export SEO fields as JSON or CSV"],["doctor ","Check setup, deps & config"]];for(let[i,r]of s)console.log(` ${x(i)} ${c.dim(r)}`);console.log(),console.log(` ${c.dim("Run ")}${c.white("seofields <command> --help")}${c.dim(" for details")}`),console.log();}function _(e,t){let o="\u2500".repeat(q),n=`seofields \u203A ${e}`;return ["",` ${x(`\u256D${o}\u256E`)}`,P(),D(`${c.bold(c.green("seofields"))}${x(" \u203A ")}${c.bold(x(e))}`,n.length),D(c.dim(t),t.length),P(),` ${x(`\u2570${o}\u256F`)}`].join(`
65
65
  `)}program.name("seofields").description("CLI for sanity-plugin-seofields \u2014 manage SEO from your terminal").version(oe).action(Ie);program.command("config",{isDefault:false}).description("Update seofields() configuration in your sanity.config file").addHelpText("before",_("config","Update seofields() configuration in your sanity.config file")).allowUnknownOption().allowExcessArguments().helpOption("-h, --help","Show help and all available options").addHelpText("after",["",` ${c.bold("Usage")}`,` ${c.dim("Pass any option as")} ${c.white("--key=value")}${c.dim(". Nested keys use dot notation.")}`,"",` ${c.bold("Top-level options")}`,` ${x("--baseUrl")}${c.dim("=<url>").padEnd(38)} ${c.dim("Canonical base URL")}`,` ${x("--seoPreview")}${c.dim("=<bool>").padEnd(36)} ${c.dim("Enable live Google-style preview")}`,` ${x("--defaultHiddenFields")}${c.dim("=<f1,f2>").padEnd(28)} ${c.dim("Comma-separated fields to hide")}`,` ${x("--types")}${c.dim("=<t1,t2>").padEnd(40)} ${c.dim("Document types that have SEO fields")}`,"",` ${c.bold("healthDashboard options")} ${c.dim("(prefix: --healthDashboard.<key>)")}`,` ${x("--healthDashboard.licenseKey")}${c.dim("=<key>").padEnd(22)} ${c.dim("License key for premium features")}`,` ${x("--healthDashboard.toolTitle")}${c.dim("=<str>").padEnd(23)} ${c.dim("Custom title shown in Studio")}`,` ${x("--healthDashboard.showDocumentId")}${c.dim("=<bool>").padEnd(17)} ${c.dim("Show document _id in dashboard")}`,` ${x("--healthDashboard.showHealthScore")}${c.dim("=<bool>").padEnd(16)} ${c.dim("Show score badge per document")}`,` ${x("--healthDashboard.query.types")}${c.dim("=<t1,t2>").padEnd(21)} ${c.dim("Filter dashboard by document types")}`,"",` ${c.bold("Examples")}`,` ${c.white("seofields config --baseUrl=https://mysite.com")}`,` ${c.white("seofields config --healthDashboard.licenseKey=SEOF-1234")}`,` ${c.white("seofields config --healthDashboard.showDocumentId=true --seoPreview=true")}`,` ${c.white("seofields config --healthDashboard.query.types=post,page")}`,` ${c.white("seofields config --defaultHiddenFields=metaImage,openGraphUrl")}`,""].join(`
66
66
  `)).action(()=>{let e=process.argv.indexOf("config"),t=e>=0?process.argv.slice(e+1):[];ge(t);});program.command("create-config").description("Interactively create a seofields.cli.ts / .js config file").addHelpText("before",_("create-config","Interactively create a seofields.cli.ts / .js config file")).action(me);program.command("init").description("Add seofields() plugin to your sanity.config file").addHelpText("before",_("init","Add seofields() plugin to your sanity.config file")).option("--preview","Enable SEO preview in the plugin config").option("--dashboard","Explicitly enable the SEO Health Dashboard").option("--no-dashboard","Disable the SEO Health Dashboard").option("--schema-org","Also add schemaOrg() plugin registration").addHelpText("after",["",` ${c.dim("Note: ")}${c.yellow("--preview")}${c.dim(", ")}${c.yellow("--dashboard")}${c.dim(", and ")}${c.yellow("--no-dashboard")}${c.dim(" only apply during initial setup.")}`,` ${c.dim(" If seofields() is already in your config, use:")}`,"",` ${c.dim(" ")}${c.white("seofields config --healthDashboard=true --seoPreview=true")}`,""].join(`
67
67
  `)).action(xe);program.command("export").description("Export all documents with SEO fields as JSON or CSV").addHelpText("before",_("export","Export all documents with SEO fields as JSON or CSV")).option("-p, --project-id <id>","Sanity project ID").option("-d, --dataset <name>","Sanity dataset name").option("-t, --token <token>","Sanity API token").option("--types <types>","Comma-separated document types to export (e.g. post,page)").option("-f, --format <format>","Output format: json or csv","json").option("-o, --output <path>","Output file path (defaults to stdout)").action(Se);program.command("report").description("Generate an SEO health report for all documents").addHelpText("before",_("report","Generate an SEO health report for all documents")).option("-p, --project-id <id>","Sanity project ID").option("-d, --dataset <name>","Sanity dataset name").option("-t, --token <token>","Sanity API token").option("--types <types>","Comma-separated document types to include").option("--format <format>",'Output format: table or summary (default: "table")',"table").action(ve);program.command("doctor").description("Check plugin configuration, dependencies, and setup").addHelpText("before",_("doctor","Check plugin configuration, dependencies, and setup")).option("--cwd <path>","Working directory to check",process.cwd()).action(he);var E=process.argv.slice(2);E.length===1&&(E[0]==="--help"||E[0]==="-h")&&(Ie(),process.exit(0));if(E.length>=2&&E[E.length-1]==="help"){let e=program.commands.find(t=>t.name()===E[0]);e&&(e.help(),process.exit(0));}program.parse();
package/dist/head.cjs CHANGED
@@ -3,12 +3,20 @@
3
3
 
4
4
 
5
5
 
6
- var _chunkFUUWBQE2cjs = require('./chunk-FUUWBQE2.cjs');
6
+
7
+
8
+ var _chunkOULUDJPIcjs = require('./chunk-OULUDJPI.cjs');
9
+
10
+
11
+ var _chunkXYAZJ3WXcjs = require('./chunk-XYAZJ3WX.cjs');
7
12
  require('./chunk-A57XNQC7.cjs');
8
13
 
9
14
 
10
15
 
11
16
 
12
17
 
13
- exports.buildSeoHead = _chunkFUUWBQE2cjs.buildSeoHead; exports.buildSeoMeta = _chunkFUUWBQE2cjs.buildSeoMeta; exports.sanitizeOGType = _chunkFUUWBQE2cjs.sanitizeOGType; exports.sanitizeTwitterCard = _chunkFUUWBQE2cjs.sanitizeTwitterCard;
18
+
19
+
20
+
21
+ exports.buildHreflangs = _chunkXYAZJ3WXcjs.buildHreflangs; exports.buildLlmsTxt = _chunkOULUDJPIcjs.buildLlmsTxt; exports.buildSeoHead = _chunkOULUDJPIcjs.buildSeoHead; exports.buildSeoMeta = _chunkOULUDJPIcjs.buildSeoMeta; exports.docsToLlmsSection = _chunkOULUDJPIcjs.docsToLlmsSection; exports.sanitizeOGType = _chunkOULUDJPIcjs.sanitizeOGType; exports.sanitizeTwitterCard = _chunkOULUDJPIcjs.sanitizeTwitterCard;
14
22
  //# sourceMappingURL=head.cjs.map
package/dist/head.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/hardik/GITHUB/sanity-plugin-seofields/npm/dist/head.cjs"],"names":[],"mappings":"AAAA;AACE;AACA;AACA;AACA;AACF,wDAA6B;AAC7B,gCAA6B;AAC7B;AACE;AACA;AACA;AACA;AACF,6OAAC","file":"/Users/hardik/GITHUB/sanity-plugin-seofields/npm/dist/head.cjs"}
1
+ {"version":3,"sources":["/Users/hardik/GITHUB/sanity-plugin-seofields/npm/dist/head.cjs"],"names":[],"mappings":"AAAA;AACE;AACA;AACA;AACA;AACA;AACA;AACF,wDAA6B;AAC7B;AACE;AACF,wDAA6B;AAC7B,gCAA6B;AAC7B;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACF,gaAAC","file":"/Users/hardik/GITHUB/sanity-plugin-seofields/npm/dist/head.cjs"}
package/dist/head.d.cts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { a as SanityImage, b as SanityImageWithAlt } from './types-DxlPXihz.cjs';
2
+ export { B as BuildHreflangsOptions, H as HreflangLinkEntry, a as HreflangTranslation, b as buildHreflangs } from './hreflang-_VbSuWYD.cjs';
2
3
 
3
4
  /**
4
5
  * Headless CMS integration helpers for sanity-plugin-seofields
@@ -190,6 +191,17 @@ interface BuildSeoMetaOptions {
190
191
  * imageUrlResolver: (img) => urlFor(img).width(1200).url()
191
192
  */
192
193
  imageUrlResolver?: (image: SanityImage | SanityImageWithAlt) => string | null | undefined;
194
+ /**
195
+ * Override the hreflang alternates. When provided, this supersedes `seo.hreflangs` — pair it with
196
+ * `buildHreflangs()` to derive alternates from `@sanity/document-internationalization` translations.
197
+ *
198
+ * @example
199
+ * hreflangs: buildHreflangs(data._translations, { baseUrl })
200
+ */
201
+ hreflangs?: Array<{
202
+ locale?: string | null;
203
+ url?: string | null;
204
+ }> | null;
193
205
  }
194
206
  declare const VALID_OG_TYPES: readonly ["website", "article", "profile", "book", "music", "video", "product"];
195
207
  type OGType = (typeof VALID_OG_TYPES)[number];
@@ -251,4 +263,52 @@ declare function buildSeoMeta(options: BuildSeoMetaOptions): SeoMetadata;
251
263
  */
252
264
  declare function buildSeoHead(options: BuildSeoMetaOptions): SeoHead;
253
265
 
254
- export { type BuildSeoMetaOptions, type SeoFieldsInput, type SeoHead, type SeoHeadLinkTag, type SeoHeadMetaTag, type SeoMetaDefaults, type SeoMetadata, buildSeoHead, buildSeoMeta, sanitizeOGType, sanitizeTwitterCard };
266
+ interface LlmsLink {
267
+ title: string;
268
+ url: string;
269
+ description?: string;
270
+ }
271
+ interface LlmsSection {
272
+ title: string;
273
+ links: LlmsLink[];
274
+ }
275
+ interface BuildLlmsTxtOptions {
276
+ /** H1 title, e.g. the site name. */
277
+ title: string;
278
+ /** One-line blockquote summary rendered directly under the title. */
279
+ summary?: string;
280
+ /** Free-form Markdown prose rendered after the summary. */
281
+ details?: string;
282
+ /** Link sections rendered as `## {title}` + a bullet list. Empty sections are omitted. */
283
+ sections?: LlmsSection[];
284
+ /** Optional version, rendered in a leading `> Version: … | Updated: …` line. */
285
+ version?: string;
286
+ /** Optional "updated" label, rendered alongside `version`. */
287
+ updated?: string;
288
+ /** Prepended to any relative link URL. */
289
+ baseUrl?: string;
290
+ }
291
+ declare function buildLlmsTxt(options: BuildLlmsTxtOptions): string;
292
+ interface LlmsDoc {
293
+ title?: string | null;
294
+ slug?: {
295
+ current?: string | null;
296
+ } | string | null;
297
+ description?: string | null;
298
+ [key: string]: unknown;
299
+ }
300
+ interface DocsToLlmsSectionOptions {
301
+ /** Section heading. */
302
+ title: string;
303
+ /** Prepended to each document's path. */
304
+ baseUrl?: string;
305
+ /** Build the path for a document. Defaults to `/${slug}`. */
306
+ resolvePath?: (doc: LlmsDoc) => string | null | undefined;
307
+ /** Override the link title. Defaults to `doc.title`. */
308
+ resolveTitle?: (doc: LlmsDoc) => string | null | undefined;
309
+ /** Override the link description. Defaults to `doc.description`. */
310
+ resolveDescription?: (doc: LlmsDoc) => string | null | undefined;
311
+ }
312
+ declare function docsToLlmsSection(docs: LlmsDoc[] | null | undefined, options: DocsToLlmsSectionOptions): LlmsSection;
313
+
314
+ export { type BuildLlmsTxtOptions, type BuildSeoMetaOptions, type DocsToLlmsSectionOptions, type LlmsDoc, type LlmsLink, type LlmsSection, type SeoFieldsInput, type SeoHead, type SeoHeadLinkTag, type SeoHeadMetaTag, type SeoMetaDefaults, type SeoMetadata, buildLlmsTxt, buildSeoHead, buildSeoMeta, docsToLlmsSection, sanitizeOGType, sanitizeTwitterCard };
package/dist/head.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { a as SanityImage, b as SanityImageWithAlt } from './types-DxlPXihz.js';
2
+ export { B as BuildHreflangsOptions, H as HreflangLinkEntry, a as HreflangTranslation, b as buildHreflangs } from './hreflang-_VbSuWYD.js';
2
3
 
3
4
  /**
4
5
  * Headless CMS integration helpers for sanity-plugin-seofields
@@ -190,6 +191,17 @@ interface BuildSeoMetaOptions {
190
191
  * imageUrlResolver: (img) => urlFor(img).width(1200).url()
191
192
  */
192
193
  imageUrlResolver?: (image: SanityImage | SanityImageWithAlt) => string | null | undefined;
194
+ /**
195
+ * Override the hreflang alternates. When provided, this supersedes `seo.hreflangs` — pair it with
196
+ * `buildHreflangs()` to derive alternates from `@sanity/document-internationalization` translations.
197
+ *
198
+ * @example
199
+ * hreflangs: buildHreflangs(data._translations, { baseUrl })
200
+ */
201
+ hreflangs?: Array<{
202
+ locale?: string | null;
203
+ url?: string | null;
204
+ }> | null;
193
205
  }
194
206
  declare const VALID_OG_TYPES: readonly ["website", "article", "profile", "book", "music", "video", "product"];
195
207
  type OGType = (typeof VALID_OG_TYPES)[number];
@@ -251,4 +263,52 @@ declare function buildSeoMeta(options: BuildSeoMetaOptions): SeoMetadata;
251
263
  */
252
264
  declare function buildSeoHead(options: BuildSeoMetaOptions): SeoHead;
253
265
 
254
- export { type BuildSeoMetaOptions, type SeoFieldsInput, type SeoHead, type SeoHeadLinkTag, type SeoHeadMetaTag, type SeoMetaDefaults, type SeoMetadata, buildSeoHead, buildSeoMeta, sanitizeOGType, sanitizeTwitterCard };
266
+ interface LlmsLink {
267
+ title: string;
268
+ url: string;
269
+ description?: string;
270
+ }
271
+ interface LlmsSection {
272
+ title: string;
273
+ links: LlmsLink[];
274
+ }
275
+ interface BuildLlmsTxtOptions {
276
+ /** H1 title, e.g. the site name. */
277
+ title: string;
278
+ /** One-line blockquote summary rendered directly under the title. */
279
+ summary?: string;
280
+ /** Free-form Markdown prose rendered after the summary. */
281
+ details?: string;
282
+ /** Link sections rendered as `## {title}` + a bullet list. Empty sections are omitted. */
283
+ sections?: LlmsSection[];
284
+ /** Optional version, rendered in a leading `> Version: … | Updated: …` line. */
285
+ version?: string;
286
+ /** Optional "updated" label, rendered alongside `version`. */
287
+ updated?: string;
288
+ /** Prepended to any relative link URL. */
289
+ baseUrl?: string;
290
+ }
291
+ declare function buildLlmsTxt(options: BuildLlmsTxtOptions): string;
292
+ interface LlmsDoc {
293
+ title?: string | null;
294
+ slug?: {
295
+ current?: string | null;
296
+ } | string | null;
297
+ description?: string | null;
298
+ [key: string]: unknown;
299
+ }
300
+ interface DocsToLlmsSectionOptions {
301
+ /** Section heading. */
302
+ title: string;
303
+ /** Prepended to each document's path. */
304
+ baseUrl?: string;
305
+ /** Build the path for a document. Defaults to `/${slug}`. */
306
+ resolvePath?: (doc: LlmsDoc) => string | null | undefined;
307
+ /** Override the link title. Defaults to `doc.title`. */
308
+ resolveTitle?: (doc: LlmsDoc) => string | null | undefined;
309
+ /** Override the link description. Defaults to `doc.description`. */
310
+ resolveDescription?: (doc: LlmsDoc) => string | null | undefined;
311
+ }
312
+ declare function docsToLlmsSection(docs: LlmsDoc[] | null | undefined, options: DocsToLlmsSectionOptions): LlmsSection;
313
+
314
+ export { type BuildLlmsTxtOptions, type BuildSeoMetaOptions, type DocsToLlmsSectionOptions, type LlmsDoc, type LlmsLink, type LlmsSection, type SeoFieldsInput, type SeoHead, type SeoHeadLinkTag, type SeoHeadMetaTag, type SeoMetaDefaults, type SeoMetadata, buildLlmsTxt, buildSeoHead, buildSeoMeta, docsToLlmsSection, sanitizeOGType, sanitizeTwitterCard };
package/dist/head.js CHANGED
@@ -1,13 +1,21 @@
1
1
  import {
2
+ buildLlmsTxt,
2
3
  buildSeoHead,
3
4
  buildSeoMeta,
5
+ docsToLlmsSection,
4
6
  sanitizeOGType,
5
7
  sanitizeTwitterCard
6
- } from "./chunk-KIEO6QG3.js";
8
+ } from "./chunk-XD4HKLEA.js";
9
+ import {
10
+ buildHreflangs
11
+ } from "./chunk-CRCXC45D.js";
7
12
  import "./chunk-7N4MLTMR.js";
8
13
  export {
14
+ buildHreflangs,
15
+ buildLlmsTxt,
9
16
  buildSeoHead,
10
17
  buildSeoMeta,
18
+ docsToLlmsSection,
11
19
  sanitizeOGType,
12
20
  sanitizeTwitterCard
13
21
  };
@@ -0,0 +1,29 @@
1
+ interface HreflangTranslation {
2
+ /** BCP 47 language/region tag, e.g. "en", "fr-FR". */
3
+ language?: string | null;
4
+ /** Sanity slug — either the raw string or the `{ current }` object. */
5
+ slug?: {
6
+ current?: string | null;
7
+ } | string | null;
8
+ /** Pre-built path (e.g. "/fr/about"). Wins over `slug`. */
9
+ path?: string | null;
10
+ /** Absolute URL. Wins over `baseUrl` + path. */
11
+ url?: string | null;
12
+ }
13
+ interface BuildHreflangsOptions {
14
+ /** Site origin, e.g. "https://example.com". Prepended to each translation's path. */
15
+ baseUrl?: string;
16
+ /** Build the path for a translation. Defaults to `/${slug}`. Return null/undefined to skip. */
17
+ resolvePath?: (t: HreflangTranslation) => string | null | undefined;
18
+ /** Language tag to also emit as an `x-default` entry (pointing at that language's URL). */
19
+ xDefault?: string;
20
+ /** Include the current document itself in the set. */
21
+ current?: HreflangTranslation;
22
+ }
23
+ interface HreflangLinkEntry {
24
+ locale: string;
25
+ url: string;
26
+ }
27
+ declare function buildHreflangs(translations: HreflangTranslation[] | null | undefined, options?: BuildHreflangsOptions): HreflangLinkEntry[];
28
+
29
+ export { type BuildHreflangsOptions as B, type HreflangLinkEntry as H, type HreflangTranslation as a, buildHreflangs as b };
@@ -0,0 +1,29 @@
1
+ interface HreflangTranslation {
2
+ /** BCP 47 language/region tag, e.g. "en", "fr-FR". */
3
+ language?: string | null;
4
+ /** Sanity slug — either the raw string or the `{ current }` object. */
5
+ slug?: {
6
+ current?: string | null;
7
+ } | string | null;
8
+ /** Pre-built path (e.g. "/fr/about"). Wins over `slug`. */
9
+ path?: string | null;
10
+ /** Absolute URL. Wins over `baseUrl` + path. */
11
+ url?: string | null;
12
+ }
13
+ interface BuildHreflangsOptions {
14
+ /** Site origin, e.g. "https://example.com". Prepended to each translation's path. */
15
+ baseUrl?: string;
16
+ /** Build the path for a translation. Defaults to `/${slug}`. Return null/undefined to skip. */
17
+ resolvePath?: (t: HreflangTranslation) => string | null | undefined;
18
+ /** Language tag to also emit as an `x-default` entry (pointing at that language's URL). */
19
+ xDefault?: string;
20
+ /** Include the current document itself in the set. */
21
+ current?: HreflangTranslation;
22
+ }
23
+ interface HreflangLinkEntry {
24
+ locale: string;
25
+ url: string;
26
+ }
27
+ declare function buildHreflangs(translations: HreflangTranslation[] | null | undefined, options?: BuildHreflangsOptions): HreflangLinkEntry[];
28
+
29
+ export { type BuildHreflangsOptions as B, type HreflangLinkEntry as H, type HreflangTranslation as a, buildHreflangs as b };
package/dist/index.cjs CHANGED
@@ -1,8 +1,11 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } }
2
2
 
3
+ var _chunkXYAZJ3WXcjs = require('./chunk-XYAZJ3WX.cjs');
3
4
 
4
5
 
5
- var _chunk3OK3S2F7cjs = require('./chunk-3OK3S2F7.cjs');
6
+
7
+
8
+ var _chunkEUG4MNRCcjs = require('./chunk-EUG4MNRC.cjs');
6
9
 
7
10
 
8
11
 
@@ -465,11 +468,11 @@ function SeoGenButton({
465
468
  let result;
466
469
  if (ai.testMode) {
467
470
  await new Promise((r) => setTimeout(r, 400));
468
- result = _chunk3OK3S2F7cjs.pickTestOutput.call(void 0, field);
471
+ result = _chunkEUG4MNRCcjs.pickTestOutput.call(void 0, field);
469
472
  } else {
470
473
  const controller = new AbortController();
471
474
  abortRef.current = controller;
472
- result = await _chunk3OK3S2F7cjs.generateSeoText.call(void 0, field, content, focusKeyword, keywords, {
475
+ result = await _chunkEUG4MNRCcjs.generateSeoText.call(void 0, field, content, focusKeyword, keywords, {
473
476
  config: _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {}, ai), { _projectId: workspace.projectId }),
474
477
  signal: controller.signal,
475
478
  meta
@@ -611,6 +614,87 @@ var FocusKeywordInput = (props) => {
611
614
  };
612
615
  var FocusKeywordInput_default = FocusKeywordInput;
613
616
 
617
+ // src/components/meta/HreflangInput.tsx
618
+
619
+
620
+
621
+
622
+
623
+ var DEFAULT_LOCALE_FIELD = "language";
624
+ var DEFAULT_API_VERSION = "2024-01-01";
625
+ function safeField(name) {
626
+ return name && /^[A-Za-z_][A-Za-z0-9_]*$/.test(name) ? name : DEFAULT_LOCALE_FIELD;
627
+ }
628
+ var HreflangInput = (props) => {
629
+ const { schemaType, onChange, renderDefault } = props;
630
+ const { options } = schemaType;
631
+ const { hreflang, baseUrl, apiVersion } = options != null ? options : {};
632
+ const rootDoc = _sanity.useFormValue.call(void 0, []);
633
+ const client = _sanity.useClient.call(void 0, { apiVersion: apiVersion != null ? apiVersion : DEFAULT_API_VERSION });
634
+ const [state, setState] = _react.useState.call(void 0, "idle");
635
+ const [message, setMessage] = _react.useState.call(void 0, null);
636
+ const localeField = safeField(hreflang == null ? void 0 : hreflang.localeField);
637
+ const handleSync = _react.useCallback.call(void 0, async () => {
638
+ var _a;
639
+ const rawId = rootDoc == null ? void 0 : rootDoc._id;
640
+ if (!rawId) {
641
+ setState("error");
642
+ setMessage("Save the document once before syncing translations.");
643
+ return;
644
+ }
645
+ setState("loading");
646
+ setMessage(null);
647
+ try {
648
+ const publishedId = rawId.replace(/^drafts\./, "");
649
+ const query = `*[_type == "translation.metadata" && references($id)][0].translations[].value->{
650
+ "language": ${localeField},
651
+ "slug": slug.current
652
+ }`;
653
+ const translations = (_a = await client.fetch(query, { id: publishedId })) != null ? _a : [];
654
+ const entries = _chunkXYAZJ3WXcjs.buildHreflangs.call(void 0, translations, {
655
+ baseUrl,
656
+ resolvePath: hreflang == null ? void 0 : hreflang.resolvePath
657
+ });
658
+ if (entries.length === 0) {
659
+ setState("error");
660
+ setMessage("No translations with a resolvable URL were found for this document.");
661
+ return;
662
+ }
663
+ const items = entries.map((e) => ({
664
+ _type: "hreflangEntry",
665
+ _key: e.locale,
666
+ locale: e.locale,
667
+ url: e.url
668
+ }));
669
+ onChange(_sanity.PatchEvent.from(_sanity.set.call(void 0, items)));
670
+ setState("done");
671
+ setMessage(
672
+ `Synced ${items.length} alternate${items.length === 1 ? "" : "s"} from translations.`
673
+ );
674
+ } catch (err) {
675
+ setState("error");
676
+ setMessage(err instanceof Error ? err.message : "Failed to sync translations.");
677
+ }
678
+ }, [rootDoc == null ? void 0 : rootDoc._id, client, localeField, baseUrl, hreflang, onChange]);
679
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _ui.Stack, { space: 3, children: [
680
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Flex, { children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
681
+ _ui.Button,
682
+ {
683
+ mode: "ghost",
684
+ tone: "primary",
685
+ fontSize: 1,
686
+ icon: _icons.TranslateIcon,
687
+ text: state === "loading" ? "Syncing\u2026" : "Sync from translations",
688
+ onClick: handleSync,
689
+ disabled: state === "loading"
690
+ }
691
+ ) }),
692
+ message && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Card, { padding: 2, radius: 2, tone: state === "error" ? "critical" : "positive", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _ui.Text, { size: 1, children: message }) }),
693
+ renderDefault(props)
694
+ ] });
695
+ };
696
+ var HreflangInput_default = HreflangInput;
697
+
614
698
  // src/components/meta/KeywordsInput.tsx
615
699
 
616
700
 
@@ -651,9 +735,9 @@ function KeywordSuggestions({
651
735
  let csv;
652
736
  if (ai.testMode) {
653
737
  await new Promise((r) => setTimeout(r, 400));
654
- csv = _chunk3OK3S2F7cjs.pickTestOutput.call(void 0, "keywords");
738
+ csv = _chunkEUG4MNRCcjs.pickTestOutput.call(void 0, "keywords");
655
739
  } else {
656
- csv = await _chunk3OK3S2F7cjs.generateSeoText.call(void 0, "keywords", content, focusKeyword, existing, {
740
+ csv = await _chunkEUG4MNRCcjs.generateSeoText.call(void 0, "keywords", content, focusKeyword, existing, {
657
741
  config: _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {}, ai), { _projectId: workspace.projectId }),
658
742
  signal: abortRef.current.signal
659
743
  });
@@ -852,7 +936,7 @@ var MetaDescription = (props) => {
852
936
  () => _chunkY46ACXM4cjs.getMetaDescriptionValidationMessages.call(void 0, value || "", keywords, isParentseoField),
853
937
  [value, keywords, isParentseoField]
854
938
  );
855
- const readability = _react.useMemo.call(void 0, () => _chunk3OK3S2F7cjs.analyzeReadability.call(void 0, value || ""), [value]);
939
+ const readability = _react.useMemo.call(void 0, () => _chunkEUG4MNRCcjs.analyzeReadability.call(void 0, value || ""), [value]);
856
940
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _ui.Stack, { space: 3, children: [
857
941
  renderDefault(props),
858
942
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
@@ -2023,6 +2107,7 @@ function buildMetaTagsPreviewField(config, fieldGroupMap) {
2023
2107
  ];
2024
2108
  }
2025
2109
  function seoFieldsSchema(config = {}) {
2110
+ var _a;
2026
2111
  const groupsCfg = config.fieldGroups;
2027
2112
  const fieldGroupMap = (groupsCfg == null ? void 0 : groupsCfg.length) ? buildFieldGroupMap(groupsCfg) : void 0;
2028
2113
  const sanityGroups = (groupsCfg == null ? void 0 : groupsCfg.length) ? toSanityGroups(groupsCfg) : void 0;
@@ -2147,12 +2232,20 @@ function seoFieldsSchema(config = {}) {
2147
2232
  fieldGroupMap
2148
2233
  ),
2149
2234
  withGroup(
2150
- _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
2235
+ _sanity.defineField.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, _chunkA57XNQC7cjs.__spreadProps.call(void 0, _chunkA57XNQC7cjs.__spreadValues.call(void 0, {
2151
2236
  name: "hreflangs"
2152
2237
  }, getFieldInfo("hreflangs", config.fieldOverrides)), {
2153
2238
  type: "array",
2154
2239
  of: [{ type: "hreflangEntry" }],
2155
- description: "Add alternate language/region versions. Include x-default for the fallback URL.",
2240
+ description: "Add alternate language/region versions. Include x-default for the fallback URL."
2241
+ }), ((_a = config.hreflang) == null ? void 0 : _a.autoFill) ? {
2242
+ components: { input: HreflangInput_default },
2243
+ options: {
2244
+ hreflang: config.hreflang,
2245
+ baseUrl: config.baseUrl,
2246
+ apiVersion: config.apiVersion
2247
+ }
2248
+ } : {}), {
2156
2249
  hidden: getFieldHiddenFunction("hreflangs", config)
2157
2250
  })),
2158
2251
  fieldGroupMap