sanity-plugin-seofields 1.8.0 → 1.9.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 (113) hide show
  1. package/README.md +437 -26
  2. package/dist/SeoHealthTool-2COI27KI.cjs +8 -0
  3. package/dist/SeoHealthTool-2COI27KI.cjs.map +1 -0
  4. package/dist/SeoHealthTool-CWI3KB2V.js +8 -0
  5. package/dist/SeoHealthTool-CWI3KB2V.js.map +1 -0
  6. package/dist/{SeoPreview-3EXR6FD4.cjs → SeoPreview-LMZAWWOE.cjs} +35 -38
  7. package/dist/SeoPreview-LMZAWWOE.cjs.map +1 -0
  8. package/dist/{SeoPreview-4PPAF4NO.js → SeoPreview-UNQBKTQO.js} +14 -11
  9. package/dist/SeoPreview-UNQBKTQO.js.map +1 -0
  10. package/dist/chunk-3OK3S2F7.cjs +673 -0
  11. package/dist/chunk-3OK3S2F7.cjs.map +1 -0
  12. package/dist/{chunk-B5IVI5LP.cjs → chunk-5XTQRILL.cjs} +286 -236
  13. package/dist/chunk-5XTQRILL.cjs.map +1 -0
  14. package/dist/{chunk-254YHUN3.cjs → chunk-7N4MLTMR.js} +8 -6
  15. package/dist/chunk-7N4MLTMR.js.map +1 -0
  16. package/dist/{chunk-DDAAVRWG.js → chunk-A57XNQC7.cjs} +9 -4
  17. package/dist/chunk-A57XNQC7.cjs.map +1 -0
  18. package/dist/chunk-BWLJDK5J.js +624 -0
  19. package/dist/chunk-BWLJDK5J.js.map +1 -0
  20. package/dist/chunk-FUUWBQE2.cjs +195 -0
  21. package/dist/chunk-FUUWBQE2.cjs.map +1 -0
  22. package/dist/{chunk-ZBHLMQTS.cjs → chunk-HHO2AKAP.js} +44 -17
  23. package/dist/chunk-HHO2AKAP.js.map +1 -0
  24. package/dist/chunk-KIEO6QG3.js +195 -0
  25. package/dist/chunk-KIEO6QG3.js.map +1 -0
  26. package/dist/chunk-R2U7JF7U.cjs +624 -0
  27. package/dist/chunk-R2U7JF7U.cjs.map +1 -0
  28. package/dist/{chunk-VR44E62I.js → chunk-XZKQWV2H.js} +204 -33
  29. package/dist/chunk-XZKQWV2H.js.map +1 -0
  30. package/dist/{chunk-HDZZQCH7.js → chunk-Y46ACXM4.cjs} +45 -5
  31. package/dist/chunk-Y46ACXM4.cjs.map +1 -0
  32. package/dist/chunk-Z5AOUU4H.js +673 -0
  33. package/dist/chunk-Z5AOUU4H.js.map +1 -0
  34. package/dist/cli.js +27 -27
  35. package/dist/{component-DEXwtemT.d.ts → component-CjT1hvxh.d.ts} +2 -2
  36. package/dist/{component-BzI-LHPw.d.cts → component-WYh0z8as.d.cts} +2 -2
  37. package/dist/define-cli.cjs +2 -4
  38. package/dist/define-cli.cjs.map +1 -1
  39. package/dist/define-cli.js +4 -4
  40. package/dist/define-cli.js.map +1 -1
  41. package/dist/head.cjs +14 -0
  42. package/dist/head.cjs.map +1 -0
  43. package/dist/head.d.cts +254 -0
  44. package/dist/head.d.ts +254 -0
  45. package/dist/head.js +14 -0
  46. package/dist/head.js.map +1 -0
  47. package/dist/index.cjs +978 -367
  48. package/dist/index.cjs.map +1 -1
  49. package/dist/index.d.cts +6 -374
  50. package/dist/index.d.ts +6 -374
  51. package/dist/index.js +920 -286
  52. package/dist/index.js.map +1 -1
  53. package/dist/next.cjs +204 -455
  54. package/dist/next.cjs.map +1 -1
  55. package/dist/next.d.cts +8 -206
  56. package/dist/next.d.ts +8 -206
  57. package/dist/next.js +181 -111
  58. package/dist/next.js.map +1 -1
  59. package/dist/plugin-DQdThyxG.d.ts +449 -0
  60. package/dist/plugin-DoGeZP79.d.cts +449 -0
  61. package/dist/schema/next.cjs +173 -327
  62. package/dist/schema/next.cjs.map +1 -1
  63. package/dist/schema/next.d.cts +4 -4
  64. package/dist/schema/next.d.ts +4 -4
  65. package/dist/schema/next.js +172 -8
  66. package/dist/schema/next.js.map +1 -1
  67. package/dist/schema.cjs +376 -517
  68. package/dist/schema.cjs.map +1 -1
  69. package/dist/schema.d.cts +5 -5
  70. package/dist/schema.d.ts +5 -5
  71. package/dist/schema.js +216 -11
  72. package/dist/schema.js.map +1 -1
  73. package/dist/server.cjs +149 -0
  74. package/dist/server.cjs.map +1 -0
  75. package/dist/server.d.cts +102 -0
  76. package/dist/server.d.ts +102 -0
  77. package/dist/server.js +149 -0
  78. package/dist/server.js.map +1 -0
  79. package/dist/{types-CW8qFdAn.d.ts → types-BAdN1RMu.d.ts} +2 -2
  80. package/dist/{types-B_vjMPtM.d.cts → types-C5i3KMgs.d.cts} +2 -2
  81. package/dist/{types-BXSCbl6p.d.cts → types-DP-DiW6f.d.cts} +2 -2
  82. package/dist/{types-C19cQx5D.d.ts → types-DRLUGLtX.d.ts} +2 -2
  83. package/dist/{types-yVmQfby9.d.cts → types-DxlPXihz.d.cts} +1 -1
  84. package/dist/{types-yVmQfby9.d.ts → types-DxlPXihz.d.ts} +1 -1
  85. package/package.json +25 -4
  86. package/dist/SeoHealthDashboard-AEKVVMUR-7NWV3C3Y.js +0 -4
  87. package/dist/SeoHealthDashboard-AEKVVMUR-7NWV3C3Y.js.map +0 -1
  88. package/dist/SeoHealthDashboard-AEKVVMUR-XWKJUH24.cjs +0 -10
  89. package/dist/SeoHealthDashboard-AEKVVMUR-XWKJUH24.cjs.map +0 -1
  90. package/dist/SeoHealthTool-2KKQJLJL.cjs +0 -11
  91. package/dist/SeoHealthTool-2KKQJLJL.cjs.map +0 -1
  92. package/dist/SeoHealthTool-3CUKGADZ.js +0 -5
  93. package/dist/SeoHealthTool-3CUKGADZ.js.map +0 -1
  94. package/dist/SeoPreview-3EXR6FD4.cjs.map +0 -1
  95. package/dist/SeoPreview-4PPAF4NO.js.map +0 -1
  96. package/dist/chunk-254YHUN3.cjs.map +0 -1
  97. package/dist/chunk-27XSYENH.js +0 -2215
  98. package/dist/chunk-27XSYENH.js.map +0 -1
  99. package/dist/chunk-B5IVI5LP.cjs.map +0 -1
  100. package/dist/chunk-BNXMFD3T.cjs +0 -431
  101. package/dist/chunk-BNXMFD3T.cjs.map +0 -1
  102. package/dist/chunk-DDAAVRWG.js.map +0 -1
  103. package/dist/chunk-HDZZQCH7.js.map +0 -1
  104. package/dist/chunk-P6IOWIO2.cjs +0 -480
  105. package/dist/chunk-P6IOWIO2.cjs.map +0 -1
  106. package/dist/chunk-UOCZFYYP.js +0 -407
  107. package/dist/chunk-UOCZFYYP.js.map +0 -1
  108. package/dist/chunk-VR44E62I.js.map +0 -1
  109. package/dist/chunk-WKXHX3GO.js +0 -424
  110. package/dist/chunk-WKXHX3GO.js.map +0 -1
  111. package/dist/chunk-XZDLJ2N6.cjs +0 -2224
  112. package/dist/chunk-XZDLJ2N6.cjs.map +0 -1
  113. package/dist/chunk-ZBHLMQTS.cjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/readability.ts","../src/utils/seoPrompts.ts","../src/utils/seoContentGen.ts"],"sourcesContent":["export interface ReadabilityResult {\n score: number\n gradeLevel: number\n avgWordsPerSentence: number\n words: number\n sentences: number\n label: string\n color: 'green' | 'orange' | 'red'\n}\n\n/**\n * Count vowel groups in a single word (improved — no regex stripping hacks).\n * - Lowercase, strip non-alpha\n * - If <= 3 chars -> 1 syllable\n * - Count consecutive vowel groups ([aeiouy]+ matches)\n * - Special case: silent trailing 'e' after consonant -> subtract 1 syllable, min 1\n */\nfunction countSyllables(word: string): number {\n const w = word.toLowerCase().replace(/[^a-z]/g, '')\n if (w.length <= 3) return 1\n\n // Count consecutive vowel groups\n const groups = w.match(/[aeiouy]+/g) ?? []\n let count = groups.length\n\n // Special case: silent trailing 'e' after consonant -> subtract 1 syllable, min 1\n if (w.endsWith('e') && w.length > 1 && !/[aeiouy]/.test(w[w.length - 2])) {\n count = Math.max(1, count - 1)\n }\n\n return Math.max(1, count)\n}\n\n/**\n * Split text on sentence boundaries (., !, ?)\n */\nfunction countSentences(text: string): number {\n const matches = text.match(/[.!?]+/g)\n return matches ? matches.length : 1\n}\n\n/**\n * Compute Flesch Reading Ease and Flesch-Kincaid Grade Level.\n * Returns null if text has fewer than 10 words.\n *\n * Flesch Reading Ease: 206.835 - 1.015*(words/sentences) - 84.6*(syllables/words)\n * FK Grade Level: 0.39*(words/sentences) + 11.8*(syllables/words) - 15.59\n */\nexport function analyzeReadability(text: string): ReadabilityResult | null {\n const wordList = text.trim().split(/\\s+/).filter(Boolean)\n const words = wordList.length\n if (words < 10) return null\n\n const sentences = countSentences(text)\n const syllables = wordList.reduce((acc, word) => acc + countSyllables(word), 0)\n\n const rawScore = 206.835 - 1.015 * (words / sentences) - 84.6 * (syllables / words)\n const score = Math.round(rawScore * 10) / 10\n\n const rawGrade = 0.39 * (words / sentences) + 11.8 * (syllables / words) - 15.59\n const gradeLevel = Math.round(rawGrade * 10) / 10\n\n const avgWordsPerSentence = Math.round((words / sentences) * 10) / 10\n\n let label: string\n let color: 'green' | 'orange' | 'red'\n\n if (score >= 70) {\n label = 'Easy to read'\n color = 'green'\n } else if (score >= 50) {\n label = 'Fairly easy'\n color = 'green'\n } else if (score >= 30) {\n label = 'Moderately difficult'\n color = 'orange'\n } else {\n label = 'Difficult to read'\n color = 'red'\n }\n\n return {\n score,\n gradeLevel,\n avgWordsPerSentence,\n words,\n sentences,\n label,\n color,\n }\n}\n","import type {AiIndustry} from '../plugin'\n\nexport const FREE_INDUSTRIES = [\n 'blog',\n 'restaurant',\n 'travel',\n 'ecommerce',\n 'education',\n 'fitness',\n 'hospitality',\n 'nonprofit',\n] as const\nexport const PRO_INDUSTRIES = [\n 'healthcare',\n 'pharmacy',\n 'finance',\n 'realestate',\n 'saas',\n 'legal',\n 'insurance',\n 'automotive',\n 'homeServices',\n] as const\nconst PUBLIC_ANGLE_COUNT = 4\n\nexport type SeoGenField =\n | 'title'\n | 'description'\n | 'focusKeyword'\n | 'keywords'\n | 'ogTitle'\n | 'ogDescription'\n | 'twitterTitle'\n | 'twitterDescription'\n\nexport type MetaContext = {title?: string; description?: string; slug?: string}\n\ntype PromptParams = {content: string; keyword: string; keywords: string[]; meta?: MetaContext}\ntype PromptFn = (p: PromptParams) => string\ntype AngleFn = (params: PromptParams & {ctx: string}) => string\ntype FieldAngles = Record<SeoGenField, AngleFn[]>\ntype PublicIndustry = (typeof FREE_INDUSTRIES)[number]\ntype LicensedPromptPoolResolver = (options: {\n licenseKey?: string\n projectId?: string\n field: SeoGenField\n industry?: AiIndustry\n /** Free-tier pool — returned as-is when the license fails validation or the pro package is unavailable. */\n publicPrompts: PromptFn[]\n}) => Promise<PromptFn[]>\n\nconst GENERIC_CTX = 'Write for a general web audience.'\n\nfunction buildKeywordLine(\n keyword: string,\n keywords: string[],\n keywordRequirement: string,\n fallback: string,\n): string {\n if (keywords.length > 0) {\n return `- At least one of these keywords MUST appear verbatim: ${keywords.join(', ')}`\n }\n\n if (keyword) {\n return `- The keyword \"${keyword}\" MUST appear ${keywordRequirement}`\n }\n\n return fallback\n}\n\nconst PUBLIC_INDUSTRY_CONTEXTS = {\n blog: 'Write for a blog or editorial publication. Use engaging, informative language. Appeal to curious readers seeking knowledge.',\n ecommerce:\n 'Write for an e-commerce store. Use action-oriented language that drives purchases. Highlight value, deals, and product benefits.',\n education:\n 'Write for an educational platform or course provider. Use encouraging, growth-focused language. Emphasize skills, outcomes, and career impact.',\n restaurant:\n 'Write for a restaurant, cafe, or food business. Use sensory, appetizing language. Evoke taste, ambiance, and memorable dining experience.',\n travel:\n 'Write for a travel agency or tourism brand. Use evocative, inspiring language. Paint vivid pictures of destinations and unforgettable experiences.',\n fitness:\n 'Write for a gym, studio, or personal training brand. Use motivating, energetic language. Emphasize results, transformation, and community.',\n hospitality:\n 'Write for a hotel, resort, or short-term rental brand. Use welcoming, sensory language. Evoke comfort, amenities, and a memorable stay.',\n nonprofit:\n 'Write for a nonprofit or charitable organization. Use sincere, mission-driven language. Emphasize impact, community, and the cause over sales language.',\n} satisfies Record<PublicIndustry, string>\n\n// ─── Title angles ────────────────────────────────────────────────────────────\n\n// Only the 4 free-tier angles live here — the remaining 6 per field live in `seofields-pro`\n// and are never shipped in this package's bundle. See resolvePromptPool below.\nconst TITLE_ANGLES = [\n 'Lead with a strong action verb',\n 'Lead with the key benefit to the reader',\n 'Use a how-to format (How to …)',\n 'Use a question format that the reader wants answered',\n]\n\nconst titleFns: AngleFn[] = TITLE_ANGLES.map((angle) => ({content, keyword, keywords, ctx}) => {\n const kwLine = buildKeywordLine(\n keyword,\n keywords,\n 'verbatim in the title',\n '- Make it compelling and clear without a specific keyword',\n )\n return `You are an SEO expert. ${ctx}\nGenerate one SEO-optimized meta title using this angle: ${angle}.\nRequirements:\n- Strictly 50–60 characters including spaces and punctuation\n${kwLine}\n- Apply the angle — do not deviate\n- Return ONLY the title text. No quotes, no explanation.\n\nPage content:\n${content}`\n})\n\n// ─── Description angles ───────────────────────────────────────────────────────\n\nconst DESC_ANGLES = [\n \"Open with a direct answer to the reader's likely question, then add a clear call to action\",\n 'Open with the pain point the reader has, then offer the solution this page provides',\n 'Open with a bold claim, then briefly back it up',\n 'Open with who this is for, then state what they will get',\n]\n\nconst descFns: AngleFn[] = DESC_ANGLES.map((angle) => ({content, keyword, keywords, ctx}) => {\n const kwLine = buildKeywordLine(\n keyword,\n keywords,\n 'naturally in the description',\n '- Include a clear call to action',\n )\n return `You are an SEO expert. ${ctx}\nGenerate one SEO-optimized meta description using this angle: ${angle}.\nRequirements:\n- Strictly 120–160 characters including spaces and punctuation\n${kwLine}\n- Short sentences (under 15 words each), plain everyday words, no jargon — aim for high readability\n- Apply the angle — do not deviate\n- Return ONLY the description text. No quotes, no explanation.\n\nPage content:\n${content}`\n})\n\n// ─── OG Title angles ──────────────────────────────────────────────────────────\n\nconst OG_TITLE_ANGLES = [\n 'Curiosity hook — make the reader need to click to find out',\n 'FOMO angle — what they will miss if they do not read this',\n 'Social proof angle — imply consensus, popularity, or trust',\n 'Question angle — pose the exact question the audience is thinking',\n]\n\nconst ogTitleFns: AngleFn[] = OG_TITLE_ANGLES.map(\n (angle) =>\n ({content, keyword, ctx}) =>\n `You are a social media and SEO expert. ${ctx}\nGenerate one Open Graph title for social sharing using this angle: ${angle}.\nRequirements:\n- Strictly 40–60 characters\n- Optimized for social sharing engagement, not just search\n- Include the core topic clearly${keyword ? ` (topic: \"${keyword}\")` : ''}\n- Apply the angle above — do not deviate from it\n- Return ONLY the OG title text. No quotes, no explanation.\n\nPage content:\n${content}`,\n)\n\n// ─── OG Description angles ────────────────────────────────────────────────────\n\nconst OG_DESC_ANGLES = [\n 'Focus on what the reader gets — concrete takeaways or outcomes',\n 'Transformation promise — describe the before/after state',\n 'Bold claim backed by a brief piece of evidence or context',\n 'Pain relief framing — identify the problem and promise relief',\n]\n\nconst ogDescFns: AngleFn[] = OG_DESC_ANGLES.map(\n (angle) =>\n ({content, keyword, ctx}) =>\n `You are a social media and SEO expert. ${ctx}\nGenerate one Open Graph description for social sharing using this angle: ${angle}.\nRequirements:\n- Strictly 90–120 characters\n- Compelling enough to drive clicks from social feeds\n- Include the core topic naturally${keyword ? ` (topic: \"${keyword}\")` : ''}\n- Apply the angle above — do not deviate from it\n- Return ONLY the OG description text. No quotes, no explanation.\n\nPage content:\n${content}`,\n)\n\n// ─── Twitter Title angles ─────────────────────────────────────────────────────\n\nconst TW_TITLE_ANGLES = [\n 'Punchy curiosity hook — make the reader need to click',\n 'Direct FOMO — what they miss by scrolling past',\n 'Conversational question the audience is silently asking',\n 'Bold, scroll-stopping claim in plain language',\n]\n\nconst twTitleFns: AngleFn[] = TW_TITLE_ANGLES.map(\n (angle) =>\n ({content, keyword, ctx}) =>\n `You are a social media copywriter. ${ctx}\nGenerate one X (Twitter) card title using this angle: ${angle}.\nRequirements:\n- Strictly 30–70 characters\n- Punchy and direct — Twitter audiences scroll fast\n- Include the core topic naturally${keyword ? ` (topic: \"${keyword}\")` : ''}\n- Apply the angle above — do not deviate from it\n- Return ONLY the title text. No quotes, no explanation.\n\nPage content:\n${content}`,\n)\n\n// ─── Twitter Description angles ───────────────────────────────────────────────\n\nconst TW_DESC_ANGLES = [\n 'Conversational direct answer — what the page gives you',\n 'Pain-point opener followed by relief in one sentence',\n 'Bold hook followed by the core benefit',\n 'Short-form transformation promise',\n]\n\nconst twDescFns: AngleFn[] = TW_DESC_ANGLES.map(\n (angle) =>\n ({content, keyword, ctx}) =>\n `You are a social media copywriter. ${ctx}\nGenerate one X (Twitter) card description using this angle: ${angle}.\nRequirements:\n- Strictly 50–200 characters\n- Conversational and punchy — casual Twitter tone\n- Include the core topic naturally${keyword ? ` (topic: \"${keyword}\")` : ''}\n- Apply the angle above — do not deviate from it\n- Return ONLY the description text. No quotes, no explanation.\n\nPage content:\n${content}`,\n)\n\n// ─── Focus Keyword angles ─────────────────────────────────────────────────────\n\nconst FK_ANGLES = [\n 'Identify the single keyword phrase a user would most likely search to find this page',\n 'Identify the primary search intent keyword — what the reader types when looking for this content',\n 'Extract the most commercially or informationally valuable keyword from this content',\n 'Identify the exact phrase that best captures the core topic of this page',\n]\n\nconst fkFns: AngleFn[] = FK_ANGLES.map((angle) => ({content, meta, ctx}) => {\n const hasTitle = Boolean(meta?.title)\n const hasDescription = Boolean(meta?.description)\n const hasSlug = Boolean(meta?.slug)\n\n const metaBlock =\n hasTitle || hasDescription || hasSlug\n ? `\\nExisting page fields:\n${hasTitle ? `Meta title: \"${meta!.title}\"` : ''}\n${hasDescription ? `Meta description: \"${meta!.description}\"` : ''}\n${hasSlug ? `URL slug: \"${meta!.slug}\"` : ''}\n\nCRITICAL: The keyword you return MUST appear verbatim (exact match, case-insensitive) inside the meta title${hasDescription ? ' and meta description' : ''} above.\nDo NOT invent a new keyword — extract one that already exists in those fields.\n${hasSlug ? 'Prefer a phrase that also appears in the URL slug.' : ''}\n`\n : ''\n\n return `You are an SEO expert. ${ctx}\nTask: ${angle}.\n${metaBlock}\nRequirements:\n- Return ONLY the keyword or keyword phrase (1–4 words typically)\n- It MUST appear verbatim in the meta title${hasDescription ? ' and description' : ''} provided above\n- No explanation, no quotes, no punctuation, no extra words\n- Single line response\n\nPage content:\n${content}`\n})\n\n// ─── Keywords angles ──────────────────────────────────────────────────────────\n\nconst KW_ANGLES = [\n 'Generate 8 keywords mixing high-volume short-tail and specific long-tail phrases',\n 'Generate 8 keywords that cover both the main topic and closely related subtopics',\n 'Generate 8 keywords targeting both informational and commercial search intent',\n 'Generate 8 keywords including question-based phrases people would search',\n]\n\nconst kwFns: AngleFn[] = KW_ANGLES.map(\n (angle) =>\n ({content, keyword, ctx}) =>\n `You are an SEO expert. ${ctx}\n${angle}${keyword ? ` Include \"${keyword}\" as one of the keywords.` : ''}\nRequirements:\n- Return exactly 8 keywords as a comma-separated list\n- No numbering, no explanation, no quotes around individual keywords\n- Mix short-tail (1–2 words) and long-tail (3–5 words) phrases\n- All keywords must be directly relevant to the page content\n\nPage content:\n${content}`,\n)\n\n// ─── Angle map ────────────────────────────────────────────────────────────────\n\nconst BASE_ANGLES: FieldAngles = {\n title: titleFns,\n description: descFns,\n focusKeyword: fkFns,\n keywords: kwFns,\n ogTitle: ogTitleFns,\n ogDescription: ogDescFns,\n twitterTitle: twTitleFns,\n twitterDescription: twDescFns,\n}\n\nfunction buildFieldPrompts(ctx: string, count: number): Record<SeoGenField, PromptFn[]> {\n const out = {} as Record<SeoGenField, PromptFn[]>\n for (const field of Object.keys(BASE_ANGLES) as SeoGenField[]) {\n out[field] = BASE_ANGLES[field]\n .slice(0, count)\n .map((fn) => (p: PromptParams) => fn({...p, ctx}))\n }\n return out\n}\n\nexport const INDUSTRY_PROMPTS = Object.fromEntries(\n (Object.keys(PUBLIC_INDUSTRY_CONTEXTS) as PublicIndustry[]).map((industry) => [\n industry,\n buildFieldPrompts(PUBLIC_INDUSTRY_CONTEXTS[industry], PUBLIC_ANGLE_COUNT),\n ]),\n) as Record<PublicIndustry, Record<SeoGenField, PromptFn[]>>\n\nexport const DEFAULT_PROMPTS = buildFieldPrompts(GENERIC_CTX, PUBLIC_ANGLE_COUNT)\n\nasync function resolvePromptPool(\n field: SeoGenField,\n industry?: AiIndustry,\n licenseKey?: string,\n projectId?: string,\n): Promise<PromptFn[]> {\n const isFreeIndustry = Boolean(\n industry && (FREE_INDUSTRIES as readonly string[]).includes(industry),\n )\n const publicPrompts = isFreeIndustry\n ? INDUSTRY_PROMPTS[industry as PublicIndustry][field]\n : DEFAULT_PROMPTS[field]\n\n if (!licenseKey) return publicPrompts\n\n // The remaining angles (up to 10 total) live entirely in `seofields-pro` — never shipped in\n // this package's bundle — and are only returned once the license actually validates.\n try {\n const mod = (await import('seofields-pro')) as {\n resolveLicensedPromptPool?: LicensedPromptPoolResolver\n }\n // Pro package unavailable — an unverified licenseKey alone must never unlock the pool.\n if (typeof mod.resolveLicensedPromptPool !== 'function') return publicPrompts\n\n return mod.resolveLicensedPromptPool({\n licenseKey,\n projectId,\n field,\n industry,\n publicPrompts,\n })\n } catch {\n return publicPrompts\n }\n}\n\nexport async function pickPrompt(\n field: SeoGenField,\n params: PromptParams,\n industry?: AiIndustry,\n licenseKey?: string,\n projectId?: string,\n): Promise<string> {\n const pool = await resolvePromptPool(field, industry, licenseKey, projectId)\n const fn = pool[Math.floor(Math.random() * pool.length)]\n return fn(params)\n}\n\n// ─── Test mode static outputs ─────────────────────────────────────────────────\n\nexport const TEST_MODE_OUTPUTS: Record<SeoGenField, string[]> = {\n title: [\n 'Boost Your SEO Rankings With These Proven Tips',\n 'How to Write Meta Titles That Drive More Clicks',\n '10 Strategies to Improve Your Search Visibility',\n 'Why Your Meta Title Is Killing Your Click-Through Rate',\n ],\n description: [\n 'Learn how to write SEO-optimized meta titles that rank higher and earn more clicks. Discover the exact techniques used by top SEO professionals.',\n 'Struggling with low organic traffic? Our complete guide covers everything you need to fix your meta tags and climb the search rankings fast.',\n 'Meta titles under 60 characters get the best click-through rates. Find out how to craft compelling titles that rank and convert visitors.',\n 'This guide is for content creators and marketers who want to improve search rankings without technical expertise. Start seeing results today.',\n ],\n focusKeyword: [\n 'seo meta title',\n 'on-page seo',\n 'meta tag optimization',\n 'organic traffic growth',\n ],\n keywords: [\n 'seo tips, meta title optimization, on-page seo, search rankings, click-through rate, seo guide, organic traffic, meta description, keyword placement, seo best practices',\n 'meta tags, title tag optimization, serp optimization, seo ranking factors, page title seo, seo for beginners, improve search rankings, seo checklist, meta title length, seo tools',\n 'on-page optimization, meta tag guide, seo title tips, increase organic traffic, search visibility, seo writing, title tag examples, meta description length, content seo, seo strategy',\n 'seo optimization guide, meta title examples, serp click-through rate, google ranking tips, seo content writing, page title best practices, keyword in title, seo traffic, rank higher google, search engine results',\n ],\n ogTitle: [\n 'The SEO Secret That Tripled Our Organic Traffic',\n 'Are You Making These 5 Common Meta Title Mistakes?',\n 'This One Change Can Double Your Search Click-Through Rate',\n 'Why 90% of Meta Titles Fail to Drive Clicks',\n ],\n ogDescription: [\n 'Get the exact framework top SEO teams use to write meta titles that rank higher and earn more clicks from search results.',\n 'Before: low CTR and invisible search rankings. After: consistent page-one results. Here is how to make that shift with your meta tags.',\n 'Research shows properly optimized meta titles get 30% more clicks. We show you exactly how to achieve that in under 10 minutes.',\n 'If your page is not getting clicks from search results, your meta title is likely the problem. Here is how to diagnose and fix it.',\n ],\n twitterTitle: [\n 'The Meta Title Trick That Tripled Our Traffic',\n \"You're Probably Writing Meta Titles Wrong\",\n 'One Change That Doubles Your Click-Through Rate',\n 'Why Most Meta Titles Fail (And How to Fix Yours)',\n ],\n twitterDescription: [\n \"Most pages leave clicks on the table because of weak meta titles. Here's the fix — in under 10 minutes.\",\n \"Low search CTR? Your meta title is probably the culprit. Here's how to diagnose and fix it today.\",\n 'Optimized meta titles get 30% more clicks. This guide shows you exactly how to write one that works.',\n 'Stop writing generic meta titles. This thread-worthy guide gives you templates that rank and convert.',\n ],\n}\n\nexport function pickTestOutput(field: SeoGenField): string {\n const arr = TEST_MODE_OUTPUTS[field]\n return arr[Math.floor(Math.random() * arr.length)]\n}\n","import type {AiConfig, AiIndustry} from '../plugin'\nimport {analyzeReadability} from './readability'\nimport {type MetaContext, pickPrompt, type SeoGenField} from './seoPrompts'\n\nconst DEFAULT_MAX_RETRIES = 2\n\nconst PROVIDER_DEFAULTS: Record<string, {baseUrl: string; model: string; requiresKey: boolean}> = {\n openai: {baseUrl: 'https://api.openai.com/v1', model: 'gpt-4o-mini', requiresKey: true},\n anthropic: {\n baseUrl: 'https://api.anthropic.com',\n model: 'claude-haiku-4-5-20251001',\n requiresKey: true,\n },\n groq: {\n baseUrl: 'https://api.groq.com/openai/v1',\n model: 'llama-3.3-70b-versatile',\n requiresKey: true,\n },\n gemini: {\n baseUrl: 'https://generativelanguage.googleapis.com/v1beta/openai',\n model: 'gemini-2.0-flash',\n requiresKey: true,\n },\n ollama: {baseUrl: 'http://localhost:11434/v1', model: 'llama3.2', requiresKey: false},\n}\n\n// Must exactly match validation thresholds in seoUtils.ts\nconst FIELD_CONSTRAINTS: Partial<Record<SeoGenField, {min: number; max: number}>> = {\n title: {min: 50, max: 60},\n description: {min: 120, max: 160},\n ogTitle: {min: 40, max: 60},\n ogDescription: {min: 90, max: 120},\n twitterTitle: {min: 30, max: 70},\n twitterDescription: {min: 50, max: 200},\n}\n\nfunction parseApiError(status: number, body: unknown): string {\n const msg =\n (body as {error?: {message?: string}})?.error?.message ||\n (body as {message?: string})?.message ||\n ''\n if (status === 401)\n return `Authentication failed (401). Check your API key.${msg ? ` ${msg}` : ''}`\n if (status === 429)\n return `Rate limit reached (429). Wait a moment and try again.${msg ? ` ${msg}` : ''}`\n if (status >= 500)\n return `Provider server error (${status}). Try again shortly.${msg ? ` ${msg}` : ''}`\n return msg || `Request failed with status ${status}.`\n}\n\ninterface CallOpts {\n apiKey?: string\n model: string\n temperature: number\n}\n\nasync function callOpenAICompat(\n baseUrl: string,\n opts: CallOpts,\n prompt: string,\n signal: AbortSignal,\n): Promise<string> {\n const headers: Record<string, string> = {'Content-Type': 'application/json'}\n if (opts.apiKey) headers.Authorization = `Bearer ${opts.apiKey}`\n\n const res = await fetch(`${baseUrl}/chat/completions`, {\n method: 'POST',\n headers,\n signal,\n body: JSON.stringify({\n model: opts.model,\n messages: [{role: 'user', content: prompt}],\n /* eslint-disable camelcase */\n max_tokens: 300,\n /* eslint-enable camelcase */\n temperature: opts.temperature,\n }),\n })\n const data = await res.json()\n if (!res.ok) throw new Error(parseApiError(res.status, data))\n return ((data.choices?.[0]?.message?.content as string) || '').trim()\n}\n\nasync function callAnthropic(\n opts: CallOpts,\n apiKey: string,\n prompt: string,\n signal: AbortSignal,\n): Promise<string> {\n const res = await fetch('https://api.anthropic.com/v1/messages', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n 'anthropic-version': '2023-06-01',\n 'anthropic-dangerous-direct-browser-access': 'true',\n },\n signal,\n body: JSON.stringify({\n model: opts.model,\n /* eslint-disable camelcase */\n max_tokens: 300,\n /* eslint-enable camelcase */\n temperature: opts.temperature,\n messages: [{role: 'user', content: prompt}],\n }),\n })\n const data = await res.json()\n if (!res.ok) throw new Error(parseApiError(res.status, data))\n return ((data.content?.[0]?.text as string) || '').trim()\n}\n\ninterface ProxyRequest {\n field: SeoGenField\n content: string\n focusKeyword: string\n keywords: string[]\n meta: MetaContext | undefined\n endpoint: string\n industry: AiIndustry | undefined\n licenseKey: string | undefined\n signal: AbortSignal\n}\n\nasync function callProxy({\n field,\n content,\n focusKeyword,\n keywords,\n meta,\n endpoint,\n industry,\n licenseKey,\n signal,\n}: ProxyRequest): Promise<string> {\n const res = await fetch(endpoint, {\n method: 'POST',\n headers: {'Content-Type': 'application/json'},\n signal,\n body: JSON.stringify({field, content, focusKeyword, keywords, meta, industry, licenseKey}),\n })\n const data = await res.json()\n if (!res.ok) throw new Error(parseApiError(res.status, data))\n const result = (data as {result?: string}).result\n if (!result) throw new Error('Proxy response missing \"result\" field.')\n return result.trim()\n}\n\n// Strip surrounding quotes that models sometimes add despite instructions\nfunction stripQuotes(text: string): string {\n return text.replace(/^[\"'\"\"]|[\"'\"\"]$/g, '').trim()\n}\n\n// Targeted fix: model sees the exact text + what's wrong — far more reliable than blind regeneration\nasync function refineLength(\n field: SeoGenField,\n raw: string,\n constraint: {min: number; max: number},\n callApi: (prompt: string) => Promise<string>,\n): Promise<string> {\n const len = raw.length\n const isTooShort = len < constraint.min\n const action = isTooShort ? 'Expand' : 'Shorten'\n const issue = isTooShort\n ? `too short at ${len} characters (minimum is ${constraint.min})`\n : `too long at ${len} characters (maximum is ${constraint.max})`\n\n const prompt = `This SEO ${field} is ${issue}. Target range: ${constraint.min}–${constraint.max} characters.\n${action} it to fit within that range. Preserve the meaning and any keywords present.\nCount every character carefully before outputting. The final text MUST be ${constraint.min}–${constraint.max} chars.\nCurrent text (${len} chars): \"${raw}\"\nReturn ONLY the rewritten text. No quotes, no labels, no character count.`\n\n return callApi(prompt)\n}\n\n// Targeted fix: insert the missing keyword while keeping length in range\nasync function refineKeyword(\n field: SeoGenField,\n raw: string,\n keywords: string[],\n constraint: {min: number; max: number} | undefined,\n callApi: (prompt: string) => Promise<string>,\n): Promise<string> {\n const kwList = keywords.join(', ')\n const rangeNote = constraint\n ? ` The final text MUST be ${constraint.min}–${constraint.max} characters.`\n : ''\n\n const prompt = `This SEO ${field} is missing a required keyword.\nRewrite it to include at least one of these keywords naturally: ${kwList}.${rangeNote}\nDo not force the keyword awkwardly — weave it in as if it was always there.\nCurrent text: \"${raw}\"\nReturn ONLY the rewritten text. No quotes, no labels, no explanation.`\n\n return callApi(prompt)\n}\n\n// Targeted fix: pick a keyword that actually exists verbatim in the existing title/description\nasync function refineFocusKeyword(\n raw: string,\n meta: MetaContext,\n callApi: (prompt: string) => Promise<string>,\n): Promise<string> {\n const missing: string[] = []\n const kw = raw.toLowerCase().trim()\n if (meta.title && !meta.title.toLowerCase().includes(kw))\n missing.push(`meta title: \"${meta.title}\"`)\n if (meta.description && !meta.description.toLowerCase().includes(kw))\n missing.push(`meta description: \"${meta.description}\"`)\n\n const prompt = `The focus keyword \"${raw}\" does not appear verbatim in the following existing page fields:\n${missing.join('\\n')}\n\nExtract a keyword or phrase (1–4 words) that appears VERBATIM in those fields.\nDo not invent a new keyword — only return a phrase already present in the text above.\n${meta.slug ? `URL slug: \"${meta.slug}\" — prefer a phrase that also matches part of this.` : ''}\n\nReturn ONLY the keyword. No quotes, no explanation, no punctuation. Single line.`\n\n return callApi(prompt)\n}\n\n// Targeted fix: simplify language to improve readability score\nasync function refineReadability(\n raw: string,\n constraint: {min: number; max: number},\n callApi: (prompt: string) => Promise<string>,\n): Promise<string> {\n const prompt = `This SEO meta description uses language that is too complex.\nRewrite it so it reads simply and clearly:\n- Use short sentences (under 15 words each)\n- Use common everyday words — no jargon or passive voice\n- Keep the same core meaning and any keywords present\n- The final text MUST be ${constraint.min}–${constraint.max} characters\nCurrent text: \"${raw}\"\nReturn ONLY the rewritten text. No quotes, no labels, no explanation.`\n\n return callApi(prompt)\n}\n\nfunction checkLength(field: SeoGenField, text: string): boolean {\n const constraint = FIELD_CONSTRAINTS[field]\n if (!constraint) return true\n return text.length >= constraint.min && text.length <= constraint.max\n}\n\nfunction checkKeywords(field: SeoGenField, text: string, keywords: string[]): boolean {\n if ((field === 'title' || field === 'description') && keywords.length > 0) {\n const lower = text.toLowerCase()\n return keywords.some((kw) => lower.includes(kw.toLowerCase().trim()))\n }\n return true\n}\n\nfunction checkReadability(field: SeoGenField, text: string): boolean {\n if (field === 'description') {\n const readability = analyzeReadability(text)\n return !readability || readability.score >= 50\n }\n return true\n}\n\nfunction checkFocusKeywordInMeta(kw: string, meta: MetaContext): boolean {\n const lower = kw.toLowerCase().trim()\n if (meta.title && !meta.title.toLowerCase().includes(lower)) return false\n if (meta.description && !meta.description.toLowerCase().includes(lower)) return false\n return true\n}\n\nfunction validateOutput(\n field: SeoGenField,\n text: string,\n keywords: string[],\n meta?: MetaContext,\n): string | null {\n if (!checkLength(field, text)) return null\n if (!checkKeywords(field, text, keywords)) return null\n if (!checkReadability(field, text)) return null\n if (field === 'focusKeyword' && meta && (meta.title || meta.description)) {\n if (!checkFocusKeywordInMeta(text, meta)) return null\n }\n return text\n}\n\nfunction warnIfKeyExposed(config: AiConfig): void {\n if (config.apiKey && typeof window !== 'undefined') {\n console.warn(\n '[sanity-plugin-seofields] \"apiKey\" is bundled into the Studio\\'s client-side JS and is readable by ' +\n 'anyone with Studio access. Use \"endpoint\" with a server-side proxy instead — see ' +\n '\"sanity-plugin-seofields/server\" for ready-made handlers.',\n )\n }\n}\n\nfunction buildCallApi(\n config: AiConfig,\n temperature: number,\n abortSignal: AbortSignal,\n): (prompt: string) => Promise<string> {\n const provider = config.provider ?? 'openai'\n const providerDef = PROVIDER_DEFAULTS[provider]\n if (!providerDef) throw new Error(`Unknown provider \"${provider}\".`)\n if (providerDef.requiresKey && !config.apiKey) {\n throw new Error(`No API key configured for \"${provider}\". Add apiKey to your ai plugin config.`)\n }\n\n const baseUrl = config.baseUrl ?? providerDef.baseUrl\n const model = config.model ?? providerDef.model\n const opts: CallOpts = {apiKey: config.apiKey, model, temperature}\n\n const useAnthropic = provider === 'anthropic' && !config.baseUrl\n return (prompt: string) =>\n useAnthropic\n ? callAnthropic(opts, config.apiKey!, prompt, abortSignal)\n : callOpenAICompat(baseUrl, opts, prompt, abortSignal)\n}\n\ninterface RefinementCtx {\n field: SeoGenField\n keywords: string[]\n meta: MetaContext | undefined\n constraint: {min: number; max: number} | undefined\n callApi: (prompt: string) => Promise<string>\n abortSignal: AbortSignal\n}\n\n// Runs passes 1-4 against a single raw response, tolerating errors/aborts by keeping the prior text.\nasync function refineRawText(raw: string, ctx: RefinementCtx): Promise<string> {\n const {field, keywords, meta, constraint, callApi, abortSignal} = ctx\n let text = raw\n\n // Pass 1 — fix length if wrong\n if (constraint && !checkLength(field, text)) {\n try {\n abortSignal.throwIfAborted()\n text = stripQuotes(await refineLength(field, text, constraint, callApi))\n } catch {\n // aborted or errored — continue with what we have\n }\n }\n\n // Pass 2 — inject missing keyword if required (title/description fields)\n if (!checkKeywords(field, text, keywords)) {\n try {\n abortSignal.throwIfAborted()\n text = stripQuotes(await refineKeyword(field, text, keywords, constraint, callApi))\n // re-fix length after keyword injection since it may have shifted\n if (constraint && !checkLength(field, text)) {\n abortSignal.throwIfAborted()\n text = stripQuotes(await refineLength(field, text, constraint, callApi))\n }\n } catch {\n // aborted or errored — continue with what we have\n }\n }\n\n // Pass 3 — simplify for readability (description only)\n if (!checkReadability(field, text) && constraint) {\n try {\n abortSignal.throwIfAborted()\n text = stripQuotes(await refineReadability(text, constraint, callApi))\n // re-fix length after rewrite\n if (!checkLength(field, text)) {\n abortSignal.throwIfAborted()\n text = stripQuotes(await refineLength(field, text, constraint, callApi))\n }\n } catch {\n // aborted or errored — continue with what we have\n }\n }\n\n // Pass 4 — ensure focus keyword exists verbatim in existing title + description\n if (\n field === 'focusKeyword' &&\n meta &&\n (meta.title || meta.description) &&\n !checkFocusKeywordInMeta(text, meta)\n ) {\n try {\n abortSignal.throwIfAborted()\n text = stripQuotes(await refineFocusKeyword(text, meta, callApi))\n } catch {\n // aborted or errored — continue with what we have\n }\n }\n\n return text\n}\n\nexport interface GenerateOpts {\n config: AiConfig\n signal?: AbortSignal\n meta?: MetaContext\n}\n\nexport async function generateSeoText(\n field: SeoGenField,\n content: string,\n focusKeyword: string,\n keywords: string[],\n {config, signal, meta}: GenerateOpts,\n): Promise<string> {\n const abortSignal = signal ?? AbortSignal.timeout(15000)\n\n if (config.endpoint) {\n return callProxy({\n field,\n content,\n focusKeyword,\n keywords,\n meta,\n endpoint: config.endpoint,\n industry: config.industry,\n licenseKey: config._licenseKey,\n signal: abortSignal,\n })\n }\n\n warnIfKeyExposed(config)\n const callApi = buildCallApi(config, config.temperature ?? 0.7, abortSignal)\n\n const maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES\n const isLicensed = Boolean(config._licenseKey)\n const constraint = FIELD_CONSTRAINTS[field]\n let firstRaw = ''\n let lastRaw = ''\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n abortSignal.throwIfAborted()\n\n const prompt = await pickPrompt(\n field,\n {content, keyword: focusKeyword, keywords, meta},\n config.industry,\n isLicensed ? config._licenseKey : undefined,\n config._projectId,\n )\n const raw = stripQuotes(await callApi(prompt))\n if (attempt === 0) firstRaw = raw\n\n lastRaw = await refineRawText(raw, {field, keywords, meta, constraint, callApi, abortSignal})\n\n const valid = validateOutput(field, lastRaw, keywords, meta)\n if (valid !== null) return valid\n }\n\n // All attempts exhausted — true = keep first response, false/undefined = keep last\n return config.keepFirstOnValidationFail === true ? firstRaw : lastRaw\n}\n"],"mappings":"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","names":[]}
package/dist/cli.js CHANGED
@@ -1,21 +1,21 @@
1
1
  #!/usr/bin/env node
2
- import {program}from'commander';import l from'picocolors';import x from'fs';import C from'path';import {createClient}from'@sanity/client';import {pathToFileURL}from'url';import*as b from'@clack/prompts';import ct from'ora';var xe=Object.defineProperty;var ee=Object.getOwnPropertySymbols;var ke=Object.prototype.hasOwnProperty,je=Object.prototype.propertyIsEnumerable;var te=(e,t,n)=>t in e?xe(e,t,{enumerable:true,configurable:true,writable:true,value:n}):e[t]=n,N=(e,t)=>{for(var n in t||(t={}))ke.call(t,n)&&te(e,n,t[n]);if(ee)for(var n of ee(t))je.call(t,n)&&te(e,n,t[n]);return e};var a={info:e=>console.log(l.blue("\u2139"),e),success:e=>console.log(l.green("\u2714"),e),warn:e=>console.log(l.yellow("\u26A0"),e),error:e=>console.log(l.red("\u2716"),e),dim:e=>console.log(l.dim(e)),heading:e=>console.log(`
3
- ${l.bold(l.cyan(e))}`),table:e=>{if(e.length===0)return;let t=e[0].map((n,o)=>Math.max(...e.map(s=>(s[o]||"").length)));for(let n of e)console.log(n.map((o,s)=>(o||"").padEnd(t[s]+2)).join(""));}},A={excellent:l.green("\u25CF"),good:l.blue("\u25CF"),fair:l.yellow("\u25CF"),poor:l.red("\u25CF"),missing:l.dim("\u25CB")};({excellent:l.green("Excellent"),good:l.blue("Good"),fair:l.yellow("Fair"),poor:l.red("Poor"),missing:l.dim("Missing")});var Ee=[".env",".env.local",".env.development.local",".env.development"];function Te(e){let t={};for(let n of e.split(`
4
- `)){let o=n.trim();if(!o||o.startsWith("#"))continue;let s=o.indexOf("=");if(s<0)continue;let i=o.slice(0,s).trim(),r=o.slice(s+1).trim();(r.startsWith('"')&&r.endsWith('"')||r.startsWith("'")&&r.endsWith("'"))&&(r=r.slice(1,-1)),t[i]=r;}return t}var oe=false;function L(e=process.cwd()){if(!oe){oe=true;for(let t of Ee){let n=C.join(e,t);if(x.existsSync(n))try{let o=Te(x.readFileSync(n,"utf-8"));for(let[s,i]of Object.entries(o))process.env[s]===void 0&&(process.env[s]=i);}catch(o){}}}}var Re=["seofields.cli.js","seofields.cli.mjs","seofields.cli.cjs","seofields.cli.ts","seofields.cli.tsx"];function W(e,t){let n=e.match(new RegExp(`${t}\\s*:\\s*['"\`]([^'"\`]+)['"\`]`));if(n)return n[1];let o=e.match(new RegExp(`${t}\\s*:\\s*process\\.env\\.([A-Z_a-z0-9]+)`));if(o)return process.env[o[1]]}function De(e){return e.split(`
2
+ import {program}from'commander';import c from'picocolors';import T from'fs';import k from'path';import {createClient}from'@sanity/client';import {pathToFileURL}from'url';import*as w from'@clack/prompts';import bt from'ora';var Ee=Object.defineProperty;var ne=Object.getOwnPropertySymbols;var Re=Object.prototype.hasOwnProperty,Fe=Object.prototype.propertyIsEnumerable;var se=(e,t,o)=>t in e?Ee(e,t,{enumerable:true,configurable:true,writable:true,value:o}):e[t]=o,K=(e,t)=>{for(var o in t||(t={}))Re.call(t,o)&&se(e,o,t[o]);if(ne)for(var o of ne(t))Fe.call(t,o)&&se(e,o,t[o]);return e};var a={info:e=>console.log(c.blue("\u2139"),e),success:e=>console.log(c.green("\u2714"),e),warn:e=>console.log(c.yellow("\u26A0"),e),error:e=>console.log(c.red("\u2716"),e),dim:e=>console.log(c.dim(e)),heading:e=>console.log(`
3
+ ${c.bold(c.cyan(e))}`),table:e=>{if(e.length===0)return;let t=e[0].map((o,n)=>Math.max(...e.map(s=>(s[n]||"").length)));for(let o of e)console.log(o.map((n,s)=>(n||"").padEnd(t[s]+2)).join(""));}},R={excellent:c.green("\u25CF"),good:c.blue("\u25CF"),fair:c.yellow("\u25CF"),poor:c.red("\u25CF"),missing:c.dim("\u25CB")};({excellent:c.green("Excellent"),good:c.blue("Good"),fair:c.yellow("Fair"),poor:c.red("Poor"),missing:c.dim("Missing")});var De=[".env",".env.local",".env.development.local",".env.development"];function _e(e){let t={};for(let o of e.split(`
4
+ `)){let n=o.trim();if(!n||n.startsWith("#"))continue;let s=n.indexOf("=");if(s<0)continue;let i=n.slice(0,s).trim(),r=n.slice(s+1).trim();(r.startsWith('"')&&r.endsWith('"')||r.startsWith("'")&&r.endsWith("'"))&&(r=r.slice(1,-1)),t[i]=r;}return t}var re=false;function H(e=process.cwd()){if(!re){re=true;for(let t of De){let o=k.join(e,t);if(T.existsSync(o))try{let n=_e(T.readFileSync(o,"utf-8"));for(let[s,i]of Object.entries(n))process.env[s]===void 0&&(process.env[s]=i);}catch(n){}}}}var Ke=["seofields.cli.js","seofields.cli.mjs","seofields.cli.cjs","seofields.cli.ts","seofields.cli.tsx"];function J(e,t){let o=e.match(new RegExp(`${t}\\s*:\\s*['"\`]([^'"\`]+)['"\`]`));if(o)return o[1];let n=e.match(new RegExp(`${t}\\s*:\\s*process\\.env\\.([A-Z_a-z0-9]+)`));if(n)return process.env[n[1]]}function Ge(e){return e.split(`
5
5
  `).filter(t=>!/^\s*\/\//.test(t)).join(`
6
- `)}async function Fe(e){var t;try{let o=await import(pathToFileURL(e).href),s=(t=o==null?void 0:o.default)!=null?t:o;return s&&typeof s=="object"?s:null}catch(n){return null}}function Pe(e){try{let t=x.readFileSync(e,"utf-8"),n=De(t),o=W(n,"projectId"),s=W(n,"dataset"),i=W(n,"token"),r=n.match(/types\s*:\s*\[([^\]]+)\]/),d;r&&(d=r[1].split(",").map(f=>f.trim().replace(/['"`]/g,"")).filter(Boolean));let c=n.match(/showConnectionInfo\s*:\s*(true|false)/),u=c?c[1]==="true":void 0;return !o&&!s&&!i&&!d&&u===void 0?null:{projectId:o,dataset:s,token:i,types:d,showConnectionInfo:u}}catch(t){return null}}var R;async function D(e=process.cwd()){if(R!==void 0)return R!=null?R:{};L(e);for(let t of Re){let n=C.join(e,t);if(!x.existsSync(n))continue;let s=t.endsWith(".ts")||t.endsWith(".tsx")?Pe(n):await Fe(n);if(s)return R=s,s}return R=null,{}}var ie=["sanity.config.ts","sanity.config.js","sanity.config.mjs","sanity.config.tsx"];function Me(e){for(let t of ie){let n=C.join(e,t);if(!x.existsSync(n))continue;let o=x.readFileSync(n,"utf-8"),s=o.match(/projectId\s*:\s*['"`]([a-z0-9-]+)['"`]/),i=o.match(/dataset\s*:\s*['"`]([a-z0-9_-]+)['"`]/);return {projectId:s==null?void 0:s[1],dataset:i==null?void 0:i[1]}}return {}}function T(e=process.cwd()){for(let t of ie){let n=C.join(e,t);if(x.existsSync(n))return n}return null}async function Ue(e={},t=process.cwd()){let n=e.projectId,o=e.dataset,s=e.token,i={projectId:"",dataset:"",token:""};n&&(i.projectId="--project-id flag"),o&&(i.dataset="--dataset flag"),s&&(i.token="--token flag");let r=await D(t),c=["seofields.cli.ts","seofields.cli.js","seofields.cli.mjs"].find(m=>{let g=C.join(t,m);return x.existsSync(g)}),u=c||"seofields.cli.*";!n&&r.projectId&&(n=r.projectId,i.projectId=u),!o&&r.dataset&&(o=r.dataset,i.dataset=u),!s&&r.token&&(s=r.token,i.token=u);let f=process.env.SANITY_PROJECT_ID||process.env.SANITY_STUDIO_PROJECT_ID,p=process.env.SANITY_DATASET||process.env.SANITY_STUDIO_DATASET;if(!n&&f){n=f;let m=process.env.SANITY_PROJECT_ID?"SANITY_PROJECT_ID":"SANITY_STUDIO_PROJECT_ID";i.projectId=`.env \u2192 ${m}`;}if(!o&&p){o=p;let m=process.env.SANITY_DATASET?"SANITY_DATASET":"SANITY_STUDIO_DATASET";i.dataset=`.env \u2192 ${m}`;}if(!n||!o){let m=Me(t),g=T(t),y=g?C.basename(g):"sanity.config.*";!n&&m.projectId&&(n=m.projectId,i.projectId=y),!o&&m.dataset&&(o=m.dataset,i.dataset=y);}if(!n)throw new Error(`Could not determine Sanity project ID.
6
+ `)}async function Ue(e){var t;try{let n=await import(pathToFileURL(e).href),s=(t=n==null?void 0:n.default)!=null?t:n;return s&&typeof s=="object"?s:null}catch(o){return null}}function Pe(e){try{let t=T.readFileSync(e,"utf-8"),o=Ge(t),n=J(o,"projectId"),s=J(o,"dataset"),i=J(o,"token"),r=o.match(/types\s*:\s*\[([^\]]+)\]/),f;r&&(f=r[1].split(",").map(d=>d.trim().replace(/['"`]/g,"")).filter(Boolean));let l=o.match(/showConnectionInfo\s*:\s*(true|false)/),u=l?l[1]==="true":void 0;return !n&&!s&&!i&&!f&&u===void 0?null:{projectId:n,dataset:s,token:i,types:f,showConnectionInfo:u}}catch(t){return null}}var F;async function A(e=process.cwd()){if(F!==void 0)return F!=null?F:{};H(e);for(let t of Ke){let o=k.join(e,t);if(!T.existsSync(o))continue;let s=t.endsWith(".ts")||t.endsWith(".tsx")?Pe(o):await Ue(o);if(s)return F=s,s}return F=null,{}}var ce=["sanity.config.ts","sanity.config.js","sanity.config.mjs","sanity.config.tsx"];function He(e){for(let t of ce){let o=k.join(e,t);if(!T.existsSync(o))continue;let n=T.readFileSync(o,"utf-8"),s=n.match(/projectId\s*:\s*['"`]([a-z0-9-]+)['"`]/),i=n.match(/dataset\s*:\s*['"`]([a-z0-9_-]+)['"`]/);return {projectId:s==null?void 0:s[1],dataset:i==null?void 0:i[1]}}return {}}function j(e=process.cwd()){for(let t of ce){let o=k.join(e,t);if(T.existsSync(o))return o}return null}async function Ve(e={},t=process.cwd()){let o=e.projectId,n=e.dataset,s=e.token,i={projectId:"",dataset:"",token:""};o&&(i.projectId="--project-id flag"),n&&(i.dataset="--dataset flag"),s&&(i.token="--token flag");let r=await A(t),l=["seofields.cli.ts","seofields.cli.js","seofields.cli.mjs"].find(m=>{let p=k.join(t,m);return T.existsSync(p)}),u=l||"seofields.cli.*";!o&&r.projectId&&(o=r.projectId,i.projectId=u),!n&&r.dataset&&(n=r.dataset,i.dataset=u),!s&&r.token&&(s=r.token,i.token=u);let d=process.env.SANITY_PROJECT_ID||process.env.SANITY_STUDIO_PROJECT_ID,g=process.env.SANITY_DATASET||process.env.SANITY_STUDIO_DATASET;if(!o&&d){o=d;let m=process.env.SANITY_PROJECT_ID?"SANITY_PROJECT_ID":"SANITY_STUDIO_PROJECT_ID";i.projectId=`.env \u2192 ${m}`;}if(!n&&g){n=g;let m=process.env.SANITY_DATASET?"SANITY_DATASET":"SANITY_STUDIO_DATASET";i.dataset=`.env \u2192 ${m}`;}if(!o||!n){let m=He(t),p=j(t),y=p?k.basename(p):"sanity.config.*";!o&&m.projectId&&(o=m.projectId,i.projectId=y),!n&&m.dataset&&(n=m.dataset,i.dataset=y);}if(!o)throw new Error(`Could not determine Sanity project ID.
7
7
  Options:
8
8
  1. Create a seofields.cli.ts file with defineSeoCli({ projectId: "..." })
9
9
  2. Pass --project-id flag
10
- 3. Set SANITY_PROJECT_ID env var`);o||(o="production",i.dataset="default");let S=process.env.SANITY_TOKEN||process.env.SANITY_AUTH_TOKEN;return !s&&S&&(s=S,i.token=process.env.SANITY_TOKEN?".env \u2192 SANITY_TOKEN":".env \u2192 SANITY_AUTH_TOKEN"),i.token||(i.token="none (unauthenticated)"),{projectId:n,dataset:o,token:s,sources:i}}async function K(e={}){let t=await Ue(e);return {client:createClient(N({projectId:t.projectId,dataset:t.dataset,apiVersion:e.apiVersion||"2024-01-01",useCdn:false},t.token?{token:t.token}:{})),resolved:t}}var He=new Set(["types","defaultHiddenFields"]);function Le(e){let t={};for(let n=0;n<e.length;n++){let o=e[n];if(!o.startsWith("--"))continue;let s=o.slice(2);if(s.includes("=")){let i=s.indexOf("=");t[s.slice(0,i)]=s.slice(i+1);}else n+1<e.length&&!e[n+1].startsWith("--")?t[s]=e[++n]:t[s]="true";}return t}function Ye(e,t){return e==="true"?true:e==="false"?false:/^\d+$/.test(e)?parseInt(e,10):/^\d+\.\d+$/.test(e)?parseFloat(e):He.has(t)&&e.includes(",")?e.split(",").map(n=>n.trim()):e}function Ge(e){let t={};for(let[n,o]of Object.entries(e)){let s=n.split("."),i=t;for(let d=0;d<s.length-1;d++){let c=s[d];(typeof i[c]!="object"||Array.isArray(i[c]))&&(i[c]={}),i=i[c];}let r=s[s.length-1];i[r]=Ye(o,r);}return t}function B(e,t){return typeof e=="string"?`'${e.replace(/\\/g,"\\\\").replace(/'/g,"\\'")}'`:typeof e=="boolean"||typeof e=="number"?String(e):Array.isArray(e)?`[${e.map(n=>B(n,t)).join(", ")}]`:typeof e=="object"&&e!==null?J(e,t):String(e)}function J(e,t){let n=Object.entries(e);if(n.length===0)return "{}";let o=" ".repeat(t),s=" ".repeat(Math.max(0,t-2));return `{
11
- ${n.map(([r,d])=>`${o}${r}: ${B(d,t+2)},`).join(`
10
+ 3. Set SANITY_PROJECT_ID env var`);n||(n="production",i.dataset="default");let b=process.env.SANITY_TOKEN||process.env.SANITY_AUTH_TOKEN;return !s&&b&&(s=b,i.token=process.env.SANITY_TOKEN?".env \u2192 SANITY_TOKEN":".env \u2192 SANITY_AUTH_TOKEN"),i.token||(i.token="none (unauthenticated)"),{projectId:o,dataset:n,token:s,sources:i}}async function Y(e={}){let t=await Ve(e);return {client:createClient(K({projectId:t.projectId,dataset:t.dataset,apiVersion:e.apiVersion||"2024-01-01",useCdn:false},t.token?{token:t.token}:{})),resolved:t}}var We=new Set(["types","defaultHiddenFields"]);function Ye(e){let t={};for(let o=0;o<e.length;o++){let n=e[o];if(!n.startsWith("--"))continue;let s=n.slice(2);if(s.includes("=")){let i=s.indexOf("=");t[s.slice(0,i)]=s.slice(i+1);}else o+1<e.length&&!e[o+1].startsWith("--")?t[s]=e[++o]:t[s]="true";}return t}function Xe(e,t){return e==="true"?true:e==="false"?false:/^\d+$/.test(e)?parseInt(e,10):/^\d+\.\d+$/.test(e)?parseFloat(e):We.has(t)&&e.includes(",")?e.split(",").map(o=>o.trim()):e}function qe(e){let t={};for(let[o,n]of Object.entries(e)){let s=o.split("."),i=t;for(let f=0;f<s.length-1;f++){let l=s[f];(typeof i[l]!="object"||Array.isArray(i[l]))&&(i[l]={}),i=i[l];}let r=s[s.length-1];i[r]=Xe(n,r);}return t}function Q(e,t){return typeof e=="string"?`'${e.replace(/\\/g,"\\\\").replace(/'/g,"\\'")}'`:typeof e=="boolean"||typeof e=="number"?String(e):Array.isArray(e)?`[${e.map(o=>Q(o,t)).join(", ")}]`:typeof e=="object"&&e!==null?z(e,t):String(e)}function z(e,t){let o=Object.entries(e);if(o.length===0)return "{}";let n=" ".repeat(t),s=" ".repeat(Math.max(0,t-2));return `{
11
+ ${o.map(([r,f])=>`${n}${r}: ${Q(f,t+2)},`).join(`
12
12
  `)}
13
- ${s}}`}function z(e,t,n,o){if(e[t]!==n)return null;let s=0,i=t;for(;i<e.length;){let r=e[i];if(r===n)s++;else if(r===o){if(s--,s===0)return {inner:e.slice(t+1,i),outerStart:t,outerEnd:i+1}}else if(r==='"'||r==="'"||r==="`"){let d=r;for(i++;i<e.length;){if(e[i]==="\\"){i+=2;continue}if(e[i]===d)break;i++;}}i++;}return null}function ce(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function q(e,t,n,o){let s=B(n,o+2),i=new RegExp(`((?:^|\\n)([ \\t]*))(${ce(t)})(\\s*:\\s*)([^\\n{[]+?)(,?)(?=\\s*(?:\\n|$|[,}]))`);if(i.test(e))return e.replace(i,(c,u,f,p,S)=>`${u}${p}${S}${s},`);let r=/\n([ \t]+)\w/.exec(e),d=r?r[1]:" ".repeat(o);return `${e.trimEnd()}
14
- ${d}${t}: ${s},
15
- `}function le(e,t,n=2){let o=e;for(let[s,i]of Object.entries(t)){if(!(typeof i=="object"&&i!==null&&!Array.isArray(i))){o=q(o,s,i,n);continue}let c=new RegExp(`\\b${ce(s)}\\s*:`).exec(o);if(!c){o=q(o,s,i,n);continue}let u=c.index+c[0].length,f=o.slice(u).search(/\S/),p=u+(f>=0?f:0);if(o[p]!=="{"){o=q(o,s,i,n);continue}let S=z(o,p,"{","}");if(!S)continue;let m=le(S.inner,i,n+2),g=o.slice(0,c.index).match(/\n([ \t]*)$/),y=g?g[1]:" ".repeat(n);o=o.slice(0,S.outerStart+1)+m+y+o.slice(S.outerEnd-1);}return o}function Ke(e){a.info("Applying config:");for(let[t,n]of Object.entries(e))a.dim(` ${l.cyan(t)}: ${l.yellow(String(n))}`);}function Ve(e){let t=/seofields\s*\(/.exec(e);(!t||t.index===void 0)&&(a.error("`seofields()` not found in config. Run `seofields init` first."),process.exit(1));let n=t.index+t[0].length-1,o=z(e,n,"(",")");return o||(a.error("Could not parse the seofields() call \u2014 please check your config file syntax."),process.exit(1)),{callIndex:t.index,parenBlock:o}}function We(e,t,n,o){let s=n.inner.trim();if(!s){let i=e.lastIndexOf(`
16
- `,t)+1,r=t-i,d=r+2;return a.dim(" (Injecting fresh config into empty seofields())"),`
17
- ${" ".repeat(d)}${J(o,d+2)},
18
- ${" ".repeat(r)}`}if(s.startsWith("{")){let i=n.inner.indexOf("{"),r=z(n.inner,i,"{","}");r||(a.error("Could not parse the existing config object \u2014 check syntax."),process.exit(1));let d=le(r.inner,o,2),c=n.inner.slice(0,r.outerStart),u=n.inner.slice(r.outerEnd);return a.dim(" (Merging into existing config object)"),`${c}{${d}}${u}`}return a.warn(`Config uses a variable reference (\`${s.slice(0,30)}\u2026\`) \u2014 cannot auto-update.`),a.dim(" Add the following manually to your config object:"),a.dim(J(o,2)),null}async function de(e){if(a.heading("seofields config"),e.length===0){ae();return}let t=Le(e);Object.keys(t).length===0&&(a.error("No valid --key=value options found."),ae(),process.exit(1));let n=Ge(t);Ke(t);let o=T(process.cwd());o||(a.error("No sanity.config.ts/js found. Run from your Sanity project root."),process.exit(1)),a.info(`Updating ${o}`);let s=x.readFileSync(o,"utf-8"),{callIndex:i,parenBlock:r}=Ve(s),d=We(s,i,r,n);if(d===null)return;let c=s.slice(0,r.outerStart+1)+d+s.slice(r.outerEnd-1);x.writeFileSync(o,c,"utf-8"),a.success(`Config updated in ${o}`);}function ae(){console.log(""),console.log(` ${l.bold("Usage:")} seofields config [--option=value ...]`),console.log(""),console.log(` ${l.bold("Top-level options:")}`),console.log(` ${l.cyan("--baseUrl")}=<url> Base URL of your site`),console.log(` ${l.cyan("--seoPreview")}=true|false Enable SERP preview in Studio`),console.log(` ${l.cyan("--defaultHiddenFields")}=field1,field2 Fields hidden by default`),console.log(""),console.log(` ${l.bold("Health Dashboard options:")}`),console.log(` ${l.cyan("--healthDashboard")}=true|false Enable/disable dashboard`),console.log(` ${l.cyan("--healthDashboard.licenseKey")}=SEOF-XXXX-\u2026 License key`),console.log(` ${l.cyan("--healthDashboard.toolTitle")}=<title> Studio nav tab label`),console.log(` ${l.cyan("--healthDashboard.showTypeColumn")}=true|false`),console.log(` ${l.cyan("--healthDashboard.showDocumentId")}=true|false`),console.log(` ${l.cyan("--healthDashboard.previewMode")}=true|false Show demo data`),console.log(` ${l.cyan("--healthDashboard.apiVersion")}=2024-01-01`),console.log(` ${l.cyan("--healthDashboard.typeColumnMode")}=badge|text`),console.log(` ${l.cyan("--healthDashboard.structureTool")}=<name>`),console.log(""),console.log(` ${l.bold("Query options:")}`),console.log(` ${l.cyan("--healthDashboard.query.types")}=post,page Limit to doc types`),console.log(` ${l.cyan("--healthDashboard.query.requireSeo")}=true|false`),console.log(` ${l.cyan("--healthDashboard.query.groq")}=<query> Custom GROQ query`),console.log(""),console.log(` ${l.bold("Examples:")}`),console.log(l.dim(" seofields config --baseUrl=https://example.com")),console.log(l.dim(" seofields config --healthDashboard.licenseKey=SEOF-777F-4C32-B154 --healthDashboard.showDocumentId=false")),console.log(l.dim(" seofields config --healthDashboard.query.types=post,page --seoPreview=true")),console.log("");}var qe=(e,t)=>`import { defineSeoCli } from 'sanity-plugin-seofields/define-cli'
13
+ ${s}}`}function Z(e,t,o,n){if(e[t]!==o)return null;let s=0,i=t;for(;i<e.length;){let r=e[i];if(r===o)s++;else if(r===n){if(s--,s===0)return {inner:e.slice(t+1,i),outerStart:t,outerEnd:i+1}}else if(r==='"'||r==="'"||r==="`"){let f=r;for(i++;i<e.length;){if(e[i]==="\\"){i+=2;continue}if(e[i]===f)break;i++;}}i++;}return null}function fe(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function B(e,t,o,n){let s=Q(o,n+2),i=new RegExp(`((?:^|\\n)([ \\t]*))(${fe(t)})(\\s*:\\s*)([^\\n{[]+?)(,?)(?=\\s*(?:\\n|$|[,}]))`);if(i.test(e))return e.replace(i,(l,u,d,g,b)=>`${u}${g}${b}${s},`);let r=/\n([ \t]+)\w/.exec(e),f=r?r[1]:" ".repeat(n);return `${e.trimEnd()}
14
+ ${f}${t}: ${s},
15
+ `}function ue(e,t,o=2){let n=e;for(let[s,i]of Object.entries(t)){if(!(typeof i=="object"&&i!==null&&!Array.isArray(i))){n=B(n,s,i,o);continue}let l=new RegExp(`\\b${fe(s)}\\s*:`).exec(n);if(!l){n=B(n,s,i,o);continue}let u=l.index+l[0].length,d=n.slice(u).search(/\S/),g=u+(d>=0?d:0);if(n[g]!=="{"){n=B(n,s,i,o);continue}let b=Z(n,g,"{","}");if(!b)continue;let m=ue(b.inner,i,o+2),p=n.slice(0,l.index).match(/\n([ \t]*)$/),y=p?p[1]:" ".repeat(o);n=n.slice(0,b.outerStart+1)+m+y+n.slice(b.outerEnd-1);}return n}function Je(e){a.info("Applying config:");for(let[t,o]of Object.entries(e))a.dim(` ${c.cyan(t)}: ${c.yellow(String(o))}`);}function Be(e){let t=/seofields\s*\(/.exec(e);(!t||t.index===void 0)&&(a.error("`seofields()` not found in config. Run `seofields init` first."),process.exit(1));let o=t.index+t[0].length-1,n=Z(e,o,"(",")");return n||(a.error("Could not parse the seofields() call \u2014 please check your config file syntax."),process.exit(1)),{callIndex:t.index,parenBlock:n}}function ze(e,t,o,n){let s=o.inner.trim();if(!s){let i=e.lastIndexOf(`
16
+ `,t)+1,r=t-i,f=r+2;return a.dim(" (Injecting fresh config into empty seofields())"),`
17
+ ${" ".repeat(f)}${z(n,f+2)},
18
+ ${" ".repeat(r)}`}if(s.startsWith("{")){let i=o.inner.indexOf("{"),r=Z(o.inner,i,"{","}");r||(a.error("Could not parse the existing config object \u2014 check syntax."),process.exit(1));let f=ue(r.inner,n,2),l=o.inner.slice(0,r.outerStart),u=o.inner.slice(r.outerEnd);return a.dim(" (Merging into existing config object)"),`${l}{${f}}${u}`}return a.warn(`Config uses a variable reference (\`${s.slice(0,30)}\u2026\`) \u2014 cannot auto-update.`),a.dim(" Add the following manually to your config object:"),a.dim(z(n,2)),null}async function ge(e){if(a.heading("seofields config"),e.length===0){de();return}let t=Ye(e);Object.keys(t).length===0&&(a.error("No valid --key=value options found."),de(),process.exit(1));let o=qe(t);Je(t);let n=j(process.cwd());n||(a.error("No sanity.config.ts/js found. Run from your Sanity project root."),process.exit(1)),a.info(`Updating ${n}`);let s=T.readFileSync(n,"utf-8"),{callIndex:i,parenBlock:r}=Be(s),f=ze(s,i,r,o);if(f===null)return;let l=s.slice(0,r.outerStart+1)+f+s.slice(r.outerEnd-1);T.writeFileSync(n,l,"utf-8"),a.success(`Config updated in ${n}`);}function de(){console.log(""),console.log(` ${c.bold("Usage:")} seofields config [--option=value ...]`),console.log(""),console.log(` ${c.bold("Top-level options:")}`),console.log(` ${c.cyan("--baseUrl")}=<url> Base URL of your site`),console.log(` ${c.cyan("--seoPreview")}=true|false Enable SERP preview in Studio`),console.log(` ${c.cyan("--defaultHiddenFields")}=field1,field2 Fields hidden by default`),console.log(""),console.log(` ${c.bold("Health Dashboard options:")}`),console.log(` ${c.cyan("--healthDashboard")}=true|false Enable/disable dashboard`),console.log(` ${c.cyan("--healthDashboard.licenseKey")}=SEOF-XXXX-\u2026 License key`),console.log(` ${c.cyan("--healthDashboard.toolTitle")}=<title> Studio nav tab label`),console.log(` ${c.cyan("--healthDashboard.showTypeColumn")}=true|false`),console.log(` ${c.cyan("--healthDashboard.showDocumentId")}=true|false`),console.log(` ${c.cyan("--healthDashboard.previewMode")}=true|false Show demo data`),console.log(` ${c.cyan("--healthDashboard.apiVersion")}=2024-01-01`),console.log(` ${c.cyan("--healthDashboard.typeColumnMode")}=badge|text`),console.log(` ${c.cyan("--healthDashboard.structureTool")}=<name>`),console.log(""),console.log(` ${c.bold("Query options:")}`),console.log(` ${c.cyan("--healthDashboard.query.types")}=post,page Limit to doc types`),console.log(` ${c.cyan("--healthDashboard.query.requireSeo")}=true|false`),console.log(` ${c.cyan("--healthDashboard.query.groq")}=<query> Custom GROQ query`),console.log(""),console.log(` ${c.bold("Examples:")}`),console.log(c.dim(" seofields config --baseUrl=https://example.com")),console.log(c.dim(" seofields config --healthDashboard.licenseKey=SEOF-777F-4C32-B154 --healthDashboard.showDocumentId=false")),console.log(c.dim(" seofields config --healthDashboard.query.types=post,page --seoPreview=true")),console.log("");}var Qe=(e,t)=>`import { defineSeoCli } from 'sanity-plugin-seofields/define-cli'
19
19
 
20
20
  export default defineSeoCli({
21
21
  projectId: '${e}',
@@ -25,7 +25,7 @@ export default defineSeoCli({
25
25
  // Document types that have SEO fields \u2014 used by \`report\` and \`export\`
26
26
  // types: ['post', 'page'],
27
27
  })
28
- `,Je=(e,t)=>`import { defineSeoCli } from 'sanity-plugin-seofields/define-cli'
28
+ `,Ze=(e,t)=>`import { defineSeoCli } from 'sanity-plugin-seofields/define-cli'
29
29
 
30
30
  export default defineSeoCli({
31
31
  projectId: '${e}',
@@ -35,33 +35,33 @@ export default defineSeoCli({
35
35
  // Document types that have SEO fields \u2014 used by \`report\` and \`export\`
36
36
  // types: ['post', 'page'],
37
37
  })
38
- `;function Be(e){var n,o;let t=T(e);if(!t)return {};try{let s=x.readFileSync(t,"utf-8"),i=(n=s.match(/projectId\s*:\s*['"`]([a-z0-9-]+)['"`]/))==null?void 0:n[1],r=(o=s.match(/dataset\s*:\s*['"`]([a-z0-9_-]+)['"`]/))==null?void 0:o[1];return {projectId:i,dataset:r}}catch(s){return {}}}async function ue(){var p,S,m,g;let e=process.cwd();b.intro(l.bold("seofields \u203A create-config"));let t=["seofields.cli.ts","seofields.cli.js","seofields.cli.mjs"].find(y=>x.existsSync(C.join(e,y)));if(t){let y=await b.confirm({message:`${l.yellow(t)} already exists. Overwrite?`,initialValue:false});(b.isCancel(y)||!y)&&(b.cancel("Aborted \u2014 existing config left untouched."),process.exit(0));}let n=Be(e),o=await b.select({message:"Which file format?",options:[{value:"ts",label:"TypeScript seofields.cli.ts (recommended)"},{value:"js",label:"JavaScript seofields.cli.js"}],initialValue:"ts"});b.isCancel(o)&&(b.cancel("Cancelled."),process.exit(0));let s=await b.text({message:"Sanity Project ID",placeholder:(p=n.projectId)!=null?p:"abc123xyz",initialValue:(S=n.projectId)!=null?S:"",validate:y=>y!=null&&y.trim()?void 0:"Project ID is required"});b.isCancel(s)&&(b.cancel("Cancelled."),process.exit(0));let i=await b.text({message:"Dataset name",placeholder:(m=n.dataset)!=null?m:"production",initialValue:(g=n.dataset)!=null?g:"production",validate:y=>y!=null&&y.trim()?void 0:"Dataset is required"});b.isCancel(i)&&(b.cancel("Cancelled."),process.exit(0));let r=await b.select({message:"How will you provide the API token?",options:[{value:"env",label:`Environment variable ${l.dim("process.env.SANITY_TOKEN")} (recommended)`},{value:"none",label:"Skip \u2014 add manually later"}],initialValue:"env"});b.isCancel(r)&&(b.cancel("Cancelled."),process.exit(0));let d=`seofields.cli.${o}`,c=C.join(e,d),f=(o==="ts"?qe:Je)(s,i);r==="none"&&(f=f.replace(` token: process.env.SANITY_TOKEN,
39
- `,"")),x.writeFileSync(c,f,"utf-8"),b.outro([l.green(`\u2714 Created ${d}`),"",l.dim(" Next steps:"),` ${l.dim("1.")} Add ${l.white("SANITY_TOKEN")} to your ${l.dim(".env")} file`,` ${l.dim("2.")} Run ${l.white("seofields report")} \u2014 no flags needed!`,` ${l.dim("3.")} Run ${l.white("seofields doctor")} to verify your setup`].join(`
40
- `));}async function ze(e){let t=[],o=["sanity.config.ts","sanity.config.js","sanity.config.mjs","sanity.config.tsx"].find(g=>x.existsSync(C.join(e,g)));t.push({name:"Sanity config file",ok:!!o,message:o?`Found ${o}`:`No sanity.config.ts/js found in ${e}`});let s=C.join(e,"package.json"),i=null;if(x.existsSync(s))try{i=JSON.parse(x.readFileSync(s,"utf-8")),t.push({name:"package.json",ok:!0,message:"Found package.json"});}catch(g){return t.push({name:"package.json",ok:false,message:"package.json exists but contains invalid JSON \u2014 fix syntax and retry"}),t}else t.push({name:"package.json",ok:false,message:"No package.json found"});let r=N(N({},i==null?void 0:i.dependencies),i==null?void 0:i.devDependencies),d=!!(r!=null&&r["sanity-plugin-seofields"]),c=r==null?void 0:r["sanity-plugin-seofields"];t.push({name:"Plugin installed",ok:d,message:d?`sanity-plugin-seofields@${c}`:"sanity-plugin-seofields not found in dependencies"});let u=[{name:"sanity",dep:"sanity",minMajor:3},{name:"react",dep:"react",minMajor:18}];for(let g of u){let y=r==null?void 0:r[g.dep],j=!!y;t.push({name:`Peer dep: ${g.name}`,ok:j,message:j?`${g.dep}@${y}`:`${g.dep} not found in dependencies`});}let f=C.join(e,"node_modules","sanity-plugin-seofields"),p=x.existsSync(f);t.push({name:"Plugin in node_modules",ok:p,message:p?"Plugin is installed in node_modules":"Plugin not found in node_modules \u2014 run npm install"}),o&&Xe(e,o,t),Ze(e,o,t);let m=["seofields.cli.ts","seofields.cli.js","seofields.cli.mjs"].find(g=>x.existsSync(C.join(e,g)));return t.push({name:"seofields.cli config",ok:!!m,message:m?`Found ${m} \u2014 project/dataset loaded automatically`:"No seofields.cli.ts found \u2014 create one with defineSeoCli() to skip --project-id flag"}),t}function Xe(e,t,n){let o=x.readFileSync(C.join(e,t),"utf-8"),s=o.includes("seofields(")||o.includes("seofields (");n.push({name:"Plugin registered",ok:s,message:s?"seofields() found in plugins array":"seofields() not found in config \u2014 run `seofields init`"});let i=o.includes("from 'sanity-plugin-seofields'")||o.includes('from "sanity-plugin-seofields"');n.push({name:"Import statement",ok:i,message:i?"import from 'sanity-plugin-seofields' found":"Missing import statement for sanity-plugin-seofields"});let r=Qe(e);n.push({name:"seoFields type usage",ok:r,message:r?"Found type: 'seoFields' in schema definitions":"No schema using type: 'seoFields' found \u2014 add it to a document type"});}function Qe(e){let t=C.join(e,"schemas"),n=C.join(e,"sanity","schemas"),o=C.join(e,"src","sanity","schemas"),s=[t,n,o];for(let i of s){if(!x.existsSync(i))continue;let r=ge(i);for(let d of r){if(!/\.(ts|tsx|js|jsx|mjs)$/.test(d))continue;let c=x.readFileSync(d,"utf-8");if(c.includes("type: 'seoFields'")||c.includes('type: "seoFields"'))return true}}return false}function Ze(e,t,n){let o=x.existsSync(C.join(e,".env"))||x.existsSync(C.join(e,".env.local"))||x.existsSync(C.join(e,".env.development")),s=!!(process.env.SANITY_PROJECT_ID||process.env.SANITY_STUDIO_PROJECT_ID),i=et(s,o,!!t);n.push({name:"Sanity env config",ok:o||s||!!t,message:i});}function et(e,t,n){return e?"SANITY_PROJECT_ID env var detected":t?".env file found":n?"Project config available from sanity.config":"No .env file or SANITY_PROJECT_ID env var found"}function tt(e){a.heading("");let t=e.filter(s=>s.ok).length,n=e.filter(s=>!s.ok).length;for(let s of e){let i=s.ok?l.green("\u2714"):l.red("\u2716"),r=s.ok?s.name:l.red(s.name);a.info(` ${i} ${r}`),a.dim(` ${s.message}`);}a.heading("");let o=n>0?`${l.green(`${t} passed`)}, ${l.red(`${n} failed`)}`:`${l.green(`${t} passed`)}, ${l.dim("0 failed")}`;a.info(` ${l.bold("Result:")} ${o}`);}async function pe(e){a.heading("seofields doctor"),a.heading("");let t=C.resolve(e.cwd),n=await ze(t);tt(n),n.filter(s=>!s.ok).length>0?(a.heading(""),a.dim(" Fix the issues above and run `seofields doctor` again."),process.exit(1)):(a.heading(""),a.success("Everything looks good! \u{1F389}"));}function ge(e){let t=[];try{let n=x.readdirSync(e,{withFileTypes:!0});for(let o of n){let s=C.join(e,o.name);o.name==="node_modules"||o.name===".git"||(o.isDirectory()?t.push(...ge(s)):t.push(s));}}catch(n){}return t}var nt=e=>e>=80?"excellent":e>=60?"good":e>=40?"fair":e>0?"poor":"missing",ot=e=>{let t=[],n=0;return e&&e.length>=50&&e.length<=60?n=15:e&&e.length>0?(n=10,e.length<50&&t.push("Meta title too short (< 50 chars)"),e.length>60&&t.push("Meta title too long (> 60 chars)")):t.push("Missing meta title"),{score:n,issues:t}},st=e=>{let t=[],n=0;return e&&e.length>=120&&e.length<=160?n=15:e&&e.length>0?(n=10,e.length<120&&t.push("Meta description too short (< 120 chars)"),e.length>160&&t.push("Meta description too long (> 160 chars)")):t.push("Missing meta description"),{score:n,issues:t}},it=e=>{let t=[],n=0;return e?(e.title?n+=6:t.push("Missing OG title"),e.description?n+=6:t.push("Missing OG description"),e.image?n+=6:t.push("Missing OG image"),e.type?n+=7:t.push("Missing OG type")):t.push("Open Graph not configured"),{score:n,issues:t}},rt=e=>{let t=[],n=0;return e?(e.title?n+=5:t.push("Missing Twitter title"),e.description?n+=5:t.push("Missing Twitter description"),e.image?n+=5:t.push("Missing Twitter image")):t.push("Twitter Card not configured"),{score:n,issues:t}},M=e=>{if(!e.seo)return {score:0,status:"missing",issues:["SEO fields not configured"]};let t=e.seo,n=t.title,o=t.description,s=t.keywords,i=t.robots,r=t.openGraph,d=t.twitter,c=0,u=[],f=ot(n);c+=f.score,u.push(...f.issues);let p=st(o);c+=p.score,u.push(...p.issues),t.metaImage?c+=10:u.push("Missing meta image"),s&&s.length>0?c+=10:u.push("No keywords defined"),i&&!i.noIndex?c+=5:i||(c+=5);let S=it(r);c+=S.score,u.push(...S.issues);let m=rt(d);c+=m.score,u.push(...m.issues);let g=!!t.metaImage,y=!!(r&&r.image),j=!!(d&&d.image);if(g&&y&&j)c+=5;else {let E=[];g||E.push("meta image"),y||E.push("OG image"),j||E.push("Twitter image"),u.push(`Missing images for full score: ${E.join(", ")}`);}let H=nt(c);return {score:c,status:H,issues:u}};async function me(e){var u;a.heading("seofields export");let t=await D();!e.types&&((u=t.types)!=null&&u.length)&&(e.types=t.types.join(","));let n=ct("Connecting to Sanity...").start(),o;try{({client:o}=await K({projectId:e.projectId,dataset:e.dataset,token:e.token}));}catch(f){n.fail(f.message),process.exit(1);}let i=`*[${e.types?`_type in [${e.types.split(",").map(f=>`"${f.trim()}"`).join(", ")}] && `:""}defined(seo)]{
38
+ `;function et(e){var o,n;let t=j(e);if(!t)return {};try{let s=T.readFileSync(t,"utf-8"),i=(o=s.match(/projectId\s*:\s*['"`]([a-z0-9-]+)['"`]/))==null?void 0:o[1],r=(n=s.match(/dataset\s*:\s*['"`]([a-z0-9_-]+)['"`]/))==null?void 0:n[1];return {projectId:i,dataset:r}}catch(s){return {}}}async function me(){var g,b,m,p;let e=process.cwd();w.intro(c.bold("seofields \u203A create-config"));let t=["seofields.cli.ts","seofields.cli.js","seofields.cli.mjs"].find(y=>T.existsSync(k.join(e,y)));if(t){let y=await w.confirm({message:`${c.yellow(t)} already exists. Overwrite?`,initialValue:false});(w.isCancel(y)||!y)&&(w.cancel("Aborted \u2014 existing config left untouched."),process.exit(0));}let o=et(e),n=await w.select({message:"Which file format?",options:[{value:"ts",label:"TypeScript seofields.cli.ts (recommended)"},{value:"js",label:"JavaScript seofields.cli.js"}],initialValue:"ts"});w.isCancel(n)&&(w.cancel("Cancelled."),process.exit(0));let s=await w.text({message:"Sanity Project ID",placeholder:(g=o.projectId)!=null?g:"abc123xyz",initialValue:(b=o.projectId)!=null?b:"",validate:y=>y!=null&&y.trim()?void 0:"Project ID is required"});w.isCancel(s)&&(w.cancel("Cancelled."),process.exit(0));let i=await w.text({message:"Dataset name",placeholder:(m=o.dataset)!=null?m:"production",initialValue:(p=o.dataset)!=null?p:"production",validate:y=>y!=null&&y.trim()?void 0:"Dataset is required"});w.isCancel(i)&&(w.cancel("Cancelled."),process.exit(0));let r=await w.select({message:"How will you provide the API token?",options:[{value:"env",label:`Environment variable ${c.dim("process.env.SANITY_TOKEN")} (recommended)`},{value:"none",label:"Skip \u2014 add manually later"}],initialValue:"env"});w.isCancel(r)&&(w.cancel("Cancelled."),process.exit(0));let f=`seofields.cli.${n}`,l=k.join(e,f),d=(n==="ts"?Qe:Ze)(s,i);r==="none"&&(d=d.replace(` token: process.env.SANITY_TOKEN,
39
+ `,"")),T.writeFileSync(l,d,"utf-8"),w.outro([c.green(`\u2714 Created ${f}`),"",c.dim(" Next steps:"),` ${c.dim("1.")} Add ${c.white("SANITY_TOKEN")} to your ${c.dim(".env")} file`,` ${c.dim("2.")} Run ${c.white("seofields report")} \u2014 no flags needed!`,` ${c.dim("3.")} Run ${c.white("seofields doctor")} to verify your setup`].join(`
40
+ `));}async function tt(e){let t=[],n=["sanity.config.ts","sanity.config.js","sanity.config.mjs","sanity.config.tsx"].find(p=>T.existsSync(k.join(e,p)));t.push({name:"Sanity config file",ok:!!n,message:n?`Found ${n}`:`No sanity.config.ts/js found in ${e}`});let s=k.join(e,"package.json"),i=null;if(T.existsSync(s))try{i=JSON.parse(T.readFileSync(s,"utf-8")),t.push({name:"package.json",ok:!0,message:"Found package.json"});}catch(p){return t.push({name:"package.json",ok:false,message:"package.json exists but contains invalid JSON \u2014 fix syntax and retry"}),t}else t.push({name:"package.json",ok:false,message:"No package.json found"});let r=K(K({},i==null?void 0:i.dependencies),i==null?void 0:i.devDependencies),f=!!(r!=null&&r["sanity-plugin-seofields"]),l=r==null?void 0:r["sanity-plugin-seofields"];t.push({name:"Plugin installed",ok:f,message:f?`sanity-plugin-seofields@${l}`:"sanity-plugin-seofields not found in dependencies"});let u=[{name:"sanity",dep:"sanity",minMajor:3},{name:"react",dep:"react",minMajor:18}];for(let p of u){let y=r==null?void 0:r[p.dep],O=!!y;t.push({name:`Peer dep: ${p.name}`,ok:O,message:O?`${p.dep}@${y}`:`${p.dep} not found in dependencies`});}let d=k.join(e,"node_modules","sanity-plugin-seofields"),g=T.existsSync(d);t.push({name:"Plugin in node_modules",ok:g,message:g?"Plugin is installed in node_modules":"Plugin not found in node_modules \u2014 run npm install"}),n&&ot(e,n,t),st(e,n,t);let m=["seofields.cli.ts","seofields.cli.js","seofields.cli.mjs"].find(p=>T.existsSync(k.join(e,p)));return t.push({name:"seofields.cli config",ok:!!m,message:m?`Found ${m} \u2014 project/dataset loaded automatically`:"No seofields.cli.ts found \u2014 create one with defineSeoCli() to skip --project-id flag"}),t}function ot(e,t,o){let n=T.readFileSync(k.join(e,t),"utf-8"),s=n.includes("seofields(")||n.includes("seofields (");o.push({name:"Plugin registered",ok:s,message:s?"seofields() found in plugins array":"seofields() not found in config \u2014 run `seofields init`"});let i=n.includes("from 'sanity-plugin-seofields'")||n.includes('from "sanity-plugin-seofields"');o.push({name:"Import statement",ok:i,message:i?"import from 'sanity-plugin-seofields' found":"Missing import statement for sanity-plugin-seofields"});let r=nt(e);o.push({name:"seoFields type usage",ok:r,message:r?"Found type: 'seoFields' in schema definitions":"No schema using type: 'seoFields' found \u2014 add it to a document type"});}function nt(e){let t=k.join(e,"schemas"),o=k.join(e,"sanity","schemas"),n=k.join(e,"src","sanity","schemas"),s=[t,o,n];for(let i of s){if(!T.existsSync(i))continue;let r=ye(i);for(let f of r){if(!/\.(ts|tsx|js|jsx|mjs)$/.test(f))continue;let l=T.readFileSync(f,"utf-8");if(l.includes("type: 'seoFields'")||l.includes('type: "seoFields"'))return true}}return false}function st(e,t,o){let n=T.existsSync(k.join(e,".env"))||T.existsSync(k.join(e,".env.local"))||T.existsSync(k.join(e,".env.development")),s=!!(process.env.SANITY_PROJECT_ID||process.env.SANITY_STUDIO_PROJECT_ID),i=it(s,n,!!t);o.push({name:"Sanity env config",ok:n||s||!!t,message:i});}function it(e,t,o){return e?"SANITY_PROJECT_ID env var detected":t?".env file found":o?"Project config available from sanity.config":"No .env file or SANITY_PROJECT_ID env var found"}function rt(e){a.heading("");let t=e.filter(s=>s.ok).length,o=e.filter(s=>!s.ok).length;for(let s of e){let i=s.ok?c.green("\u2714"):c.red("\u2716"),r=s.ok?s.name:c.red(s.name);a.info(` ${i} ${r}`),a.dim(` ${s.message}`);}a.heading("");let n=o>0?`${c.green(`${t} passed`)}, ${c.red(`${o} failed`)}`:`${c.green(`${t} passed`)}, ${c.dim("0 failed")}`;a.info(` ${c.bold("Result:")} ${n}`);}async function he(e){a.heading("seofields doctor"),a.heading("");let t=k.resolve(e.cwd),o=await tt(t);rt(o),o.filter(s=>!s.ok).length>0?(a.heading(""),a.dim(" Fix the issues above and run `seofields doctor` again."),process.exit(1)):(a.heading(""),a.success("Everything looks good! \u{1F389}"));}function ye(e){let t=[];try{let o=T.readdirSync(e,{withFileTypes:!0});for(let n of o){let s=k.join(e,n.name);n.name==="node_modules"||n.name===".git"||(n.isDirectory()?t.push(...ye(s)):t.push(s));}}catch(o){}return t}var G=(e,t)=>{if(!e||t.length===0)return false;let o=e.toLowerCase();return t.some(n=>n&&o.includes(n.toLowerCase()))},at=e=>e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),X=(e,t,o=3)=>{if(!e||t.length===0)return false;let n=e.toLowerCase();return t.some(s=>{if(!s)return false;let i=n.match(new RegExp(at(s.toLowerCase()),"g"));return i?i.length>o:false})};var ct=(e,t)=>!e||t.length===0?true:e.toLowerCase().startsWith(t[0].toLowerCase());var be=(e,t,o)=>{let n=(e||"").trim();if(!n)return {hasFocusKeyword:false,inTitle:false,atStartOfTitle:false,inDescription:false,isStuffed:false};let s=[n];return {hasFocusKeyword:true,inTitle:G(t||"",s),atStartOfTitle:!!t&&ct(t,s),inDescription:G(o||"",s),isStuffed:X(t||"",s,2)}};var lt=e=>e>=80?"excellent":e>=60?"good":e>=40?"fair":e>0?"poor":"missing",dt=e=>{let t=[],o=0;return e&&e.length>=50&&e.length<=60?o=15:e&&e.length>0?(o=10,e.length<50&&t.push("Meta title too short (< 50 chars)"),e.length>60&&t.push("Meta title too long (> 60 chars)")):t.push("Missing meta title"),{score:o,issues:t}},ft=e=>{let t=[],o=0;return e&&e.length>=120&&e.length<=160?o=15:e&&e.length>0?(o=10,e.length<120&&t.push("Meta description too short (< 120 chars)"),e.length>160&&t.push("Meta description too long (> 160 chars)")):t.push("Missing meta description"),{score:o,issues:t}},ut=e=>{let t=[],o=0;return e?(e.title?o+=6:t.push("Missing OG title"),e.description?o+=6:t.push("Missing OG description"),e.image?o+=6:t.push("Missing OG image"),e.type?o+=7:t.push("Missing OG type")):t.push("Open Graph not configured"),{score:o,issues:t}},gt=e=>{let t=[],o=0;return e?(e.title?o+=5:t.push("Missing Twitter title"),e.description?o+=5:t.push("Missing Twitter description"),e.image?o+=5:t.push("Missing Twitter image")):t.push("Twitter Card not configured"),{score:o,issues:t}},pt=(e,t,o)=>{if(!e||e.length===0)return {score:0,issues:["No keywords defined"],stuffed:false};let n=[],s=2;G(t||"",e)||G(o||"",e)?s+=3:n.push("Keywords defined but not used in title or description");let i=X(t||"",e)||X(o||"",e);return i&&n.push("Keyword stuffing detected \u2014 reduce repeated keyword usage"),{score:s,issues:n,stuffed:i}},mt=(e,t,o)=>{let n=be(e,t,o);if(!n.hasFocusKeyword)return {score:0,issues:["No focus keyword defined"],stuffed:false};let s=[],i=1;return n.atStartOfTitle?i+=4:n.inTitle?(i+=2,s.push("Focus keyword is in the title but not at the start")):n.inDescription?(i+=1,s.push("Focus keyword missing from meta title")):s.push("Focus keyword missing from meta title and description"),n.isStuffed&&s.push("Focus keyword appears too many times in title \u2014 avoid stuffing"),{score:i,issues:s,stuffed:n.isStuffed}},ht=(e,t,o,n)=>{let s=pt(e,o,n),i=mt(t,o,n),r=s.score+i.score;return (s.stuffed||i.stuffed)&&(r=Math.max(0,r-2)),{score:Math.min(r,10),issues:[...s.issues,...i.issues]}},U=e=>{if(!e.seo)return {score:0,status:"missing",issues:["SEO fields not configured"]};let t=e.seo,o=t.title,n=t.description,s=t.keywords,i=t.focusKeyword,r=t.robots,f=t.openGraph,l=t.twitter,u=0,d=[],g=dt(o);u+=g.score,d.push(...g.issues);let b=ft(n);u+=b.score,d.push(...b.issues),t.metaImage?u+=10:d.push("Missing meta image");let m=ht(s,i,o,n);u+=m.score,d.push(...m.issues),r&&!r.noIndex?u+=5:r||(u+=5);let p=ut(f);u+=p.score,d.push(...p.issues);let y=gt(l);u+=y.score,d.push(...y.issues);let O=!!t.metaImage,M=!!(f&&f.image),N=!!(l&&l.image);if(O&&M&&N)u+=5;else {let L=[];O||L.push("meta image"),M||L.push("OG image"),N||L.push("Twitter image"),d.push(`Missing images for full score: ${L.join(", ")}`);}let je=lt(u);return {score:u,status:je,issues:d}};async function Se(e){var u;a.heading("seofields export");let t=await A();!e.types&&((u=t.types)!=null&&u.length)&&(e.types=t.types.join(","));let o=bt("Connecting to Sanity...").start(),n;try{({client:n}=await Y({projectId:e.projectId,dataset:e.dataset,token:e.token}));}catch(d){o.fail(d.message),process.exit(1);}let i=`*[${e.types?`_type in [${e.types.split(",").map(d=>`"${d.trim()}"`).join(", ")}] && `:""}defined(seo)]{
41
41
  _id,
42
42
  _type,
43
43
  _updatedAt,
44
44
  title,
45
45
  slug,
46
46
  seo
47
- } | order(_updatedAt desc)`;n.text="Fetching documents...";let r;try{r=await o.fetch(i);}catch(f){n.fail(`Failed to fetch documents: ${f.message}`),process.exit(1);}if(n.succeed(`Found ${r.length} document(s) with SEO fields`),r.length===0){a.warn("No documents found. Check your project ID, dataset, and API token permissions.");return}let d=r.map(f=>{var S,m,g,y,j,H,E;let p=M(f);return {_id:f._id,_type:f._type,title:f.title||"",slug:((S=f.slug)==null?void 0:S.current)||"",seoScore:p.score,seoStatus:p.status,issues:p.issues,seoTitle:((m=f.seo)==null?void 0:m.title)||"",seoDescription:((g=f.seo)==null?void 0:g.description)||"",hasKeywords:Array.isArray((y=f.seo)==null?void 0:y.keywords)&&f.seo.keywords.length>0,hasOpenGraph:!!((j=f.seo)!=null&&j.openGraph),hasTwitter:!!((H=f.seo)!=null&&H.twitter),hasMetaImage:!!((E=f.seo)!=null&&E.metaImage),_updatedAt:f._updatedAt||""}}),c;if(e.format==="csv"){let f=["_id","_type","title","slug","seoScore","seoStatus","issues","seoTitle","seoDescription","hasKeywords","hasOpenGraph","hasTwitter","hasMetaImage","_updatedAt"],p=[f.join(",")];for(let S of d)p.push(f.map(m=>{let g=S[m];return Array.isArray(g)?`"${g.join("; ").replace(/"/g,'""')}"`:typeof g=="string"?`"${g.replace(/"/g,'""')}"`:String(g)}).join(","));c=p.join(`
48
- `);}else c=JSON.stringify(d,null,2);e.output?(x.writeFileSync(e.output,c,"utf-8"),a.success(`Exported to ${e.output}`)):console.log(c);}function lt(){process.argv.includes("--dashboard")&&process.argv.includes("--no-dashboard")&&(a.error("Conflicting flags: --dashboard and --no-dashboard cannot be used together."),process.exit(1));}function dt(e){let t=[];return e.preview&&t.push("seoPreview: true"),e.dashboard===true&&t.push("healthDashboard: true"),e.dashboard===false&&t.push("healthDashboard: false"),t.length>0?`seofields({ ${t.join(", ")} })`:"seofields()"}function he(e,t,n,o){let s=[t?"":"import seofields from 'sanity-plugin-seofields'",o&&!n?"import {schemaOrg} from 'sanity-plugin-seofields/schema'":""].filter(Boolean).join(`
47
+ } | order(_updatedAt desc)`;o.text="Fetching documents...";let r;try{r=await n.fetch(i);}catch(d){o.fail(`Failed to fetch documents: ${d.message}`),process.exit(1);}if(o.succeed(`Found ${r.length} document(s) with SEO fields`),r.length===0){a.warn("No documents found. Check your project ID, dataset, and API token permissions.");return}let f=r.map(d=>{var b,m,p,y,O,M,N;let g=U(d);return {_id:d._id,_type:d._type,title:d.title||"",slug:((b=d.slug)==null?void 0:b.current)||"",seoScore:g.score,seoStatus:g.status,issues:g.issues,seoTitle:((m=d.seo)==null?void 0:m.title)||"",seoDescription:((p=d.seo)==null?void 0:p.description)||"",hasKeywords:Array.isArray((y=d.seo)==null?void 0:y.keywords)&&d.seo.keywords.length>0,hasOpenGraph:!!((O=d.seo)!=null&&O.openGraph),hasTwitter:!!((M=d.seo)!=null&&M.twitter),hasMetaImage:!!((N=d.seo)!=null&&N.metaImage),_updatedAt:d._updatedAt||""}}),l;if(e.format==="csv"){let d=["_id","_type","title","slug","seoScore","seoStatus","issues","seoTitle","seoDescription","hasKeywords","hasOpenGraph","hasTwitter","hasMetaImage","_updatedAt"],g=[d.join(",")];for(let b of f)g.push(d.map(m=>{let p=b[m];return Array.isArray(p)?`"${p.join("; ").replace(/"/g,'""')}"`:typeof p=="string"?`"${p.replace(/"/g,'""')}"`:String(p)}).join(","));l=g.join(`
48
+ `);}else l=JSON.stringify(f,null,2);e.output?(T.writeFileSync(e.output,l,"utf-8"),a.success(`Exported to ${e.output}`)):console.log(l);}function St(){process.argv.includes("--dashboard")&&process.argv.includes("--no-dashboard")&&(a.error("Conflicting flags: --dashboard and --no-dashboard cannot be used together."),process.exit(1));}function wt(e){let t=[];return e.preview&&t.push("seoPreview: true"),e.dashboard===true&&t.push("healthDashboard: true"),e.dashboard===false&&t.push("healthDashboard: false"),t.length>0?`seofields({ ${t.join(", ")} })`:"seofields()"}function we(e,t,o,n){let s=[t?"":"import seofields from 'sanity-plugin-seofields'",n&&!o?"import {schemaOrg} from 'sanity-plugin-seofields/schema'":""].filter(Boolean).join(`
49
49
  `);if(!s)return e;let i=[...e.matchAll(/^import\s.+$/gm)].at(-1),r=i?i.index+i[0].length:0;return r>0?`${e.slice(0,r)}
50
50
  ${s}${e.slice(r)}`:`${s}
51
51
 
52
- ${e}`}function ft(e,t,n,o){a.success(`Updated ${e}`),a.dim(""),a.dim(" What was added:"),a.dim(" import seofields from 'sanity-plugin-seofields'"),o&&a.dim(" import {schemaOrg} from 'sanity-plugin-seofields/schema'"),a.dim(` plugins: [${t}${n}, ...]`),a.dim(""),a.dim(" Next steps:"),a.dim(" 1. Add the seo field to your document schemas:"),a.dim(" defineField({ name: 'seo', title: 'SEO', type: 'seoFields' })"),a.dim(" 2. Restart Sanity Studio");}async function ye(e){a.heading("seofields init"),lt();let t=T(process.cwd());t||(a.error(`No sanity.config.ts/js found in current directory.
53
- Run this command from your Sanity project root.`),process.exit(1)),a.info(`Found config: ${t}`);let n=x.readFileSync(t,"utf-8"),o=/from\s+['"]sanity-plugin-seofields['"]/.test(n),s=/from\s+['"]sanity-plugin-seofields\/schema['"]/.test(n),i=/plugins\s*:\s*\[[\s\S]*?\bseofields\s*\(/.test(n),r=/plugins\s*:\s*\[[\s\S]*?\bschemaOrg\s*\(/.test(n);if(i){let f=e.schemaOrg&&(!s||!r),p=!o;if(!f&&!p){a.warn("seofields() is already registered in your config."),a.dim(" No changes made.");return}if(n=he(n,o,s,!!e.schemaOrg),e.schemaOrg&&!r){let S=/plugins\s*:\s*\[/.exec(n);if(S){let m=S.index+S[0].length;n=`${n.slice(0,m)}
54
- schemaOrg(),${n.slice(m)}`,a.success("Added schemaOrg() to plugins array");}}x.writeFileSync(t,n,"utf-8"),p&&a.success("Added missing import for seofields"),e.schemaOrg&&!s&&a.dim(" Added: import {schemaOrg} from 'sanity-plugin-seofields/schema'"),a.success(`Updated ${t}`);return}let d=dt(e),c=e.schemaOrg&&!r?", schemaOrg()":"";n=he(n,o,s,!!e.schemaOrg);let u=/plugins\s*:\s*\[/.exec(n);if(u){let f=u.index+u[0].length;n=`${n.slice(0,f)}
55
- ${d}${c},${n.slice(f)}`,a.success("Added seofields() to plugins array"),c&&a.success("Added schemaOrg() to plugins array"),e.schemaOrg&&r&&a.dim(" schemaOrg() already in plugins \u2014 import added only");}else a.warn(`Could not find \`plugins: [...]\` in your config.
52
+ ${e}`}function xt(e,t,o,n){a.success(`Updated ${e}`),a.dim(""),a.dim(" What was added:"),a.dim(" import seofields from 'sanity-plugin-seofields'"),n&&a.dim(" import {schemaOrg} from 'sanity-plugin-seofields/schema'"),a.dim(` plugins: [${t}${o}, ...]`),a.dim(""),a.dim(" Next steps:"),a.dim(" 1. Add the seo field to your document schemas:"),a.dim(" defineField({ name: 'seo', title: 'SEO', type: 'seoFields' })"),a.dim(" 2. Restart Sanity Studio");}async function xe(e){a.heading("seofields init"),St();let t=j(process.cwd());t||(a.error(`No sanity.config.ts/js found in current directory.
53
+ Run this command from your Sanity project root.`),process.exit(1)),a.info(`Found config: ${t}`);let o=T.readFileSync(t,"utf-8"),n=/from\s+['"]sanity-plugin-seofields['"]/.test(o),s=/from\s+['"]sanity-plugin-seofields\/schema['"]/.test(o),i=/plugins\s*:\s*\[[\s\S]*?\bseofields\s*\(/.test(o),r=/plugins\s*:\s*\[[\s\S]*?\bschemaOrg\s*\(/.test(o);if(i){let d=e.schemaOrg&&(!s||!r),g=!n;if(!d&&!g){a.warn("seofields() is already registered in your config."),a.dim(" No changes made.");return}if(o=we(o,n,s,!!e.schemaOrg),e.schemaOrg&&!r){let b=/plugins\s*:\s*\[/.exec(o);if(b){let m=b.index+b[0].length;o=`${o.slice(0,m)}
54
+ schemaOrg(),${o.slice(m)}`,a.success("Added schemaOrg() to plugins array");}}T.writeFileSync(t,o,"utf-8"),g&&a.success("Added missing import for seofields"),e.schemaOrg&&!s&&a.dim(" Added: import {schemaOrg} from 'sanity-plugin-seofields/schema'"),a.success(`Updated ${t}`);return}let f=wt(e),l=e.schemaOrg&&!r?", schemaOrg()":"";o=we(o,n,s,!!e.schemaOrg);let u=/plugins\s*:\s*\[/.exec(o);if(u){let d=u.index+u[0].length;o=`${o.slice(0,d)}
55
+ ${f}${l},${o.slice(d)}`,a.success("Added seofields() to plugins array"),l&&a.success("Added schemaOrg() to plugins array"),e.schemaOrg&&r&&a.dim(" schemaOrg() already in plugins \u2014 import added only");}else a.warn(`Could not find \`plugins: [...]\` in your config.
56
56
  Please add manually:
57
- plugins: [${d}${c}]`);x.writeFileSync(t,n,"utf-8"),ft(t,d,c,!!e.schemaOrg);}async function be(e){var f;a.heading("seofields report");let t=await D();!e.types&&((f=t.types)!=null&&f.length)&&(e.types=t.types.join(","));let n=t.showConnectionInfo===true,o=ct("Connecting to Sanity...").start(),s,i;try{({client:s,resolved:i}=await K({projectId:e.projectId,dataset:e.dataset,token:e.token}));}catch(p){o.fail(p.message),process.exit(1);}let d=`*[${e.types?`_type in [${e.types.split(",").map(p=>`"${p.trim()}"`).join(", ")}] && `:""}defined(seo)]{
57
+ plugins: [${f}${l}]`);T.writeFileSync(t,o,"utf-8"),xt(t,f,l,!!e.schemaOrg);}async function ve(e){var d;a.heading("seofields report");let t=await A();!e.types&&((d=t.types)!=null&&d.length)&&(e.types=t.types.join(","));let o=t.showConnectionInfo===true,n=bt("Connecting to Sanity...").start(),s,i;try{({client:s,resolved:i}=await Y({projectId:e.projectId,dataset:e.dataset,token:e.token}));}catch(g){n.fail(g.message),process.exit(1);}let f=`*[${e.types?`_type in [${e.types.split(",").map(g=>`"${g.trim()}"`).join(", ")}] && `:""}defined(seo)]{
58
58
  _id,
59
59
  _type,
60
60
  _updatedAt,
61
61
  title,
62
62
  slug,
63
63
  seo
64
- } | order(_updatedAt desc)`;o.text="Fetching documents...";let c;try{c=await s.fetch(d);}catch(p){o.fail(`Failed to fetch: ${p.message}`),process.exit(1);}if(o.succeed(`Analysed ${c.length} document(s)`),c.length===0){a.warn("No documents with SEO fields found."),n&&Se(i);return}let u=c.map(p=>({doc:p,health:M(p)}));await pt(u,c,e),n&&Se(i);}async function pt(e,t,n){let o={excellent:0,good:0,fair:0,poor:0,missing:0},s=0;for(let{health:c}of e)o[c.status]=(o[c.status]||0)+1,s+=c.score;let i=Math.round(s/e.length);if(gt(t,i,o),n.format==="summary")return;n.format!=="table"&&(a.info(""),a.warn(`Unknown format "${n.format}", defaulting to table output.`)),St(e);let r={};for(let{health:c}of e)for(let u of c.issues)r[u]=(r[u]||0)+1;let d=Object.entries(r).sort(([,c],[,u])=>u-c).slice(0,10);d.length>0&&$t(d,t.length),a.info("");}function gt(e,t,n){a.heading("\u{1F4CA} Summary"),a.info(""),a.info(` Total documents: ${l.bold(String(e.length))}`),a.info(` Average score: ${l.bold(we(t))}%`),a.info(""),a.info(` ${A.excellent} Excellent (80+): ${n.excellent}`),a.info(` ${A.good} Good (60-79): ${n.good}`),a.info(` ${A.fair} Fair (40-59): ${n.fair}`),a.info(` ${A.poor} Poor (1-39): ${n.poor}`),a.info(` ${A.missing} Missing (0): ${n.missing}`);}var mt={excellent:"Excellent",good:"Good",fair:"Fair",poor:"Poor",missing:"Missing"},ht={excellent:l.green,good:l.blue,fair:l.yellow,poor:l.red,missing:l.dim};function yt(e,t=11){var s,i;let n=((s=mt[e])!=null?s:e).padEnd(t);return ((i=ht[e])!=null?i:(r=>r))(n)}function St(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))),n=11,o=14,s=5,i=l.dim("\u2500".repeat(s+2+n+2+o+2+t));a.info(` ${l.dim("Score".padEnd(s))} ${l.dim("Status".padEnd(n))} ${l.dim("Type".padEnd(o))} ${l.dim("Title")}`),a.info(` ${i}`);for(let{doc:r,health:d}of e){let c=$e(r.title||r._id,t),u=String(d.score).padStart(s),f=yt(d.status,n),p=l.dim($e(r._type,o).padEnd(o));a.info(` ${we(d.score,u)} ${f} ${p} ${c}`);}}function $t(e,t){a.heading("\u{1F50D} Top Issues"),a.info("");for(let[n,o]of e){let s=Math.round(o/t*100);a.info(` ${l.red(String(o).padStart(4))} docs (${s}%) ${n}`);}}function Se(e){a.info(""),a.info(` ${l.dim("\u2500".repeat(52))}`),a.info(` ${l.dim("Connection info")}`),a.info(` ${l.dim("Project ID:")} ${l.white(e.projectId)} ${l.dim(`\u2190 ${e.sources.projectId}`)}`),a.info(` ${l.dim("Dataset: ")} ${l.white(e.dataset)} ${l.dim(`\u2190 ${e.sources.dataset}`)}`),a.info(` ${l.dim("Token: ")} ${l.dim(e.sources.token)}`),a.info("");}function we(e,t){let n=t!=null?t:String(e);return e>=80?l.green(n):e>=60?l.blue(n):e>=40?l.yellow(n):e>0?l.red(n):l.dim(n)}function $e(e,t){return e.length>t?`${e.slice(0,t-1)}\u2026`:e}L();var Z="1.8.0",ve=[l.cyan,e=>l.bold(l.blue(e)),l.blue,l.magenta,l.red,l.yellow],w=ve[Math.floor(Math.random()*ve.length)],Ce=["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 bt(){return Ce[Math.floor(Math.random()*Ce.length)]}var wt=62,V=wt-2;function vt(e,t){let n=e.split(" "),o=[],s="";for(let i of n)s.length+(s?1:0)+i.length>t?(s&&o.push(s),s=i):s=s?`${s} ${i}`:i;return s&&o.push(s),o}function F(e,t){let n=V-3,o=" ".repeat(Math.max(0,n-t));return ` ${w("\u2502")} ${e}${o} ${w("\u2502")}`}function U(){return F("",0)}function Ie(){let e="\u2500".repeat(V),t=bt(),n=V-3-5,o=vt(t,n);console.log(),console.log(` ${w(`\u256D${e}\u256E`)}`),console.log(U()),console.log(F(`${l.bold(l.green("seofields"))}${l.dim(` v${Z}`)}`,`seofields v${Z}`.length)),console.log(F(l.dim("SEO tooling for Sanity CMS \u2014 manage from your terminal"),54)),console.log(U());for(let i=0;i<o.length;i++){let r=i===0?`${l.dim("Tip: ")}${l.white(o[i])}`:` ${l.white(o[i])}`,d=5+o[i].length;console.log(F(r,d));}console.log(U()),console.log(` ${w(`\u2570${e}\u256F`)}`),console.log(),console.log(` ${l.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(` ${w(i)} ${l.dim(r)}`);console.log(),console.log(` ${l.dim("Run ")}${l.white("seofields <command> --help")}${l.dim(" for details")}`),console.log();}function P(e,t){let n="\u2500".repeat(V),o=`seofields \u203A ${e}`;return ["",` ${w(`\u256D${n}\u256E`)}`,U(),F(`${l.bold(l.green("seofields"))}${w(" \u203A ")}${l.bold(w(e))}`,o.length),F(l.dim(t),t.length),U(),` ${w(`\u2570${n}\u256F`)}`].join(`
65
- `)}program.name("seofields").description("CLI for sanity-plugin-seofields \u2014 manage SEO from your terminal").version(Z).action(Ie);program.command("config",{isDefault:false}).description("Update seofields() configuration in your sanity.config file").addHelpText("before",P("config","Update seofields() configuration in your sanity.config file")).allowUnknownOption().allowExcessArguments().helpOption("-h, --help","Show help and all available options").addHelpText("after",["",` ${l.bold("Usage")}`,` ${l.dim("Pass any option as")} ${l.white("--key=value")}${l.dim(". Nested keys use dot notation.")}`,"",` ${l.bold("Top-level options")}`,` ${w("--baseUrl")}${l.dim("=<url>").padEnd(38)} ${l.dim("Canonical base URL")}`,` ${w("--seoPreview")}${l.dim("=<bool>").padEnd(36)} ${l.dim("Enable live Google-style preview")}`,` ${w("--defaultHiddenFields")}${l.dim("=<f1,f2>").padEnd(28)} ${l.dim("Comma-separated fields to hide")}`,` ${w("--types")}${l.dim("=<t1,t2>").padEnd(40)} ${l.dim("Document types that have SEO fields")}`,"",` ${l.bold("healthDashboard options")} ${l.dim("(prefix: --healthDashboard.<key>)")}`,` ${w("--healthDashboard.licenseKey")}${l.dim("=<key>").padEnd(22)} ${l.dim("License key for premium features")}`,` ${w("--healthDashboard.toolTitle")}${l.dim("=<str>").padEnd(23)} ${l.dim("Custom title shown in Studio")}`,` ${w("--healthDashboard.showDocumentId")}${l.dim("=<bool>").padEnd(17)} ${l.dim("Show document _id in dashboard")}`,` ${w("--healthDashboard.showHealthScore")}${l.dim("=<bool>").padEnd(16)} ${l.dim("Show score badge per document")}`,` ${w("--healthDashboard.query.types")}${l.dim("=<t1,t2>").padEnd(21)} ${l.dim("Filter dashboard by document types")}`,"",` ${l.bold("Examples")}`,` ${l.white("seofields config --baseUrl=https://mysite.com")}`,` ${l.white("seofields config --healthDashboard.licenseKey=SEOF-1234")}`,` ${l.white("seofields config --healthDashboard.showDocumentId=true --seoPreview=true")}`,` ${l.white("seofields config --healthDashboard.query.types=post,page")}`,` ${l.white("seofields config --defaultHiddenFields=metaImage,openGraphUrl")}`,""].join(`
66
- `)).action(()=>{let e=process.argv.indexOf("config"),t=e>=0?process.argv.slice(e+1):[];de(t);});program.command("create-config").description("Interactively create a seofields.cli.ts / .js config file").addHelpText("before",P("create-config","Interactively create a seofields.cli.ts / .js config file")).action(ue);program.command("init").description("Add seofields() plugin to your sanity.config file").addHelpText("before",P("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",["",` ${l.dim("Note: ")}${l.yellow("--preview")}${l.dim(", ")}${l.yellow("--dashboard")}${l.dim(", and ")}${l.yellow("--no-dashboard")}${l.dim(" only apply during initial setup.")}`,` ${l.dim(" If seofields() is already in your config, use:")}`,"",` ${l.dim(" ")}${l.white("seofields config --healthDashboard=true --seoPreview=true")}`,""].join(`
67
- `)).action(ye);program.command("export").description("Export all documents with SEO fields as JSON or CSV").addHelpText("before",P("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(me);program.command("report").description("Generate an SEO health report for all documents").addHelpText("before",P("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(be);program.command("doctor").description("Check plugin configuration, dependencies, and setup").addHelpText("before",P("doctor","Check plugin configuration, dependencies, and setup")).option("--cwd <path>","Working directory to check",process.cwd()).action(pe);var _=process.argv.slice(2);_.length===1&&(_[0]==="--help"||_[0]==="-h")&&(Ie(),process.exit(0));if(_.length>=2&&_[_.length-1]==="help"){let e=program.commands.find(t=>t.name()===_[0]);e&&(e.help(),process.exit(0));}program.parse();
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(`
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
+ `)).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
+ `)).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();
@@ -1,6 +1,6 @@
1
1
  import { JSX } from 'react';
2
- import { R as SchemaOrgAggregateRatingData, U as SchemaOrgArticleData, V as SchemaOrgBlogPostingData, W as SchemaOrgBookData, X as SchemaOrgBrandData, Y as SchemaOrgBreadcrumbListData, Z as SchemaOrgContactPointData, a0 as SchemaOrgCountryData, a1 as SchemaOrgCourseData, a3 as SchemaOrgEventData, a4 as SchemaOrgFAQPageData, a5 as SchemaOrgHowToData, a7 as SchemaOrgImageObjectData, a8 as SchemaOrgItemListData, a9 as SchemaOrgJobPostingData, aa as SchemaOrgLegalServiceData, ab as SchemaOrgLocalBusinessData, ac as SchemaOrgMovieData, ad as SchemaOrgMusicAlbumData, ae as SchemaOrgMusicRecordingData, af as SchemaOrgNewsArticleData, ag as SchemaOrgOfferData, ah as SchemaOrgOpinionNewsArticleData, ai as SchemaOrgOrganizationData, ak as SchemaOrgPersonData, am as SchemaOrgPlaceData, an as SchemaOrgPostalAddressData, ao as SchemaOrgProductData, ap as SchemaOrgProfilePageData, aq as SchemaOrgRecipeData, ar as SchemaOrgRestaurantData, as as SchemaOrgReviewData, at as SchemaOrgServiceData, au as SchemaOrgSoftwareApplicationData, ax as SchemaOrgVideoObjectData, ay as SchemaOrgWebApplicationData, az as SchemaOrgWebPageData, aA as SchemaOrgWebsiteData } from './types-C19cQx5D.js';
3
- import { b as SanityImage } from './types-yVmQfby9.js';
2
+ import { e as SchemaOrgAggregateRatingData, f as SchemaOrgArticleData, g as SchemaOrgBlogPostingData, h as SchemaOrgBookData, i as SchemaOrgBrandData, j as SchemaOrgBreadcrumbListData, k as SchemaOrgContactPointData, l as SchemaOrgCountryData, m as SchemaOrgCourseData, n as SchemaOrgEventData, o as SchemaOrgFAQPageData, p as SchemaOrgHowToData, q as SchemaOrgImageObjectData, r as SchemaOrgItemListData, s as SchemaOrgJobPostingData, t as SchemaOrgLegalServiceData, u as SchemaOrgLocalBusinessData, v as SchemaOrgMovieData, w as SchemaOrgMusicAlbumData, x as SchemaOrgMusicRecordingData, y as SchemaOrgNewsArticleData, z as SchemaOrgOfferData, A as SchemaOrgOpinionNewsArticleData, B as SchemaOrgOrganizationData, C as SchemaOrgPersonData, D as SchemaOrgPlaceData, E as SchemaOrgPostalAddressData, F as SchemaOrgProductData, G as SchemaOrgProfilePageData, H as SchemaOrgRecipeData, I as SchemaOrgRestaurantData, J as SchemaOrgReviewData, K as SchemaOrgServiceData, L as SchemaOrgSoftwareApplicationData, M as SchemaOrgVideoObjectData, N as SchemaOrgWebApplicationData, O as SchemaOrgWebPageData, P as SchemaOrgWebsiteData } from './types-DRLUGLtX.js';
3
+ import { a as SanityImage } from './types-DxlPXihz.js';
4
4
 
5
5
  interface AggregateRatingSchemaProps {
6
6
  data?: SchemaOrgAggregateRatingData | null;
@@ -1,6 +1,6 @@
1
1
  import { JSX } from 'react';
2
- import { R as SchemaOrgAggregateRatingData, U as SchemaOrgArticleData, V as SchemaOrgBlogPostingData, W as SchemaOrgBookData, X as SchemaOrgBrandData, Y as SchemaOrgBreadcrumbListData, Z as SchemaOrgContactPointData, a0 as SchemaOrgCountryData, a1 as SchemaOrgCourseData, a3 as SchemaOrgEventData, a4 as SchemaOrgFAQPageData, a5 as SchemaOrgHowToData, a7 as SchemaOrgImageObjectData, a8 as SchemaOrgItemListData, a9 as SchemaOrgJobPostingData, aa as SchemaOrgLegalServiceData, ab as SchemaOrgLocalBusinessData, ac as SchemaOrgMovieData, ad as SchemaOrgMusicAlbumData, ae as SchemaOrgMusicRecordingData, af as SchemaOrgNewsArticleData, ag as SchemaOrgOfferData, ah as SchemaOrgOpinionNewsArticleData, ai as SchemaOrgOrganizationData, ak as SchemaOrgPersonData, am as SchemaOrgPlaceData, an as SchemaOrgPostalAddressData, ao as SchemaOrgProductData, ap as SchemaOrgProfilePageData, aq as SchemaOrgRecipeData, ar as SchemaOrgRestaurantData, as as SchemaOrgReviewData, at as SchemaOrgServiceData, au as SchemaOrgSoftwareApplicationData, ax as SchemaOrgVideoObjectData, ay as SchemaOrgWebApplicationData, az as SchemaOrgWebPageData, aA as SchemaOrgWebsiteData } from './types-BXSCbl6p.cjs';
3
- import { b as SanityImage } from './types-yVmQfby9.cjs';
2
+ import { e as SchemaOrgAggregateRatingData, f as SchemaOrgArticleData, g as SchemaOrgBlogPostingData, h as SchemaOrgBookData, i as SchemaOrgBrandData, j as SchemaOrgBreadcrumbListData, k as SchemaOrgContactPointData, l as SchemaOrgCountryData, m as SchemaOrgCourseData, n as SchemaOrgEventData, o as SchemaOrgFAQPageData, p as SchemaOrgHowToData, q as SchemaOrgImageObjectData, r as SchemaOrgItemListData, s as SchemaOrgJobPostingData, t as SchemaOrgLegalServiceData, u as SchemaOrgLocalBusinessData, v as SchemaOrgMovieData, w as SchemaOrgMusicAlbumData, x as SchemaOrgMusicRecordingData, y as SchemaOrgNewsArticleData, z as SchemaOrgOfferData, A as SchemaOrgOpinionNewsArticleData, B as SchemaOrgOrganizationData, C as SchemaOrgPersonData, D as SchemaOrgPlaceData, E as SchemaOrgPostalAddressData, F as SchemaOrgProductData, G as SchemaOrgProfilePageData, H as SchemaOrgRecipeData, I as SchemaOrgRestaurantData, J as SchemaOrgReviewData, K as SchemaOrgServiceData, L as SchemaOrgSoftwareApplicationData, M as SchemaOrgVideoObjectData, N as SchemaOrgWebApplicationData, O as SchemaOrgWebPageData, P as SchemaOrgWebsiteData } from './types-DP-DiW6f.cjs';
3
+ import { a as SanityImage } from './types-DxlPXihz.cjs';
4
4
 
5
5
  interface AggregateRatingSchemaProps {
6
6
  data?: SchemaOrgAggregateRatingData | null;
@@ -1,12 +1,10 @@
1
- 'use strict';
2
-
3
- require('./chunk-254YHUN3.cjs');
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});require('./chunk-A57XNQC7.cjs');
4
2
 
5
3
  // src/define-cli.ts
6
4
  function defineSeoCli(config) {
7
5
  return config;
8
6
  }
9
7
 
8
+
10
9
  exports.defineSeoCli = defineSeoCli;
11
- //# sourceMappingURL=define-cli.cjs.map
12
10
  //# sourceMappingURL=define-cli.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/define-cli.ts"],"names":[],"mappings":";;;;;AAyCO,SAAS,aAAa,MAAA,EAAgD;AAC3E,EAAA,OAAO,MAAA;AACT","file":"define-cli.cjs","sourcesContent":["/**\n * Configuration helper for `seofields` CLI.\n *\n * Create a `seofields.cli.ts` (or `.js`) file in your project root and\n * use `defineSeoCli()` to configure the CLI without passing flags every time.\n *\n * @example\n * ```ts\n * // seofields.cli.ts\n * import { defineSeoCli } from 'sanity-plugin-seofields/define-cli'\n *\n * export default defineSeoCli({\n * projectId: 'my-project-id',\n * dataset: 'production',\n * token: process.env.SANITY_TOKEN,\n * types: ['post', 'page', 'article'],\n * })\n * ```\n */\n\nexport interface SeoFieldsCliConfig {\n /** Sanity project ID */\n projectId?: string\n /** Sanity dataset name (default: \"production\") */\n dataset?: string\n /** Sanity API token (read access at minimum) */\n token?: string\n /** Default document types for `report` and `export` commands */\n types?: string[]\n /**\n * Show connection info (project ID, dataset, token source) at the end of\n * `report` and `export` output. Useful for debugging which credentials\n * are being used. Defaults to `false`.\n */\n showConnectionInfo?: boolean\n}\n\n/**\n * Define CLI configuration for sanity-plugin-seofields commands.\n * Acts as an identity function — provides type safety for the config object.\n */\nexport function defineSeoCli(config: SeoFieldsCliConfig): SeoFieldsCliConfig {\n return config\n}\n"]}
1
+ {"version":3,"sources":["/Users/hardik/GITHUB/sanity-plugin-seofields/npm/dist/define-cli.cjs","../src/define-cli.ts"],"names":[],"mappings":"AAAA,yGAA6B;AAC7B;AACA;ACuCO,SAAS,YAAA,CAAa,MAAA,EAAgD;AAC3E,EAAA,OAAO,MAAA;AACT;ADrCA;AACE;AACF,oCAAC","file":"/Users/hardik/GITHUB/sanity-plugin-seofields/npm/dist/define-cli.cjs","sourcesContent":[null,"/**\n * Configuration helper for `seofields` CLI.\n *\n * Create a `seofields.cli.ts` (or `.js`) file in your project root and\n * use `defineSeoCli()` to configure the CLI without passing flags every time.\n *\n * @example\n * ```ts\n * // seofields.cli.ts\n * import { defineSeoCli } from 'sanity-plugin-seofields/define-cli'\n *\n * export default defineSeoCli({\n * projectId: 'my-project-id',\n * dataset: 'production',\n * token: process.env.SANITY_TOKEN,\n * types: ['post', 'page', 'article'],\n * })\n * ```\n */\n\nexport interface SeoFieldsCliConfig {\n /** Sanity project ID */\n projectId?: string\n /** Sanity dataset name (default: \"production\") */\n dataset?: string\n /** Sanity API token (read access at minimum) */\n token?: string\n /** Default document types for `report` and `export` commands */\n types?: string[]\n /**\n * Show connection info (project ID, dataset, token source) at the end of\n * `report` and `export` output. Useful for debugging which credentials\n * are being used. Defaults to `false`.\n */\n showConnectionInfo?: boolean\n}\n\n/**\n * Define CLI configuration for sanity-plugin-seofields commands.\n * Acts as an identity function — provides type safety for the config object.\n */\nexport function defineSeoCli(config: SeoFieldsCliConfig): SeoFieldsCliConfig {\n return config\n}\n"]}
@@ -1,10 +1,10 @@
1
- import './chunk-DDAAVRWG.js';
1
+ import "./chunk-7N4MLTMR.js";
2
2
 
3
3
  // src/define-cli.ts
4
4
  function defineSeoCli(config) {
5
5
  return config;
6
6
  }
7
-
8
- export { defineSeoCli };
9
- //# sourceMappingURL=define-cli.js.map
7
+ export {
8
+ defineSeoCli
9
+ };
10
10
  //# sourceMappingURL=define-cli.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/define-cli.ts"],"names":[],"mappings":";;;AAyCO,SAAS,aAAa,MAAA,EAAgD;AAC3E,EAAA,OAAO,MAAA;AACT","file":"define-cli.js","sourcesContent":["/**\n * Configuration helper for `seofields` CLI.\n *\n * Create a `seofields.cli.ts` (or `.js`) file in your project root and\n * use `defineSeoCli()` to configure the CLI without passing flags every time.\n *\n * @example\n * ```ts\n * // seofields.cli.ts\n * import { defineSeoCli } from 'sanity-plugin-seofields/define-cli'\n *\n * export default defineSeoCli({\n * projectId: 'my-project-id',\n * dataset: 'production',\n * token: process.env.SANITY_TOKEN,\n * types: ['post', 'page', 'article'],\n * })\n * ```\n */\n\nexport interface SeoFieldsCliConfig {\n /** Sanity project ID */\n projectId?: string\n /** Sanity dataset name (default: \"production\") */\n dataset?: string\n /** Sanity API token (read access at minimum) */\n token?: string\n /** Default document types for `report` and `export` commands */\n types?: string[]\n /**\n * Show connection info (project ID, dataset, token source) at the end of\n * `report` and `export` output. Useful for debugging which credentials\n * are being used. Defaults to `false`.\n */\n showConnectionInfo?: boolean\n}\n\n/**\n * Define CLI configuration for sanity-plugin-seofields commands.\n * Acts as an identity function — provides type safety for the config object.\n */\nexport function defineSeoCli(config: SeoFieldsCliConfig): SeoFieldsCliConfig {\n return config\n}\n"]}
1
+ {"version":3,"sources":["../src/define-cli.ts"],"sourcesContent":["/**\n * Configuration helper for `seofields` CLI.\n *\n * Create a `seofields.cli.ts` (or `.js`) file in your project root and\n * use `defineSeoCli()` to configure the CLI without passing flags every time.\n *\n * @example\n * ```ts\n * // seofields.cli.ts\n * import { defineSeoCli } from 'sanity-plugin-seofields/define-cli'\n *\n * export default defineSeoCli({\n * projectId: 'my-project-id',\n * dataset: 'production',\n * token: process.env.SANITY_TOKEN,\n * types: ['post', 'page', 'article'],\n * })\n * ```\n */\n\nexport interface SeoFieldsCliConfig {\n /** Sanity project ID */\n projectId?: string\n /** Sanity dataset name (default: \"production\") */\n dataset?: string\n /** Sanity API token (read access at minimum) */\n token?: string\n /** Default document types for `report` and `export` commands */\n types?: string[]\n /**\n * Show connection info (project ID, dataset, token source) at the end of\n * `report` and `export` output. Useful for debugging which credentials\n * are being used. Defaults to `false`.\n */\n showConnectionInfo?: boolean\n}\n\n/**\n * Define CLI configuration for sanity-plugin-seofields commands.\n * Acts as an identity function — provides type safety for the config object.\n */\nexport function defineSeoCli(config: SeoFieldsCliConfig): SeoFieldsCliConfig {\n return config\n}\n"],"mappings":";;;AAyCO,SAAS,aAAa,QAAgD;AAC3E,SAAO;AACT;","names":[]}
package/dist/head.cjs ADDED
@@ -0,0 +1,14 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
+
3
+
4
+
5
+
6
+ var _chunkFUUWBQE2cjs = require('./chunk-FUUWBQE2.cjs');
7
+ require('./chunk-A57XNQC7.cjs');
8
+
9
+
10
+
11
+
12
+
13
+ exports.buildSeoHead = _chunkFUUWBQE2cjs.buildSeoHead; exports.buildSeoMeta = _chunkFUUWBQE2cjs.buildSeoMeta; exports.sanitizeOGType = _chunkFUUWBQE2cjs.sanitizeOGType; exports.sanitizeTwitterCard = _chunkFUUWBQE2cjs.sanitizeTwitterCard;
14
+ //# sourceMappingURL=head.cjs.map
@@ -0,0 +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"}