react-ssr-seo-toolkit 1.0.5 → 1.2.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 (82) hide show
  1. package/README.md +633 -460
  2. package/dist/adapters/nextjs.cjs +2 -0
  3. package/dist/adapters/nextjs.cjs.map +1 -0
  4. package/dist/adapters/nextjs.d.cts +86 -0
  5. package/dist/adapters/nextjs.d.ts +86 -0
  6. package/dist/adapters/nextjs.js +2 -0
  7. package/dist/adapters/nextjs.js.map +1 -0
  8. package/dist/adapters/react-router.cjs +2 -0
  9. package/dist/adapters/react-router.cjs.map +1 -0
  10. package/dist/adapters/react-router.d.cts +71 -0
  11. package/dist/adapters/react-router.d.ts +71 -0
  12. package/dist/adapters/react-router.js +2 -0
  13. package/dist/adapters/react-router.js.map +1 -0
  14. package/dist/chunk-3UCLUFOI.js +5 -0
  15. package/dist/chunk-3UCLUFOI.js.map +1 -0
  16. package/dist/chunk-A62THBIS.cjs +3 -0
  17. package/dist/chunk-A62THBIS.cjs.map +1 -0
  18. package/dist/chunk-FKDMHECL.cjs +2 -0
  19. package/dist/chunk-FKDMHECL.cjs.map +1 -0
  20. package/dist/chunk-GNLXGAS6.js +3 -0
  21. package/dist/chunk-GNLXGAS6.js.map +1 -0
  22. package/dist/chunk-LEOORZQY.cjs +5 -0
  23. package/dist/chunk-LEOORZQY.cjs.map +1 -0
  24. package/dist/chunk-LP66SUGR.js +2 -0
  25. package/dist/chunk-LP66SUGR.js.map +1 -0
  26. package/dist/chunk-ROA74LH6.cjs +61 -0
  27. package/dist/chunk-ROA74LH6.cjs.map +1 -0
  28. package/dist/chunk-WM5VVPED.js +61 -0
  29. package/dist/chunk-WM5VVPED.js.map +1 -0
  30. package/dist/chunk-X535MU7Z.js +5 -0
  31. package/dist/chunk-X535MU7Z.js.map +1 -0
  32. package/dist/chunk-ZADUJHVR.cjs +5 -0
  33. package/dist/chunk-ZADUJHVR.cjs.map +1 -0
  34. package/dist/chunk-ZECSTNEE.cjs +3 -0
  35. package/dist/chunk-ZECSTNEE.cjs.map +1 -0
  36. package/dist/chunk-ZVFEGG25.js +3 -0
  37. package/dist/chunk-ZVFEGG25.js.map +1 -0
  38. package/dist/components.cjs +1 -1
  39. package/dist/components.d.cts +2 -2
  40. package/dist/components.d.ts +2 -2
  41. package/dist/components.js +1 -1
  42. package/dist/index-Br7Sh9Ur.d.cts +329 -0
  43. package/dist/index-Br7Sh9Ur.d.ts +329 -0
  44. package/dist/{index-DAGfo2Fc.d.ts → index-CKNcgAj8.d.ts} +10 -2
  45. package/dist/{index-RBSUcdqN.d.cts → index-Wgogf4CX.d.cts} +10 -2
  46. package/dist/index.cjs +1 -1
  47. package/dist/index.d.cts +7 -4
  48. package/dist/index.d.ts +7 -4
  49. package/dist/index.js +1 -1
  50. package/dist/og.cjs +2 -0
  51. package/dist/og.cjs.map +1 -0
  52. package/dist/og.d.cts +5 -0
  53. package/dist/og.d.ts +5 -0
  54. package/dist/og.js +2 -0
  55. package/dist/og.js.map +1 -0
  56. package/dist/schema.cjs +1 -1
  57. package/dist/schema.d.cts +6 -2
  58. package/dist/schema.d.ts +6 -2
  59. package/dist/schema.js +1 -1
  60. package/dist/sitemap.cjs +2 -0
  61. package/dist/sitemap.cjs.map +1 -0
  62. package/dist/sitemap.d.cts +12 -0
  63. package/dist/sitemap.d.ts +12 -0
  64. package/dist/sitemap.js +2 -0
  65. package/dist/sitemap.js.map +1 -0
  66. package/dist/validation.cjs +2 -0
  67. package/dist/validation.cjs.map +1 -0
  68. package/dist/validation.d.cts +8 -0
  69. package/dist/validation.d.ts +8 -0
  70. package/dist/validation.js +2 -0
  71. package/dist/validation.js.map +1 -0
  72. package/package.json +58 -2
  73. package/dist/chunk-63ETSZTD.cjs +0 -3
  74. package/dist/chunk-63ETSZTD.cjs.map +0 -1
  75. package/dist/chunk-ES4OXVOR.js +0 -3
  76. package/dist/chunk-ES4OXVOR.js.map +0 -1
  77. package/dist/chunk-QBHCTDUJ.cjs +0 -2
  78. package/dist/chunk-QBHCTDUJ.cjs.map +0 -1
  79. package/dist/chunk-YMCW2G4X.js +0 -2
  80. package/dist/chunk-YMCW2G4X.js.map +0 -1
  81. package/dist/index-Dr2yktvz.d.cts +0 -136
  82. package/dist/index-Dr2yktvz.d.ts +0 -136
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/index.ts","../src/core/index.ts"],"names":["safeJsonLdSerialize","data","json","_key","value","omitEmpty","obj","result","key","val","deepMerge","base","override","overrideObj","baseObj","overrideVal","baseVal","normalizeUrl","url","trimmed","buildFullUrl","path","normalizedBase","normalizedPath","createSEOConfig","config","normalizeSEOConfig","mergeSEOConfig","merged","normalized","buildTitle","title","template","buildDescription","description","maxLength","buildCanonicalUrl","baseUrl","buildRobotsDirectives","directives","noIndexNoFollow","noIndex","buildOpenGraph","tags","simple","property","image","buildTwitterMetadata","buildAlternateLinks","alternates","alt"],"mappings":"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","file":"chunk-FKDMHECL.cjs","sourcesContent":["/**\n * Safely serialize a value to JSON for use in a <script> tag.\n * Escapes characters that could break out of script context.\n */\nexport function safeJsonLdSerialize(data: unknown): string {\n const json = JSON.stringify(data, (_key, value) => {\n if (value === undefined || value === null) return undefined;\n return value;\n });\n\n if (!json) return \"{}\";\n\n return json\n .replace(/</g, \"\\\\u003c\")\n .replace(/>/g, \"\\\\u003e\")\n .replace(/&/g, \"\\\\u0026\")\n .replace(/\\u2028/g, \"\\\\u2028\")\n .replace(/\\u2029/g, \"\\\\u2029\");\n}\n\n/**\n * Strip undefined/null values from an object (shallow).\n */\nexport function omitEmpty<T extends Record<string, unknown>>(\n obj: T\n): Partial<T> {\n const result: Partial<T> = {};\n for (const key of Object.keys(obj) as Array<keyof T>) {\n const val = obj[key];\n if (val !== undefined && val !== null && val !== \"\") {\n result[key] = val;\n }\n }\n return result;\n}\n\n/**\n * Deep-merge two objects. Arrays are replaced, not concatenated.\n */\nexport function deepMerge<T>(\n base: T,\n override: Partial<T>\n): T {\n const result = { ...base } as Record<string, unknown>;\n const overrideObj = override as Record<string, unknown>;\n const baseObj = base as Record<string, unknown>;\n\n for (const key of Object.keys(overrideObj)) {\n const overrideVal = overrideObj[key];\n const baseVal = baseObj[key];\n\n if (overrideVal === undefined) continue;\n\n if (\n overrideVal !== null &&\n typeof overrideVal === \"object\" &&\n !Array.isArray(overrideVal) &&\n baseVal !== null &&\n typeof baseVal === \"object\" &&\n !Array.isArray(baseVal)\n ) {\n result[key] = deepMerge(\n baseVal as Record<string, unknown>,\n overrideVal as Record<string, unknown>\n );\n } else {\n result[key] = overrideVal;\n }\n }\n\n return result as T;\n}\n\n/**\n * Normalize a URL by trimming whitespace and removing trailing slashes\n * (except for root \"/\").\n */\nexport function normalizeUrl(url: string): string {\n const trimmed = url.trim();\n if (trimmed === \"/\" || trimmed === \"\") return trimmed;\n return trimmed.replace(/\\/+$/, \"\");\n}\n\n/**\n * Build a full canonical URL from a base and a path.\n */\nexport function buildFullUrl(base: string, path?: string): string {\n const normalizedBase = normalizeUrl(base);\n if (!path || path === \"/\") return normalizedBase;\n const normalizedPath = path.startsWith(\"/\") ? path : `/${path}`;\n return normalizedBase + normalizeUrl(normalizedPath);\n}\n","import type {\n SEOConfig,\n OpenGraphConfig,\n TwitterConfig,\n RobotsConfig,\n AlternateLink,\n} from \"../types/index.js\";\nimport { deepMerge, normalizeUrl } from \"../utils/index.js\";\n\n// ─── Config builder ───────────────────────────────────────────\n\n/**\n * Create an SEO config with sensible defaults.\n */\nexport function createSEOConfig(config: SEOConfig = {}): SEOConfig {\n return normalizeSEOConfig(config);\n}\n\n/**\n * Deep-merge a base SEO config with page-level overrides.\n * Useful for combining a global site config with per-page config.\n */\nexport function mergeSEOConfig(\n base: SEOConfig,\n override: SEOConfig\n): SEOConfig {\n const merged = deepMerge(base, override);\n\n // Arrays should be replaced, not deep-merged\n if (override.alternates !== undefined) {\n merged.alternates = override.alternates;\n }\n if (override.additionalMetaTags !== undefined) {\n merged.additionalMetaTags = override.additionalMetaTags;\n }\n if (override.additionalLinkTags !== undefined) {\n merged.additionalLinkTags = override.additionalLinkTags;\n }\n if (override.jsonLd !== undefined) {\n merged.jsonLd = override.jsonLd;\n }\n\n return normalizeSEOConfig(merged);\n}\n\n/**\n * Normalize an SEO config: trim strings, normalize URLs, remove empties.\n */\nexport function normalizeSEOConfig(config: SEOConfig): SEOConfig {\n const normalized: SEOConfig = { ...config };\n\n if (normalized.title) {\n normalized.title = normalized.title.trim();\n }\n if (normalized.description) {\n normalized.description = normalized.description.trim();\n }\n if (normalized.canonical) {\n normalized.canonical = normalizeUrl(normalized.canonical);\n }\n\n return normalized;\n}\n\n// ─── Title ────────────────────────────────────────────────────\n\n/**\n * Build a title string, optionally applying a template.\n * Template uses `%s` as the placeholder for the page title.\n *\n * @example\n * buildTitle(\"About\", \"%s | MySite\") // \"About | MySite\"\n * buildTitle(\"Home\") // \"Home\"\n */\nexport function buildTitle(title?: string, template?: string): string {\n if (!title) return \"\";\n const trimmed = title.trim();\n if (!template) return trimmed;\n return template.replace(/%s/g, trimmed);\n}\n\n/**\n * Build a description, truncating at a max length if specified.\n */\nexport function buildDescription(\n description?: string,\n maxLength?: number\n): string {\n if (!description) return \"\";\n const trimmed = description.trim();\n if (!maxLength || trimmed.length <= maxLength) return trimmed;\n return trimmed.slice(0, maxLength).trimEnd() + \"…\";\n}\n\n// ─── Canonical URL ────────────────────────────────────────────\n\n/**\n * Build a canonical URL from a base URL and optional path.\n */\nexport function buildCanonicalUrl(\n baseUrl: string,\n path?: string\n): string {\n const normalizedBase = normalizeUrl(baseUrl);\n if (!path || path === \"/\") return normalizedBase;\n const normalizedPath = path.startsWith(\"/\") ? path : `/${path}`;\n return normalizedBase + normalizeUrl(normalizedPath);\n}\n\n// ─── Robots ───────────────────────────────────────────────────\n\n/**\n * Build a robots meta content string from a config.\n *\n * @example\n * buildRobotsDirectives({ index: true, follow: true }) // \"index, follow\"\n * buildRobotsDirectives({ index: false, follow: false, noarchive: true })\n * // \"noindex, nofollow, noarchive\"\n */\nexport function buildRobotsDirectives(config?: RobotsConfig): string {\n if (!config) return \"\";\n\n const directives: string[] = [];\n\n if (config.index === false) directives.push(\"noindex\");\n else if (config.index === true) directives.push(\"index\");\n\n if (config.follow === false) directives.push(\"nofollow\");\n else if (config.follow === true) directives.push(\"follow\");\n\n if (config.noarchive) directives.push(\"noarchive\");\n if (config.nosnippet) directives.push(\"nosnippet\");\n if (config.noimageindex) directives.push(\"noimageindex\");\n if (config.notranslate) directives.push(\"notranslate\");\n\n if (config.maxSnippet !== undefined)\n directives.push(`max-snippet:${config.maxSnippet}`);\n if (config.maxImagePreview)\n directives.push(`max-image-preview:${config.maxImagePreview}`);\n if (config.maxVideoPreview !== undefined)\n directives.push(`max-video-preview:${config.maxVideoPreview}`);\n\n return directives.join(\", \");\n}\n\n// ─── Convenience helpers ──────────────────────────────────────\n\n/**\n * Create a noindex/nofollow robots config.\n */\nexport function noIndexNoFollow(): RobotsConfig {\n return { index: false, follow: false };\n}\n\n/**\n * Create a noindex robots config that still allows following links.\n */\nexport function noIndex(): RobotsConfig {\n return { index: false, follow: true };\n}\n\n// ─── Open Graph ───────────────────────────────────────────────\n\n/**\n * Build Open Graph meta tag entries from config.\n * Returns an array of { property, content } pairs.\n */\nexport function buildOpenGraph(\n config?: OpenGraphConfig\n): Array<{ property: string; content: string }> {\n if (!config) return [];\n\n const tags: Array<{ property: string; content: string }> = [];\n\n const simple: Array<[keyof OpenGraphConfig, string]> = [\n [\"title\", \"og:title\"],\n [\"description\", \"og:description\"],\n [\"url\", \"og:url\"],\n [\"siteName\", \"og:site_name\"],\n [\"type\", \"og:type\"],\n [\"locale\", \"og:locale\"],\n ];\n\n for (const [key, property] of simple) {\n const value = config[key];\n if (typeof value === \"string\" && value.trim()) {\n tags.push({ property, content: value.trim() });\n }\n }\n\n if (config.images) {\n for (const image of config.images) {\n if (!image.url) continue;\n tags.push({ property: \"og:image\", content: image.url });\n if (image.alt) tags.push({ property: \"og:image:alt\", content: image.alt });\n if (image.width)\n tags.push({\n property: \"og:image:width\",\n content: String(image.width),\n });\n if (image.height)\n tags.push({\n property: \"og:image:height\",\n content: String(image.height),\n });\n if (image.type)\n tags.push({ property: \"og:image:type\", content: image.type });\n }\n }\n\n return tags;\n}\n\n// ─── Twitter ──────────────────────────────────────────────────\n\n/**\n * Build Twitter card meta tag entries from config.\n * Returns an array of { name, content } pairs.\n */\nexport function buildTwitterMetadata(\n config?: TwitterConfig\n): Array<{ name: string; content: string }> {\n if (!config) return [];\n\n const tags: Array<{ name: string; content: string }> = [];\n\n if (config.card) tags.push({ name: \"twitter:card\", content: config.card });\n if (config.site) tags.push({ name: \"twitter:site\", content: config.site });\n if (config.creator)\n tags.push({ name: \"twitter:creator\", content: config.creator });\n if (config.title)\n tags.push({ name: \"twitter:title\", content: config.title });\n if (config.description)\n tags.push({ name: \"twitter:description\", content: config.description });\n if (config.image)\n tags.push({ name: \"twitter:image\", content: config.image });\n if (config.imageAlt)\n tags.push({ name: \"twitter:image:alt\", content: config.imageAlt });\n\n return tags;\n}\n\n// ─── Hreflang ─────────────────────────────────────────────────\n\n/**\n * Build alternate link entries for hreflang from an array of alternates.\n * Returns an array of { rel, hreflang, href } suitable for <link> tags.\n */\nexport function buildAlternateLinks(\n alternates?: AlternateLink[]\n): Array<{ rel: string; hreflang: string; href: string }> {\n if (!alternates || alternates.length === 0) return [];\n return alternates.map((alt) => ({\n rel: \"alternate\",\n hreflang: alt.hreflang,\n href: normalizeUrl(alt.href),\n }));\n}\n"]}
@@ -0,0 +1,3 @@
1
+ import {i,l,o,p as p$1,q,a}from'./chunk-LP66SUGR.js';import t from'react';function _({title:e,titleTemplate:n,description:o$1,canonical:i$1,robots:c,openGraph:s,twitter:f,alternates:g,additionalMetaTags:h,additionalLinkTags:u,jsonLd:d,nonce:y}){let l$1=[],O=0,m=()=>`seo-${O++}`,x=i(e,n);x&&l$1.push(t.createElement("title",{key:m()},x)),o$1?.trim()&&l$1.push(t.createElement("meta",{key:m(),name:"description",content:o$1.trim()})),i$1?.trim()&&l$1.push(t.createElement("link",{key:m(),rel:"canonical",href:i$1.trim()}));let k=l(c);k&&l$1.push(t.createElement("meta",{key:m(),name:"robots",content:k}));let H=o(s);for(let r of H)l$1.push(t.createElement("meta",{key:m(),property:r.property,content:r.content}));let B=p$1(f);for(let r of B)l$1.push(t.createElement("meta",{key:m(),name:r.name,content:r.content}));let I=q(g);for(let r of I)l$1.push(t.createElement("link",{key:m(),rel:r.rel,hrefLang:r.hreflang,href:r.href}));if(h)for(let r of h){let E={content:r.content};r.name&&(E.name=r.name),r.property&&(E.property=r.property),l$1.push(t.createElement("meta",{key:m(),...E}));}if(u)for(let r of u)l$1.push(t.createElement("link",{key:m(),...r}));if(d){let r=Array.isArray(d)?d:[d];for(let E of r)l$1.push(t.createElement("script",{key:m(),type:"application/ld+json",nonce:y,dangerouslySetInnerHTML:{__html:a(E)}}));}return t.createElement(t.Fragment,null,...l$1)}function $({data:e,nonce:n}){return t.createElement("script",{type:"application/ld+json",nonce:n,dangerouslySetInnerHTML:{__html:a(e)}})}function b(e){if(!e)return "example.com";try{return new URL(e).hostname.replace(/^www\./,"")}catch{return e.replace(/^https?:\/\/(www\.)?/,"").split("/")[0]}}function p(e,n){return e?e.length>n?`${e.slice(0,n-1)}\u2026`:e:""}var v={fontFamily:"system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",boxSizing:"border-box",userSelect:"none"};function R({src:e,height:n,bg:o="#e4e6ea"}){let i={width:"100%",height:n,background:o,display:"flex",alignItems:"center",justifyContent:"center",overflow:"hidden",flexShrink:0};return e?t.createElement("div",{style:i},t.createElement("img",{src:e,alt:"",style:{width:"100%",height:"100%",objectFit:"cover"}})):t.createElement("div",{style:i},t.createElement("span",{style:{color:"#bcc0c9",fontSize:13}},"No image"))}function W({config:e,style:n}){let o=p(e.openGraph?.title??e.title,70),i=p(e.openGraph?.description??e.description,125),c=e.openGraph?.images?.[0]?.url??e.twitter?.image,s=b(e.canonical??e.openGraph?.url),f=e.twitter?.card!=="summary",g={...v,border:"1px solid #cfd9de",borderRadius:16,overflow:"hidden",maxWidth:550,background:"#fff",...n},h={padding:"12px 14px"},u={fontSize:13,color:"#536471",marginBottom:4},d={fontSize:15,fontWeight:700,color:"#0f1419",lineHeight:1.3,marginBottom:4},y={fontSize:15,color:"#536471",lineHeight:1.4};return f?t.createElement("div",{style:g},t.createElement(R,{src:c,height:275,bg:"#e7e9ea"}),t.createElement("div",{style:h},t.createElement("div",{style:u},s),t.createElement("div",{style:d},o),t.createElement("div",{style:y},i))):t.createElement("div",{style:{...g,display:"flex",alignItems:"center"}},t.createElement(R,{src:c,height:110,bg:"#e7e9ea"}),t.createElement("div",{style:{...h,flex:1}},t.createElement("div",{style:u},s),t.createElement("div",{style:d},o),t.createElement("div",{style:y},i)))}function T({config:e,style:n}){let o=p(e.openGraph?.title??e.title,88),i=p(e.openGraph?.description??e.description,110),c=e.openGraph?.images?.[0]?.url,s=b(e.canonical??e.openGraph?.url),f={...v,border:"1px solid #dddfe2",maxWidth:527,background:"#fff",...n};return t.createElement("div",{style:f},t.createElement(R,{src:c,height:275}),t.createElement("div",{style:{background:"#f0f2f5",padding:"10px 12px"}},t.createElement("div",{style:{color:"#606770",fontSize:11,textTransform:"uppercase",marginBottom:4,letterSpacing:"0.5px"}},s),t.createElement("div",{style:{fontWeight:600,fontSize:14,color:"#1c2b33",lineHeight:1.3,marginBottom:2}},o),t.createElement("div",{style:{color:"#606770",fontSize:14,lineHeight:1.4}},i)))}function F({config:e,style:n}){let o=p(e.openGraph?.title??e.title,120),i=e.openGraph?.images?.[0]?.url,c=b(e.canonical??e.openGraph?.url),s={...v,border:"1px solid #e0e0e0",borderRadius:2,maxWidth:552,background:"#fff",...n};return t.createElement("div",{style:s},t.createElement(R,{src:i,height:288,bg:"#dce6f1"}),t.createElement("div",{style:{padding:"8px 12px 12px"}},t.createElement("div",{style:{fontSize:14,fontWeight:600,color:"rgba(0,0,0,0.9)",lineHeight:1.4,marginBottom:4}},o),t.createElement("div",{style:{fontSize:12,color:"rgba(0,0,0,0.6)"}},c)))}function A({config:e,style:n}){let o=p(e.openGraph?.title??e.title,60),i=p(e.openGraph?.description??e.description,160),c=e.openGraph?.siteName??b(e.canonical),s=e.canonical??e.openGraph?.url??"https://example.com";return t.createElement("div",{style:{...v,fontFamily:"arial,sans-serif",maxWidth:600,padding:4,...n}},t.createElement("div",{style:{display:"flex",alignItems:"center",gap:8,marginBottom:4}},t.createElement("div",{style:{width:26,height:26,borderRadius:"50%",background:"#e8eaed",display:"flex",alignItems:"center",justifyContent:"center",fontSize:11,color:"#444",fontWeight:600}},c[0].toUpperCase()),t.createElement("div",null,t.createElement("div",{style:{fontSize:14,color:"#202124",lineHeight:1.3}},c),t.createElement("div",{style:{fontSize:12,color:"#4d5156",lineHeight:1.3}},p(s,60)))),t.createElement("div",{style:{fontSize:20,color:"#1a0dab",lineHeight:1.3,marginBottom:4,cursor:"pointer"}},o),t.createElement("div",{style:{fontSize:14,color:"#4d5156",lineHeight:1.5}},i))}function Q({config:e,platform:n="twitter",style:o}){switch(n){case "facebook":return t.createElement(T,{config:e,style:o});case "linkedin":return t.createElement(F,{config:e,style:o});case "google":return t.createElement(A,{config:e,style:o});default:return t.createElement(W,{config:e,style:o})}}
2
+ export{_ as a,$ as b,Q as c};//# sourceMappingURL=chunk-GNLXGAS6.js.map
3
+ //# sourceMappingURL=chunk-GNLXGAS6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/SEOHead.tsx","../src/components/JsonLd.tsx","../src/components/SEOPreview.tsx"],"names":["SEOHead","title","titleTemplate","description","canonical","robots","openGraph","twitter","alternates","additionalMetaTags","additionalLinkTags","jsonLd","nonce","elements","key","k","resolvedTitle","buildTitle","React","robotsContent","buildRobotsDirectives","ogTags","buildOpenGraph","tag","twitterTags","buildTwitterMetadata","altLinks","buildAlternateLinks","link","meta","props","schemas","schema","safeJsonLdSerialize","JsonLd","data","extractDomain","url","truncate","str","max","BASE","ImagePlaceholder","src","height","bg","style","TwitterCard","config","image","domain","isLarge","card","domainStyle","titleStyle","descStyle","FacebookCard","LinkedInCard","GoogleCard","siteName","SEOPreview","platform"],"mappings":"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","file":"chunk-GNLXGAS6.js","sourcesContent":["import React from \"react\";\nimport type { SEOConfig } from \"../types/index.js\";\nimport {\n buildTitle,\n buildRobotsDirectives,\n buildOpenGraph,\n buildTwitterMetadata,\n buildAlternateLinks,\n} from \"../core/index.js\";\nimport { safeJsonLdSerialize } from \"../utils/index.js\";\n\nexport interface SEOHeadProps extends SEOConfig {\n /**\n * Optional nonce for CSP-compatible script tags.\n */\n nonce?: string;\n}\n\n/**\n * Generic SSR-safe React component that renders SEO-related tags.\n *\n * Renders: title, meta description, canonical link, robots meta,\n * Open Graph tags, Twitter tags, alternate/hreflang links,\n * additional meta/link tags, and JSON-LD scripts.\n *\n * Designed to be placed inside a <head> element in SSR apps.\n * Does NOT use any browser globals — fully SSR-compatible.\n */\nexport function SEOHead({\n title,\n titleTemplate,\n description,\n canonical,\n robots,\n openGraph,\n twitter,\n alternates,\n additionalMetaTags,\n additionalLinkTags,\n jsonLd,\n nonce,\n}: SEOHeadProps): React.ReactElement {\n const elements: React.ReactElement[] = [];\n let key = 0;\n const k = () => `seo-${key++}`;\n\n // Title\n const resolvedTitle = buildTitle(title, titleTemplate);\n if (resolvedTitle) {\n elements.push(React.createElement(\"title\", { key: k() }, resolvedTitle));\n }\n\n // Description\n if (description?.trim()) {\n elements.push(\n React.createElement(\"meta\", {\n key: k(),\n name: \"description\",\n content: description.trim(),\n })\n );\n }\n\n // Canonical\n if (canonical?.trim()) {\n elements.push(\n React.createElement(\"link\", {\n key: k(),\n rel: \"canonical\",\n href: canonical.trim(),\n })\n );\n }\n\n // Robots\n const robotsContent = buildRobotsDirectives(robots);\n if (robotsContent) {\n elements.push(\n React.createElement(\"meta\", {\n key: k(),\n name: \"robots\",\n content: robotsContent,\n })\n );\n }\n\n // Open Graph\n const ogTags = buildOpenGraph(openGraph);\n for (const tag of ogTags) {\n elements.push(\n React.createElement(\"meta\", {\n key: k(),\n property: tag.property,\n content: tag.content,\n })\n );\n }\n\n // Twitter\n const twitterTags = buildTwitterMetadata(twitter);\n for (const tag of twitterTags) {\n elements.push(\n React.createElement(\"meta\", {\n key: k(),\n name: tag.name,\n content: tag.content,\n })\n );\n }\n\n // Hreflang alternates\n const altLinks = buildAlternateLinks(alternates);\n for (const link of altLinks) {\n elements.push(\n React.createElement(\"link\", {\n key: k(),\n rel: link.rel,\n hrefLang: link.hreflang,\n href: link.href,\n })\n );\n }\n\n // Additional meta tags\n if (additionalMetaTags) {\n for (const meta of additionalMetaTags) {\n const props: Record<string, string> = { content: meta.content };\n if (meta.name) props.name = meta.name;\n if (meta.property) props.property = meta.property;\n elements.push(React.createElement(\"meta\", { key: k(), ...props }));\n }\n }\n\n // Additional link tags\n if (additionalLinkTags) {\n for (const link of additionalLinkTags) {\n elements.push(React.createElement(\"link\", { key: k(), ...link }));\n }\n }\n\n // JSON-LD\n if (jsonLd) {\n const schemas = Array.isArray(jsonLd) ? jsonLd : [jsonLd];\n for (const schema of schemas) {\n elements.push(\n React.createElement(\"script\", {\n key: k(),\n type: \"application/ld+json\",\n nonce,\n dangerouslySetInnerHTML: {\n __html: safeJsonLdSerialize(schema),\n },\n })\n );\n }\n }\n\n return React.createElement(React.Fragment, null, ...elements);\n}\n","import React from \"react\";\nimport { safeJsonLdSerialize } from \"../utils/index.js\";\n\nexport interface JsonLdProps {\n data: Record<string, unknown> | Array<Record<string, unknown>>;\n nonce?: string;\n}\n\n/**\n * Renders a <script type=\"application/ld+json\"> tag with safely serialized JSON-LD.\n * SSR-safe: no browser globals used.\n */\nexport function JsonLd({ data, nonce }: JsonLdProps): React.ReactElement {\n return React.createElement(\"script\", {\n type: \"application/ld+json\",\n nonce,\n dangerouslySetInnerHTML: {\n __html: safeJsonLdSerialize(data),\n },\n });\n}\n","import React from \"react\";\nimport type { SEOConfig } from \"../types/index.js\";\n\nexport interface SEOPreviewProps {\n config: SEOConfig;\n platform?: \"twitter\" | \"facebook\" | \"linkedin\" | \"google\";\n className?: string;\n style?: React.CSSProperties;\n}\n\nfunction extractDomain(url?: string): string {\n if (!url) return \"example.com\";\n try {\n return new URL(url).hostname.replace(/^www\\./, \"\");\n } catch {\n return url.replace(/^https?:\\/\\/(www\\.)?/, \"\").split(\"/\")[0];\n }\n}\n\nfunction truncate(str: string | undefined, max: number): string {\n if (!str) return \"\";\n return str.length > max ? `${str.slice(0, max - 1)}…` : str;\n}\n\nconst BASE: React.CSSProperties = {\n fontFamily: \"system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif\",\n boxSizing: \"border-box\",\n userSelect: \"none\",\n};\n\nfunction ImagePlaceholder({\n src,\n height,\n bg = \"#e4e6ea\",\n}: {\n src?: string;\n height: number;\n bg?: string;\n}): React.ReactElement {\n const style: React.CSSProperties = {\n width: \"100%\",\n height,\n background: bg,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n overflow: \"hidden\",\n flexShrink: 0,\n };\n if (src) {\n return React.createElement(\n \"div\",\n { style },\n React.createElement(\"img\", {\n src,\n alt: \"\",\n style: { width: \"100%\", height: \"100%\", objectFit: \"cover\" },\n })\n );\n }\n return React.createElement(\n \"div\",\n { style },\n React.createElement(\n \"span\",\n { style: { color: \"#bcc0c9\", fontSize: 13 } },\n \"No image\"\n )\n );\n}\n\nfunction TwitterCard({\n config,\n style,\n}: {\n config: SEOConfig;\n style?: React.CSSProperties;\n}): React.ReactElement {\n const title = truncate(config.openGraph?.title ?? config.title, 70);\n const description = truncate(\n config.openGraph?.description ?? config.description,\n 125\n );\n const image = config.openGraph?.images?.[0]?.url ?? config.twitter?.image;\n const domain = extractDomain(config.canonical ?? config.openGraph?.url);\n const isLarge = config.twitter?.card !== \"summary\";\n\n const card: React.CSSProperties = {\n ...BASE,\n border: \"1px solid #cfd9de\",\n borderRadius: 16,\n overflow: \"hidden\",\n maxWidth: 550,\n background: \"#fff\",\n ...style,\n };\n\n const meta: React.CSSProperties = {\n padding: \"12px 14px\",\n };\n\n const domainStyle: React.CSSProperties = {\n fontSize: 13,\n color: \"#536471\",\n marginBottom: 4,\n };\n\n const titleStyle: React.CSSProperties = {\n fontSize: 15,\n fontWeight: 700,\n color: \"#0f1419\",\n lineHeight: 1.3,\n marginBottom: 4,\n };\n\n const descStyle: React.CSSProperties = {\n fontSize: 15,\n color: \"#536471\",\n lineHeight: 1.4,\n };\n\n if (!isLarge) {\n return React.createElement(\n \"div\",\n { style: { ...card, display: \"flex\", alignItems: \"center\" } },\n React.createElement(ImagePlaceholder, {\n src: image,\n height: 110,\n bg: \"#e7e9ea\",\n }),\n React.createElement(\n \"div\",\n { style: { ...meta, flex: 1 } },\n React.createElement(\"div\", { style: domainStyle }, domain),\n React.createElement(\"div\", { style: titleStyle }, title),\n React.createElement(\"div\", { style: descStyle }, description)\n )\n );\n }\n\n return React.createElement(\n \"div\",\n { style: card },\n React.createElement(ImagePlaceholder, { src: image, height: 275, bg: \"#e7e9ea\" }),\n React.createElement(\n \"div\",\n { style: meta },\n React.createElement(\"div\", { style: domainStyle }, domain),\n React.createElement(\"div\", { style: titleStyle }, title),\n React.createElement(\"div\", { style: descStyle }, description)\n )\n );\n}\n\nfunction FacebookCard({\n config,\n style,\n}: {\n config: SEOConfig;\n style?: React.CSSProperties;\n}): React.ReactElement {\n const title = truncate(config.openGraph?.title ?? config.title, 88);\n const description = truncate(\n config.openGraph?.description ?? config.description,\n 110\n );\n const image = config.openGraph?.images?.[0]?.url;\n const domain = extractDomain(config.canonical ?? config.openGraph?.url);\n\n const card: React.CSSProperties = {\n ...BASE,\n border: \"1px solid #dddfe2\",\n maxWidth: 527,\n background: \"#fff\",\n ...style,\n };\n\n return React.createElement(\n \"div\",\n { style: card },\n React.createElement(ImagePlaceholder, { src: image, height: 275 }),\n React.createElement(\n \"div\",\n { style: { background: \"#f0f2f5\", padding: \"10px 12px\" } },\n React.createElement(\n \"div\",\n {\n style: {\n color: \"#606770\",\n fontSize: 11,\n textTransform: \"uppercase\" as const,\n marginBottom: 4,\n letterSpacing: \"0.5px\",\n },\n },\n domain\n ),\n React.createElement(\n \"div\",\n {\n style: {\n fontWeight: 600,\n fontSize: 14,\n color: \"#1c2b33\",\n lineHeight: 1.3,\n marginBottom: 2,\n },\n },\n title\n ),\n React.createElement(\n \"div\",\n { style: { color: \"#606770\", fontSize: 14, lineHeight: 1.4 } },\n description\n )\n )\n );\n}\n\nfunction LinkedInCard({\n config,\n style,\n}: {\n config: SEOConfig;\n style?: React.CSSProperties;\n}): React.ReactElement {\n const title = truncate(config.openGraph?.title ?? config.title, 120);\n const image = config.openGraph?.images?.[0]?.url;\n const domain = extractDomain(config.canonical ?? config.openGraph?.url);\n\n const card: React.CSSProperties = {\n ...BASE,\n border: \"1px solid #e0e0e0\",\n borderRadius: 2,\n maxWidth: 552,\n background: \"#fff\",\n ...style,\n };\n\n return React.createElement(\n \"div\",\n { style: card },\n React.createElement(ImagePlaceholder, { src: image, height: 288, bg: \"#dce6f1\" }),\n React.createElement(\n \"div\",\n { style: { padding: \"8px 12px 12px\" } },\n React.createElement(\n \"div\",\n {\n style: {\n fontSize: 14,\n fontWeight: 600,\n color: \"rgba(0,0,0,0.9)\",\n lineHeight: 1.4,\n marginBottom: 4,\n },\n },\n title\n ),\n React.createElement(\n \"div\",\n { style: { fontSize: 12, color: \"rgba(0,0,0,0.6)\" } },\n domain\n )\n )\n );\n}\n\nfunction GoogleCard({\n config,\n style,\n}: {\n config: SEOConfig;\n style?: React.CSSProperties;\n}): React.ReactElement {\n const title = truncate(config.openGraph?.title ?? config.title, 60);\n const description = truncate(\n config.openGraph?.description ?? config.description,\n 160\n );\n const siteName = config.openGraph?.siteName ?? extractDomain(config.canonical);\n const canonical = config.canonical ?? config.openGraph?.url ?? \"https://example.com\";\n\n return React.createElement(\n \"div\",\n {\n style: {\n ...BASE,\n fontFamily: \"arial,sans-serif\",\n maxWidth: 600,\n padding: 4,\n ...style,\n },\n },\n React.createElement(\n \"div\",\n { style: { display: \"flex\", alignItems: \"center\", gap: 8, marginBottom: 4 } },\n React.createElement(\n \"div\",\n {\n style: {\n width: 26,\n height: 26,\n borderRadius: \"50%\",\n background: \"#e8eaed\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n fontSize: 11,\n color: \"#444\",\n fontWeight: 600,\n },\n },\n siteName[0].toUpperCase()\n ),\n React.createElement(\n \"div\",\n null,\n React.createElement(\n \"div\",\n { style: { fontSize: 14, color: \"#202124\", lineHeight: 1.3 } },\n siteName\n ),\n React.createElement(\n \"div\",\n { style: { fontSize: 12, color: \"#4d5156\", lineHeight: 1.3 } },\n truncate(canonical, 60)\n )\n )\n ),\n React.createElement(\n \"div\",\n {\n style: {\n fontSize: 20,\n color: \"#1a0dab\",\n lineHeight: 1.3,\n marginBottom: 4,\n cursor: \"pointer\",\n },\n },\n title\n ),\n React.createElement(\n \"div\",\n { style: { fontSize: 14, color: \"#4d5156\", lineHeight: 1.5 } },\n description\n )\n );\n}\n\nexport function SEOPreview({\n config,\n platform = \"twitter\",\n style,\n}: SEOPreviewProps): React.ReactElement {\n switch (platform) {\n case \"facebook\":\n return React.createElement(FacebookCard, { config, style });\n case \"linkedin\":\n return React.createElement(LinkedInCard, { config, style });\n case \"google\":\n return React.createElement(GoogleCard, { config, style });\n default:\n return React.createElement(TwitterCard, { config, style });\n }\n}\n"]}
@@ -0,0 +1,5 @@
1
+ 'use strict';function c(e){let i=[];for(let n of e){let s=n.name??n.path,t=n.seo??{};t.title?t.title.length>60&&i.push({page:s,field:"title",message:`Title is ${t.title.length} chars (recommended \u226460)`,severity:"warning"}):i.push({page:s,field:"title",message:"Missing page title",severity:"error"}),t.description?t.description.length>160&&i.push({page:s,field:"description",message:`Description is ${t.description.length} chars (recommended \u2264160)`,severity:"warning"}):i.push({page:s,field:"description",message:"Missing meta description",severity:"error"}),t.canonical||i.push({page:s,field:"canonical",message:"Missing canonical URL",severity:"warning"}),t.openGraph?.images?.length||i.push({page:s,field:"og:image",message:"Missing og:image",severity:"warning"}),!t.openGraph?.title&&!t.title&&i.push({page:s,field:"og:title",message:"Missing og:title",severity:"warning"}),t.twitter?.card||i.push({page:s,field:"twitter:card",message:"Missing twitter:card",severity:"warning"}),t.jsonLd||i.push({page:s,field:"structured-data",message:"No structured data (JSON-LD)",severity:"warning"});}return i}function p(e){if(!e.length)return "\u2705 All pages passed SEO validation.";let i=new Map;for(let s of e){let t=i.get(s.page)??[];t.push(s),i.set(s.page,t);}let n=[];for(let[s,t]of i){n.push(`
2
+ ${s}:`);for(let a of t){let o=a.severity==="error"?"\u274C":"\u26A0\uFE0F ";n.push(` ${o} [${a.field}] ${a.message}`);}}return n.join(`
3
+ `).trim()}var r=[{name:"Title",field:"title",maxPoints:20,check:e=>e.title?e.title.length>60?{passed:true,points:10,message:`Too long (${e.title.length}/60 chars)`}:{passed:true,points:20}:{passed:false,points:0,message:"Missing title"}},{name:"Description",field:"description",maxPoints:15,check:e=>e.description?e.description.length>160?{passed:true,points:8,message:`Too long (${e.description.length}/160 chars)`}:{passed:true,points:15}:{passed:false,points:0,message:"Missing description"}},{name:"Canonical URL",field:"canonical",maxPoints:10,check:e=>e.canonical?{passed:true,points:10}:{passed:false,points:0,message:"Missing canonical URL"}},{name:"og:image",field:"og:image",maxPoints:15,check:e=>e.openGraph?.images?.length?{passed:true,points:15}:{passed:false,points:0,message:"Missing og:image"}},{name:"og:title",field:"og:title",maxPoints:5,check:e=>e.openGraph?.title||e.title?{passed:true,points:5}:{passed:false,points:0,message:"Missing og:title"}},{name:"og:description",field:"og:description",maxPoints:5,check:e=>e.openGraph?.description||e.description?{passed:true,points:5}:{passed:false,points:0,message:"Missing og:description"}},{name:"Twitter Card",field:"twitter:card",maxPoints:10,check:e=>e.twitter?.card?{passed:true,points:10}:{passed:false,points:0,message:"Missing twitter:card"}},{name:"Structured Data",field:"structured-data",maxPoints:10,check:e=>e.jsonLd?{passed:true,points:10}:{passed:false,points:0,message:"No JSON-LD structured data"}},{name:"Robots Directives",field:"robots",maxPoints:5,check:e=>e.robots!==void 0?{passed:true,points:5}:{passed:false,points:0,message:"No robots directives"}},{name:"Hreflang",field:"hreflang",maxPoints:5,check:e=>e.alternates?.length?{passed:true,points:5}:{passed:false,points:0,message:"No hreflang alternates"}}];function g(e,i="Page"){let n=0,s=r.reduce((a,o)=>a+o.maxPoints,0),t=[];for(let a of r){let o=a.check(e);n+=o.points,t.push({name:a.name,passed:o.passed,points:o.points,maxPoints:a.maxPoints,message:o.message});}return {page:i,score:n,maxScore:s,percentage:Math.round(n/s*100),checks:t}}function d(e){let n=[`${e.percentage>=80?"\u{1F7E2}":e.percentage>=50?"\u{1F7E1}":"\u{1F534}"} SEO Score: ${e.page} ${e.score}/${e.maxScore} (${e.percentage}%)`];for(let s of e.checks){let t=s.passed?"\u2705":"\u274C",a=s.message?` \u2014 ${s.message}`:"",o=s.passed?"":` (-${s.maxPoints})`;n.push(` ${t} ${s.name}${o}${a}`);}return n.join(`
4
+ `)}exports.a=c;exports.b=p;exports.c=g;exports.d=d;//# sourceMappingURL=chunk-LEOORZQY.cjs.map
5
+ //# sourceMappingURL=chunk-LEOORZQY.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/validation/index.ts"],"names":["validateSEO","routes","issues","route","page","seo","printValidationReport","byPage","issue","list","lines","pageIssues","icon","SCORE_CHECKS","c","getSEOScore","config","pageName","score","maxScore","sum","checks","def","result","formatSEOScore","check","detail","pts"],"mappings":"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","file":"chunk-LEOORZQY.cjs","sourcesContent":["import type {\n SEOConfig,\n SEOValidationIssue,\n RouteWithSEO,\n SEOScoreResult,\n SEOScoreCheck,\n} from \"../types/index.js\";\n\n// ─── Validate SEO ─────────────────────────────────────────────\n\nexport function validateSEO(routes: RouteWithSEO[]): SEOValidationIssue[] {\n const issues: SEOValidationIssue[] = [];\n\n for (const route of routes) {\n const page = route.name ?? route.path;\n const seo = route.seo ?? {};\n\n if (!seo.title) {\n issues.push({ page, field: \"title\", message: \"Missing page title\", severity: \"error\" });\n } else if (seo.title.length > 60) {\n issues.push({\n page,\n field: \"title\",\n message: `Title is ${seo.title.length} chars (recommended ≤60)`,\n severity: \"warning\",\n });\n }\n\n if (!seo.description) {\n issues.push({ page, field: \"description\", message: \"Missing meta description\", severity: \"error\" });\n } else if (seo.description.length > 160) {\n issues.push({\n page,\n field: \"description\",\n message: `Description is ${seo.description.length} chars (recommended ≤160)`,\n severity: \"warning\",\n });\n }\n\n if (!seo.canonical) {\n issues.push({ page, field: \"canonical\", message: \"Missing canonical URL\", severity: \"warning\" });\n }\n\n if (!seo.openGraph?.images?.length) {\n issues.push({ page, field: \"og:image\", message: \"Missing og:image\", severity: \"warning\" });\n }\n\n if (!seo.openGraph?.title && !seo.title) {\n issues.push({ page, field: \"og:title\", message: \"Missing og:title\", severity: \"warning\" });\n }\n\n if (!seo.twitter?.card) {\n issues.push({ page, field: \"twitter:card\", message: \"Missing twitter:card\", severity: \"warning\" });\n }\n\n if (!seo.jsonLd) {\n issues.push({\n page,\n field: \"structured-data\",\n message: \"No structured data (JSON-LD)\",\n severity: \"warning\",\n });\n }\n }\n\n return issues;\n}\n\nexport function printValidationReport(issues: SEOValidationIssue[]): string {\n if (!issues.length) return \"✅ All pages passed SEO validation.\";\n\n const byPage = new Map<string, SEOValidationIssue[]>();\n for (const issue of issues) {\n const list = byPage.get(issue.page) ?? [];\n list.push(issue);\n byPage.set(issue.page, list);\n }\n\n const lines: string[] = [];\n for (const [page, pageIssues] of byPage) {\n lines.push(`\\n${page}:`);\n for (const issue of pageIssues) {\n const icon = issue.severity === \"error\" ? \"❌\" : \"⚠️ \";\n lines.push(` ${icon} [${issue.field}] ${issue.message}`);\n }\n }\n return lines.join(\"\\n\").trim();\n}\n\n// ─── SEO Score ────────────────────────────────────────────────\n\nconst SCORE_CHECKS: Array<{\n name: string;\n field: string;\n maxPoints: number;\n check: (config: SEOConfig) => { passed: boolean; points: number; message?: string };\n}> = [\n {\n name: \"Title\",\n field: \"title\",\n maxPoints: 20,\n check: (c) => {\n if (!c.title) return { passed: false, points: 0, message: \"Missing title\" };\n if (c.title.length > 60)\n return { passed: true, points: 10, message: `Too long (${c.title.length}/60 chars)` };\n return { passed: true, points: 20 };\n },\n },\n {\n name: \"Description\",\n field: \"description\",\n maxPoints: 15,\n check: (c) => {\n if (!c.description) return { passed: false, points: 0, message: \"Missing description\" };\n if (c.description.length > 160)\n return { passed: true, points: 8, message: `Too long (${c.description.length}/160 chars)` };\n return { passed: true, points: 15 };\n },\n },\n {\n name: \"Canonical URL\",\n field: \"canonical\",\n maxPoints: 10,\n check: (c) =>\n c.canonical\n ? { passed: true, points: 10 }\n : { passed: false, points: 0, message: \"Missing canonical URL\" },\n },\n {\n name: \"og:image\",\n field: \"og:image\",\n maxPoints: 15,\n check: (c) =>\n c.openGraph?.images?.length\n ? { passed: true, points: 15 }\n : { passed: false, points: 0, message: \"Missing og:image\" },\n },\n {\n name: \"og:title\",\n field: \"og:title\",\n maxPoints: 5,\n check: (c) =>\n c.openGraph?.title || c.title\n ? { passed: true, points: 5 }\n : { passed: false, points: 0, message: \"Missing og:title\" },\n },\n {\n name: \"og:description\",\n field: \"og:description\",\n maxPoints: 5,\n check: (c) =>\n c.openGraph?.description || c.description\n ? { passed: true, points: 5 }\n : { passed: false, points: 0, message: \"Missing og:description\" },\n },\n {\n name: \"Twitter Card\",\n field: \"twitter:card\",\n maxPoints: 10,\n check: (c) =>\n c.twitter?.card\n ? { passed: true, points: 10 }\n : { passed: false, points: 0, message: \"Missing twitter:card\" },\n },\n {\n name: \"Structured Data\",\n field: \"structured-data\",\n maxPoints: 10,\n check: (c) =>\n c.jsonLd\n ? { passed: true, points: 10 }\n : { passed: false, points: 0, message: \"No JSON-LD structured data\" },\n },\n {\n name: \"Robots Directives\",\n field: \"robots\",\n maxPoints: 5,\n check: (c) =>\n c.robots !== undefined\n ? { passed: true, points: 5 }\n : { passed: false, points: 0, message: \"No robots directives\" },\n },\n {\n name: \"Hreflang\",\n field: \"hreflang\",\n maxPoints: 5,\n check: (c) =>\n c.alternates?.length\n ? { passed: true, points: 5 }\n : { passed: false, points: 0, message: \"No hreflang alternates\" },\n },\n];\n\nexport function getSEOScore(config: SEOConfig, pageName = \"Page\"): SEOScoreResult {\n let score = 0;\n const maxScore = SCORE_CHECKS.reduce((sum, c) => sum + c.maxPoints, 0);\n const checks: SEOScoreCheck[] = [];\n\n for (const def of SCORE_CHECKS) {\n const result = def.check(config);\n score += result.points;\n checks.push({\n name: def.name,\n passed: result.passed,\n points: result.points,\n maxPoints: def.maxPoints,\n message: result.message,\n });\n }\n\n return {\n page: pageName,\n score,\n maxScore,\n percentage: Math.round((score / maxScore) * 100),\n checks,\n };\n}\n\nexport function formatSEOScore(result: SEOScoreResult): string {\n const bar = result.percentage >= 80 ? \"🟢\" : result.percentage >= 50 ? \"🟡\" : \"🔴\";\n const lines = [\n `${bar} SEO Score: ${result.page} ${result.score}/${result.maxScore} (${result.percentage}%)`,\n ];\n\n for (const check of result.checks) {\n const icon = check.passed ? \"✅\" : \"❌\";\n const detail = check.message ? ` — ${check.message}` : \"\";\n const pts = !check.passed ? ` (-${check.maxPoints})` : \"\";\n lines.push(` ${icon} ${check.name}${pts}${detail}`);\n }\n\n return lines.join(\"\\n\");\n}\n"]}
@@ -0,0 +1,2 @@
1
+ function f(e){let t=JSON.stringify(e,(n,r)=>{if(r!=null)return r});return t?t.replace(/</g,"\\u003c").replace(/>/g,"\\u003e").replace(/&/g,"\\u0026").replace(/\u2028/g,"\\u2028").replace(/\u2029/g,"\\u2029"):"{}"}function d(e){let t={};for(let n of Object.keys(e)){let r=e[n];r!=null&&r!==""&&(t[n]=r);}return t}function l(e,t){let n={...e},r=t,u=e;for(let i of Object.keys(r)){let s=r[i],a=u[i];s!==void 0&&(s!==null&&typeof s=="object"&&!Array.isArray(s)&&a!==null&&typeof a=="object"&&!Array.isArray(a)?n[i]=l(a,s):n[i]=s);}return n}function o(e){let t=e.trim();return t==="/"||t===""?t:t.replace(/\/+$/,"")}function g(e,t){let n=o(e);if(!t||t==="/")return n;let r=t.startsWith("/")?t:`/${t}`;return n+o(r)}function h(e={}){return p(e)}function y(e,t){let n=l(e,t);return t.alternates!==void 0&&(n.alternates=t.alternates),t.additionalMetaTags!==void 0&&(n.additionalMetaTags=t.additionalMetaTags),t.additionalLinkTags!==void 0&&(n.additionalLinkTags=t.additionalLinkTags),t.jsonLd!==void 0&&(n.jsonLd=t.jsonLd),p(n)}function p(e){let t={...e};return t.title&&(t.title=t.title.trim()),t.description&&(t.description=t.description.trim()),t.canonical&&(t.canonical=o(t.canonical)),t}function x(e,t){if(!e)return "";let n=e.trim();return t?t.replace(/%s/g,n):n}function w(e,t){if(!e)return "";let n=e.trim();return !t||n.length<=t?n:n.slice(0,t).trimEnd()+"\u2026"}function C(e,t){let n=o(e);if(!t||t==="/")return n;let r=t.startsWith("/")?t:`/${t}`;return n+o(r)}function k(e){if(!e)return "";let t=[];return e.index===false?t.push("noindex"):e.index===true&&t.push("index"),e.follow===false?t.push("nofollow"):e.follow===true&&t.push("follow"),e.noarchive&&t.push("noarchive"),e.nosnippet&&t.push("nosnippet"),e.noimageindex&&t.push("noimageindex"),e.notranslate&&t.push("notranslate"),e.maxSnippet!==void 0&&t.push(`max-snippet:${e.maxSnippet}`),e.maxImagePreview&&t.push(`max-image-preview:${e.maxImagePreview}`),e.maxVideoPreview!==void 0&&t.push(`max-video-preview:${e.maxVideoPreview}`),t.join(", ")}function O(){return {index:false,follow:false}}function b(){return {index:false,follow:true}}function T(e){if(!e)return [];let t=[],n=[["title","og:title"],["description","og:description"],["url","og:url"],["siteName","og:site_name"],["type","og:type"],["locale","og:locale"]];for(let[r,u]of n){let i=e[r];typeof i=="string"&&i.trim()&&t.push({property:u,content:i.trim()});}if(e.images)for(let r of e.images)r.url&&(t.push({property:"og:image",content:r.url}),r.alt&&t.push({property:"og:image:alt",content:r.alt}),r.width&&t.push({property:"og:image:width",content:String(r.width)}),r.height&&t.push({property:"og:image:height",content:String(r.height)}),r.type&&t.push({property:"og:image:type",content:r.type}));return t}function S(e){if(!e)return [];let t=[];return e.card&&t.push({name:"twitter:card",content:e.card}),e.site&&t.push({name:"twitter:site",content:e.site}),e.creator&&t.push({name:"twitter:creator",content:e.creator}),e.title&&t.push({name:"twitter:title",content:e.title}),e.description&&t.push({name:"twitter:description",content:e.description}),e.image&&t.push({name:"twitter:image",content:e.image}),e.imageAlt&&t.push({name:"twitter:image:alt",content:e.imageAlt}),t}function A(e){return !e||e.length===0?[]:e.map(t=>({rel:"alternate",hreflang:t.hreflang,href:o(t.href)}))}export{f as a,d as b,l as c,o as d,g as e,h as f,y as g,p as h,x as i,w as j,C as k,k as l,O as m,b as n,T as o,S as p,A as q};//# sourceMappingURL=chunk-LP66SUGR.js.map
2
+ //# sourceMappingURL=chunk-LP66SUGR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/index.ts","../src/core/index.ts"],"names":["safeJsonLdSerialize","data","json","_key","value","omitEmpty","obj","result","key","val","deepMerge","base","override","overrideObj","baseObj","overrideVal","baseVal","normalizeUrl","url","trimmed","buildFullUrl","path","normalizedBase","normalizedPath","createSEOConfig","config","normalizeSEOConfig","mergeSEOConfig","merged","normalized","buildTitle","title","template","buildDescription","description","maxLength","buildCanonicalUrl","baseUrl","buildRobotsDirectives","directives","noIndexNoFollow","noIndex","buildOpenGraph","tags","simple","property","image","buildTwitterMetadata","buildAlternateLinks","alternates","alt"],"mappings":"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","file":"chunk-LP66SUGR.js","sourcesContent":["/**\n * Safely serialize a value to JSON for use in a <script> tag.\n * Escapes characters that could break out of script context.\n */\nexport function safeJsonLdSerialize(data: unknown): string {\n const json = JSON.stringify(data, (_key, value) => {\n if (value === undefined || value === null) return undefined;\n return value;\n });\n\n if (!json) return \"{}\";\n\n return json\n .replace(/</g, \"\\\\u003c\")\n .replace(/>/g, \"\\\\u003e\")\n .replace(/&/g, \"\\\\u0026\")\n .replace(/\\u2028/g, \"\\\\u2028\")\n .replace(/\\u2029/g, \"\\\\u2029\");\n}\n\n/**\n * Strip undefined/null values from an object (shallow).\n */\nexport function omitEmpty<T extends Record<string, unknown>>(\n obj: T\n): Partial<T> {\n const result: Partial<T> = {};\n for (const key of Object.keys(obj) as Array<keyof T>) {\n const val = obj[key];\n if (val !== undefined && val !== null && val !== \"\") {\n result[key] = val;\n }\n }\n return result;\n}\n\n/**\n * Deep-merge two objects. Arrays are replaced, not concatenated.\n */\nexport function deepMerge<T>(\n base: T,\n override: Partial<T>\n): T {\n const result = { ...base } as Record<string, unknown>;\n const overrideObj = override as Record<string, unknown>;\n const baseObj = base as Record<string, unknown>;\n\n for (const key of Object.keys(overrideObj)) {\n const overrideVal = overrideObj[key];\n const baseVal = baseObj[key];\n\n if (overrideVal === undefined) continue;\n\n if (\n overrideVal !== null &&\n typeof overrideVal === \"object\" &&\n !Array.isArray(overrideVal) &&\n baseVal !== null &&\n typeof baseVal === \"object\" &&\n !Array.isArray(baseVal)\n ) {\n result[key] = deepMerge(\n baseVal as Record<string, unknown>,\n overrideVal as Record<string, unknown>\n );\n } else {\n result[key] = overrideVal;\n }\n }\n\n return result as T;\n}\n\n/**\n * Normalize a URL by trimming whitespace and removing trailing slashes\n * (except for root \"/\").\n */\nexport function normalizeUrl(url: string): string {\n const trimmed = url.trim();\n if (trimmed === \"/\" || trimmed === \"\") return trimmed;\n return trimmed.replace(/\\/+$/, \"\");\n}\n\n/**\n * Build a full canonical URL from a base and a path.\n */\nexport function buildFullUrl(base: string, path?: string): string {\n const normalizedBase = normalizeUrl(base);\n if (!path || path === \"/\") return normalizedBase;\n const normalizedPath = path.startsWith(\"/\") ? path : `/${path}`;\n return normalizedBase + normalizeUrl(normalizedPath);\n}\n","import type {\n SEOConfig,\n OpenGraphConfig,\n TwitterConfig,\n RobotsConfig,\n AlternateLink,\n} from \"../types/index.js\";\nimport { deepMerge, normalizeUrl } from \"../utils/index.js\";\n\n// ─── Config builder ───────────────────────────────────────────\n\n/**\n * Create an SEO config with sensible defaults.\n */\nexport function createSEOConfig(config: SEOConfig = {}): SEOConfig {\n return normalizeSEOConfig(config);\n}\n\n/**\n * Deep-merge a base SEO config with page-level overrides.\n * Useful for combining a global site config with per-page config.\n */\nexport function mergeSEOConfig(\n base: SEOConfig,\n override: SEOConfig\n): SEOConfig {\n const merged = deepMerge(base, override);\n\n // Arrays should be replaced, not deep-merged\n if (override.alternates !== undefined) {\n merged.alternates = override.alternates;\n }\n if (override.additionalMetaTags !== undefined) {\n merged.additionalMetaTags = override.additionalMetaTags;\n }\n if (override.additionalLinkTags !== undefined) {\n merged.additionalLinkTags = override.additionalLinkTags;\n }\n if (override.jsonLd !== undefined) {\n merged.jsonLd = override.jsonLd;\n }\n\n return normalizeSEOConfig(merged);\n}\n\n/**\n * Normalize an SEO config: trim strings, normalize URLs, remove empties.\n */\nexport function normalizeSEOConfig(config: SEOConfig): SEOConfig {\n const normalized: SEOConfig = { ...config };\n\n if (normalized.title) {\n normalized.title = normalized.title.trim();\n }\n if (normalized.description) {\n normalized.description = normalized.description.trim();\n }\n if (normalized.canonical) {\n normalized.canonical = normalizeUrl(normalized.canonical);\n }\n\n return normalized;\n}\n\n// ─── Title ────────────────────────────────────────────────────\n\n/**\n * Build a title string, optionally applying a template.\n * Template uses `%s` as the placeholder for the page title.\n *\n * @example\n * buildTitle(\"About\", \"%s | MySite\") // \"About | MySite\"\n * buildTitle(\"Home\") // \"Home\"\n */\nexport function buildTitle(title?: string, template?: string): string {\n if (!title) return \"\";\n const trimmed = title.trim();\n if (!template) return trimmed;\n return template.replace(/%s/g, trimmed);\n}\n\n/**\n * Build a description, truncating at a max length if specified.\n */\nexport function buildDescription(\n description?: string,\n maxLength?: number\n): string {\n if (!description) return \"\";\n const trimmed = description.trim();\n if (!maxLength || trimmed.length <= maxLength) return trimmed;\n return trimmed.slice(0, maxLength).trimEnd() + \"…\";\n}\n\n// ─── Canonical URL ────────────────────────────────────────────\n\n/**\n * Build a canonical URL from a base URL and optional path.\n */\nexport function buildCanonicalUrl(\n baseUrl: string,\n path?: string\n): string {\n const normalizedBase = normalizeUrl(baseUrl);\n if (!path || path === \"/\") return normalizedBase;\n const normalizedPath = path.startsWith(\"/\") ? path : `/${path}`;\n return normalizedBase + normalizeUrl(normalizedPath);\n}\n\n// ─── Robots ───────────────────────────────────────────────────\n\n/**\n * Build a robots meta content string from a config.\n *\n * @example\n * buildRobotsDirectives({ index: true, follow: true }) // \"index, follow\"\n * buildRobotsDirectives({ index: false, follow: false, noarchive: true })\n * // \"noindex, nofollow, noarchive\"\n */\nexport function buildRobotsDirectives(config?: RobotsConfig): string {\n if (!config) return \"\";\n\n const directives: string[] = [];\n\n if (config.index === false) directives.push(\"noindex\");\n else if (config.index === true) directives.push(\"index\");\n\n if (config.follow === false) directives.push(\"nofollow\");\n else if (config.follow === true) directives.push(\"follow\");\n\n if (config.noarchive) directives.push(\"noarchive\");\n if (config.nosnippet) directives.push(\"nosnippet\");\n if (config.noimageindex) directives.push(\"noimageindex\");\n if (config.notranslate) directives.push(\"notranslate\");\n\n if (config.maxSnippet !== undefined)\n directives.push(`max-snippet:${config.maxSnippet}`);\n if (config.maxImagePreview)\n directives.push(`max-image-preview:${config.maxImagePreview}`);\n if (config.maxVideoPreview !== undefined)\n directives.push(`max-video-preview:${config.maxVideoPreview}`);\n\n return directives.join(\", \");\n}\n\n// ─── Convenience helpers ──────────────────────────────────────\n\n/**\n * Create a noindex/nofollow robots config.\n */\nexport function noIndexNoFollow(): RobotsConfig {\n return { index: false, follow: false };\n}\n\n/**\n * Create a noindex robots config that still allows following links.\n */\nexport function noIndex(): RobotsConfig {\n return { index: false, follow: true };\n}\n\n// ─── Open Graph ───────────────────────────────────────────────\n\n/**\n * Build Open Graph meta tag entries from config.\n * Returns an array of { property, content } pairs.\n */\nexport function buildOpenGraph(\n config?: OpenGraphConfig\n): Array<{ property: string; content: string }> {\n if (!config) return [];\n\n const tags: Array<{ property: string; content: string }> = [];\n\n const simple: Array<[keyof OpenGraphConfig, string]> = [\n [\"title\", \"og:title\"],\n [\"description\", \"og:description\"],\n [\"url\", \"og:url\"],\n [\"siteName\", \"og:site_name\"],\n [\"type\", \"og:type\"],\n [\"locale\", \"og:locale\"],\n ];\n\n for (const [key, property] of simple) {\n const value = config[key];\n if (typeof value === \"string\" && value.trim()) {\n tags.push({ property, content: value.trim() });\n }\n }\n\n if (config.images) {\n for (const image of config.images) {\n if (!image.url) continue;\n tags.push({ property: \"og:image\", content: image.url });\n if (image.alt) tags.push({ property: \"og:image:alt\", content: image.alt });\n if (image.width)\n tags.push({\n property: \"og:image:width\",\n content: String(image.width),\n });\n if (image.height)\n tags.push({\n property: \"og:image:height\",\n content: String(image.height),\n });\n if (image.type)\n tags.push({ property: \"og:image:type\", content: image.type });\n }\n }\n\n return tags;\n}\n\n// ─── Twitter ──────────────────────────────────────────────────\n\n/**\n * Build Twitter card meta tag entries from config.\n * Returns an array of { name, content } pairs.\n */\nexport function buildTwitterMetadata(\n config?: TwitterConfig\n): Array<{ name: string; content: string }> {\n if (!config) return [];\n\n const tags: Array<{ name: string; content: string }> = [];\n\n if (config.card) tags.push({ name: \"twitter:card\", content: config.card });\n if (config.site) tags.push({ name: \"twitter:site\", content: config.site });\n if (config.creator)\n tags.push({ name: \"twitter:creator\", content: config.creator });\n if (config.title)\n tags.push({ name: \"twitter:title\", content: config.title });\n if (config.description)\n tags.push({ name: \"twitter:description\", content: config.description });\n if (config.image)\n tags.push({ name: \"twitter:image\", content: config.image });\n if (config.imageAlt)\n tags.push({ name: \"twitter:image:alt\", content: config.imageAlt });\n\n return tags;\n}\n\n// ─── Hreflang ─────────────────────────────────────────────────\n\n/**\n * Build alternate link entries for hreflang from an array of alternates.\n * Returns an array of { rel, hreflang, href } suitable for <link> tags.\n */\nexport function buildAlternateLinks(\n alternates?: AlternateLink[]\n): Array<{ rel: string; hreflang: string; href: string }> {\n if (!alternates || alternates.length === 0) return [];\n return alternates.map((alt) => ({\n rel: \"alternate\",\n hreflang: alt.hreflang,\n href: normalizeUrl(alt.href),\n }));\n}\n"]}
@@ -0,0 +1,61 @@
1
+ 'use strict';var a="system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif";function g(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function p(t,s,i){let l=t.split(" "),n=[],e="";for(let f of l){if(n.length>=i)break;let o=e?`${e} ${f}`:f;if(o.length>s&&e){if(n.length===i-1){n.push(`${e.slice(0,s-1)}\u2026`),e="";break}n.push(e),e=f;}else !e&&o.length>s?n.push(`${o.slice(0,s-1)}\u2026`):e=o;}return e&&n.length<i&&n.push(e),n}function u(t,s,i,l,n,e="400",f="start"){if(!t.length)return "";let o=Math.round(l*1.35),$=t.map((r,c)=>`<tspan x="${s}" dy="${c===0?0:o}">${g(r)}</tspan>`).join("");return `<text x="${s}" y="${i}" font-family="${a}" font-size="${l}" font-weight="${e}" fill="${n}" text-anchor="${f}">${$}</text>`}function H(t){let{title:s,description:i,brand:l,textColor:n="#ffffff",accentColor:e="#6366f1"}=t,f=t.backgroundColor??"#0f0c29",o=t.backgroundColor??"#24243e",$=p(s,24,2),r=i?p(i,55,2):[],c=68,m=34,h=Math.round(c*1.35),y=Math.round(m*1.4),d=28,x=$.length*h+(r.length?d+r.length*y:0),w=Math.round((630-x)/2)+c,b=w+$.length*h+d;return `<?xml version="1.0" encoding="UTF-8"?>
2
+ <svg width="1200" height="630" viewBox="0 0 1200 630" xmlns="http://www.w3.org/2000/svg">
3
+ <defs>
4
+ <linearGradient id="bg" x1="0" y1="0" x2="1200" y2="630" gradientUnits="userSpaceOnUse">
5
+ <stop offset="0%" stop-color="${f}"/>
6
+ <stop offset="100%" stop-color="${o}"/>
7
+ </linearGradient>
8
+ </defs>
9
+ <rect width="1200" height="630" fill="url(#bg)"/>
10
+ <rect x="0" y="0" width="8" height="630" fill="${e}"/>
11
+ <rect x="80" y="${w-c-24}" width="56" height="5" fill="${e}" rx="2.5"/>
12
+ ${u($,80,w,c,n,"700")}
13
+ ${r.length?u(r,80,b,m,"rgba(255,255,255,0.72)"):""}
14
+ ${l?`<text x="1140" y="590" font-family="${a}" font-size="26" fill="rgba(255,255,255,0.38)" text-anchor="end">${g(l)}</text>`:""}
15
+ </svg>`.trim()}function v(t){let{title:s,description:i,brand:l,category:n,author:e,dateString:f,accentColor:o="#e63946"}=t,$=t.backgroundColor??"#f8f9fa",r=t.textColor??"#1a1a2e",c=p(s,38,3),m=i?p(i,68,2):[],h=52,y=28,d=Math.round(h*1.3),x=n?200:170,w=x+c.length*d+24;return `<?xml version="1.0" encoding="UTF-8"?>
16
+ <svg width="1200" height="630" viewBox="0 0 1200 630" xmlns="http://www.w3.org/2000/svg">
17
+ <rect width="1200" height="630" fill="${$}"/>
18
+ <rect x="0" y="0" width="1200" height="8" fill="${o}"/>
19
+ ${n?`<rect x="60" y="120" width="${n.length*13+32}" height="36" fill="${o}" rx="18"/>
20
+ <text x="76" y="144" font-family="${a}" font-size="18" font-weight="600" fill="#ffffff">${g(n.toUpperCase())}</text>`:""}
21
+ ${u(c,60,x,h,r,"700")}
22
+ ${m.length?u(m,60,w,y,`${r}b3`):""}
23
+ <rect x="60" y="550" width="1080" height="1" fill="rgba(0,0,0,0.1)"/>
24
+ ${e?`<text x="60" y="586" font-family="${a}" font-size="22" fill="${r}99">${g(e)}</text>`:""}
25
+ ${f?`<text x="1140" y="586" font-family="${a}" font-size="22" fill="${r}66" text-anchor="end">${g(f)}</text>`:""}
26
+ ${l?`<text x="${1200/2}" y="586" font-family="${a}" font-size="22" font-weight="600" fill="${o}" text-anchor="middle">${g(l)}</text>`:""}
27
+ </svg>`.trim()}function W(t){let{title:s,brand:i,homeTeam:l,awayTeam:n,eventDate:e,accentColor:f="#ffd700"}=t,o=t.backgroundColor??"#0a0a1a",$=t.backgroundColor??"#1a1a3e",r=t.textColor??"#ffffff";if(l&&n){let h=p(l.toUpperCase(),12,2),y=p(n.toUpperCase(),12,2),d=64,x=h.length>1?300:330;return `<?xml version="1.0" encoding="UTF-8"?>
28
+ <svg width="1200" height="630" viewBox="0 0 1200 630" xmlns="http://www.w3.org/2000/svg">
29
+ <defs>
30
+ <linearGradient id="bg" x1="0" y1="0" x2="1200" y2="630" gradientUnits="userSpaceOnUse">
31
+ <stop offset="0%" stop-color="${o}"/>
32
+ <stop offset="100%" stop-color="${$}"/>
33
+ </linearGradient>
34
+ <linearGradient id="div" x1="0" y1="0" x2="0" y2="1">
35
+ <stop offset="0%" stop-color="rgba(255,215,0,0)"/>
36
+ <stop offset="50%" stop-color="rgba(255,215,0,0.25)"/>
37
+ <stop offset="100%" stop-color="rgba(255,215,0,0)"/>
38
+ </linearGradient>
39
+ </defs>
40
+ <rect width="1200" height="630" fill="url(#bg)"/>
41
+ <rect x="591" y="60" width="18" height="510" fill="url(#div)" rx="9"/>
42
+ ${u(h,300,x,d,r,"800","middle")}
43
+ <text x="600" y="345" font-family="${a}" font-size="56" font-weight="900" fill="${f}" text-anchor="middle">VS</text>
44
+ ${u(y,900,x,d,r,"800","middle")}
45
+ ${e?`<text x="${1200/2}" y="510" font-family="${a}" font-size="26" fill="rgba(255,255,255,0.55)" text-anchor="middle">${g(e)}</text>`:""}
46
+ ${i?`<text x="1140" y="594" font-family="${a}" font-size="24" fill="rgba(255,255,255,0.3)" text-anchor="end">${g(i)}</text>`:""}
47
+ </svg>`.trim()}let c=p(s,28,2);return `<?xml version="1.0" encoding="UTF-8"?>
48
+ <svg width="1200" height="630" viewBox="0 0 1200 630" xmlns="http://www.w3.org/2000/svg">
49
+ <defs>
50
+ <linearGradient id="bg" x1="0" y1="0" x2="1200" y2="630" gradientUnits="userSpaceOnUse">
51
+ <stop offset="0%" stop-color="${o}"/>
52
+ <stop offset="100%" stop-color="${$}"/>
53
+ </linearGradient>
54
+ </defs>
55
+ <rect width="1200" height="630" fill="url(#bg)"/>
56
+ <rect x="0" y="0" width="8" height="630" fill="${f}"/>
57
+ ${u(c,80,300,64,r,"700")}
58
+ ${e?`<text x="80" y="410" font-family="${a}" font-size="28" fill="rgba(255,255,255,0.55)">${g(e)}</text>`:""}
59
+ ${i?`<text x="1140" y="594" font-family="${a}" font-size="24" fill="rgba(255,255,255,0.3)" text-anchor="end">${g(i)}</text>`:""}
60
+ </svg>`.trim()}function G(t){switch(t.template){case "article":return v(t);case "sports-event":return W(t);default:return H(t)}}exports.a=G;//# sourceMappingURL=chunk-ROA74LH6.cjs.map
61
+ //# sourceMappingURL=chunk-ROA74LH6.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/og/index.ts"],"names":["FONT","esc","str","wrapText","text","maxChars","maxLines","words","lines","line","word","test","multilineText","x","y","fontSize","fill","fontWeight","textAnchor","lh","tspans","l","i","createDefaultSVG","options","title","description","brand","textColor","accentColor","bg1","bg2","titleLines","descLines","titleSize","descSize","titleLH","descLH","gap","totalH","startY","descY","createArticleSVG","category","author","dateString","bg","titleY","createSportsEventSVG","homeTeam","awayTeam","eventDate","homeLines","awayLines","teamSize","teamY","createOGImageSVG"],"mappings":"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","file":"chunk-ROA74LH6.cjs","sourcesContent":["import type { OGImageOptions } from \"../types/index.js\";\n\nconst W = 1200;\nconst H = 630;\nconst FONT = \"system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif\";\n\nfunction esc(str: string): string {\n return str\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#39;\");\n}\n\nfunction wrapText(text: string, maxChars: number, maxLines: number): string[] {\n const words = text.split(\" \");\n const lines: string[] = [];\n let line = \"\";\n\n for (const word of words) {\n if (lines.length >= maxLines) break;\n const test = line ? `${line} ${word}` : word;\n if (test.length > maxChars && line) {\n if (lines.length === maxLines - 1) {\n lines.push(`${line.slice(0, maxChars - 1)}…`);\n line = \"\";\n break;\n }\n lines.push(line);\n line = word;\n } else if (!line && test.length > maxChars) {\n lines.push(`${test.slice(0, maxChars - 1)}…`);\n } else {\n line = test;\n }\n }\n if (line && lines.length < maxLines) lines.push(line);\n return lines;\n}\n\nfunction multilineText(\n lines: string[],\n x: number,\n y: number,\n fontSize: number,\n fill: string,\n fontWeight = \"400\",\n textAnchor = \"start\"\n): string {\n if (!lines.length) return \"\";\n const lh = Math.round(fontSize * 1.35);\n const tspans = lines\n .map((l, i) => `<tspan x=\"${x}\" dy=\"${i === 0 ? 0 : lh}\">${esc(l)}</tspan>`)\n .join(\"\");\n return `<text x=\"${x}\" y=\"${y}\" font-family=\"${FONT}\" font-size=\"${fontSize}\" font-weight=\"${fontWeight}\" fill=\"${fill}\" text-anchor=\"${textAnchor}\">${tspans}</text>`;\n}\n\n// ─── Default template ─────────────────────────────────────────\n\nfunction createDefaultSVG(options: OGImageOptions): string {\n const {\n title,\n description,\n brand,\n textColor = \"#ffffff\",\n accentColor = \"#6366f1\",\n } = options;\n\n const bg1 = options.backgroundColor ?? \"#0f0c29\";\n const bg2 = options.backgroundColor ?? \"#24243e\";\n\n const titleLines = wrapText(title, 24, 2);\n const descLines = description ? wrapText(description, 55, 2) : [];\n\n const titleSize = 68;\n const descSize = 34;\n const titleLH = Math.round(titleSize * 1.35);\n const descLH = Math.round(descSize * 1.4);\n const gap = 28;\n\n const totalH =\n titleLines.length * titleLH +\n (descLines.length ? gap + descLines.length * descLH : 0);\n\n const startY = Math.round((H - totalH) / 2) + titleSize;\n const descY = startY + titleLines.length * titleLH + gap;\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<svg width=\"${W}\" height=\"${H}\" viewBox=\"0 0 ${W} ${H}\" xmlns=\"http://www.w3.org/2000/svg\">\n <defs>\n <linearGradient id=\"bg\" x1=\"0\" y1=\"0\" x2=\"${W}\" y2=\"${H}\" gradientUnits=\"userSpaceOnUse\">\n <stop offset=\"0%\" stop-color=\"${bg1}\"/>\n <stop offset=\"100%\" stop-color=\"${bg2}\"/>\n </linearGradient>\n </defs>\n <rect width=\"${W}\" height=\"${H}\" fill=\"url(#bg)\"/>\n <rect x=\"0\" y=\"0\" width=\"8\" height=\"${H}\" fill=\"${accentColor}\"/>\n <rect x=\"80\" y=\"${startY - titleSize - 24}\" width=\"56\" height=\"5\" fill=\"${accentColor}\" rx=\"2.5\"/>\n ${multilineText(titleLines, 80, startY, titleSize, textColor, \"700\")}\n ${descLines.length ? multilineText(descLines, 80, descY, descSize, \"rgba(255,255,255,0.72)\") : \"\"}\n ${brand ? `<text x=\"${W - 60}\" y=\"${H - 40}\" font-family=\"${FONT}\" font-size=\"26\" fill=\"rgba(255,255,255,0.38)\" text-anchor=\"end\">${esc(brand)}</text>` : \"\"}\n</svg>`.trim();\n}\n\n// ─── Article template ─────────────────────────────────────────\n\nfunction createArticleSVG(options: OGImageOptions): string {\n const {\n title,\n description,\n brand,\n category,\n author,\n dateString,\n accentColor = \"#e63946\",\n } = options;\n\n const bg = options.backgroundColor ?? \"#f8f9fa\";\n const textColor = options.textColor ?? \"#1a1a2e\";\n\n const titleLines = wrapText(title, 38, 3);\n const descLines = description ? wrapText(description, 68, 2) : [];\n\n const titleSize = 52;\n const descSize = 28;\n const titleLH = Math.round(titleSize * 1.3);\n\n const titleY = category ? 200 : 170;\n const descY = titleY + titleLines.length * titleLH + 24;\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<svg width=\"${W}\" height=\"${H}\" viewBox=\"0 0 ${W} ${H}\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect width=\"${W}\" height=\"${H}\" fill=\"${bg}\"/>\n <rect x=\"0\" y=\"0\" width=\"${W}\" height=\"8\" fill=\"${accentColor}\"/>\n ${\n category\n ? `<rect x=\"60\" y=\"120\" width=\"${category.length * 13 + 32}\" height=\"36\" fill=\"${accentColor}\" rx=\"18\"/>\n <text x=\"${60 + 16}\" y=\"144\" font-family=\"${FONT}\" font-size=\"18\" font-weight=\"600\" fill=\"#ffffff\">${esc(category.toUpperCase())}</text>`\n : \"\"\n }\n ${multilineText(titleLines, 60, titleY, titleSize, textColor, \"700\")}\n ${descLines.length ? multilineText(descLines, 60, descY, descSize, `${textColor}b3`) : \"\"}\n <rect x=\"60\" y=\"${H - 80}\" width=\"${W - 120}\" height=\"1\" fill=\"rgba(0,0,0,0.1)\"/>\n ${\n author\n ? `<text x=\"60\" y=\"${H - 44}\" font-family=\"${FONT}\" font-size=\"22\" fill=\"${textColor}99\">${esc(author)}</text>`\n : \"\"\n }\n ${\n dateString\n ? `<text x=\"${W - 60}\" y=\"${H - 44}\" font-family=\"${FONT}\" font-size=\"22\" fill=\"${textColor}66\" text-anchor=\"end\">${esc(dateString)}</text>`\n : \"\"\n }\n ${\n brand\n ? `<text x=\"${W / 2}\" y=\"${H - 44}\" font-family=\"${FONT}\" font-size=\"22\" font-weight=\"600\" fill=\"${accentColor}\" text-anchor=\"middle\">${esc(brand)}</text>`\n : \"\"\n }\n</svg>`.trim();\n}\n\n// ─── Sports event template ────────────────────────────────────\n\nfunction createSportsEventSVG(options: OGImageOptions): string {\n const {\n title,\n brand,\n homeTeam,\n awayTeam,\n eventDate,\n accentColor = \"#ffd700\",\n } = options;\n\n const bg1 = options.backgroundColor ?? \"#0a0a1a\";\n const bg2 = options.backgroundColor ?? \"#1a1a3e\";\n const textColor = options.textColor ?? \"#ffffff\";\n\n if (homeTeam && awayTeam) {\n const homeLines = wrapText(homeTeam.toUpperCase(), 12, 2);\n const awayLines = wrapText(awayTeam.toUpperCase(), 12, 2);\n const teamSize = 64;\n const teamY = homeLines.length > 1 ? 300 : 330;\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<svg width=\"${W}\" height=\"${H}\" viewBox=\"0 0 ${W} ${H}\" xmlns=\"http://www.w3.org/2000/svg\">\n <defs>\n <linearGradient id=\"bg\" x1=\"0\" y1=\"0\" x2=\"${W}\" y2=\"${H}\" gradientUnits=\"userSpaceOnUse\">\n <stop offset=\"0%\" stop-color=\"${bg1}\"/>\n <stop offset=\"100%\" stop-color=\"${bg2}\"/>\n </linearGradient>\n <linearGradient id=\"div\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" stop-color=\"rgba(255,215,0,0)\"/>\n <stop offset=\"50%\" stop-color=\"rgba(255,215,0,0.25)\"/>\n <stop offset=\"100%\" stop-color=\"rgba(255,215,0,0)\"/>\n </linearGradient>\n </defs>\n <rect width=\"${W}\" height=\"${H}\" fill=\"url(#bg)\"/>\n <rect x=\"591\" y=\"60\" width=\"18\" height=\"510\" fill=\"url(#div)\" rx=\"9\"/>\n ${multilineText(homeLines, 300, teamY, teamSize, textColor, \"800\", \"middle\")}\n <text x=\"600\" y=\"345\" font-family=\"${FONT}\" font-size=\"56\" font-weight=\"900\" fill=\"${accentColor}\" text-anchor=\"middle\">VS</text>\n ${multilineText(awayLines, 900, teamY, teamSize, textColor, \"800\", \"middle\")}\n ${eventDate ? `<text x=\"${W / 2}\" y=\"510\" font-family=\"${FONT}\" font-size=\"26\" fill=\"rgba(255,255,255,0.55)\" text-anchor=\"middle\">${esc(eventDate)}</text>` : \"\"}\n ${brand ? `<text x=\"${W - 60}\" y=\"${H - 36}\" font-family=\"${FONT}\" font-size=\"24\" fill=\"rgba(255,255,255,0.3)\" text-anchor=\"end\">${esc(brand)}</text>` : \"\"}\n</svg>`.trim();\n }\n\n const titleLines = wrapText(title, 28, 2);\n const titleSize = 64;\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<svg width=\"${W}\" height=\"${H}\" viewBox=\"0 0 ${W} ${H}\" xmlns=\"http://www.w3.org/2000/svg\">\n <defs>\n <linearGradient id=\"bg\" x1=\"0\" y1=\"0\" x2=\"${W}\" y2=\"${H}\" gradientUnits=\"userSpaceOnUse\">\n <stop offset=\"0%\" stop-color=\"${bg1}\"/>\n <stop offset=\"100%\" stop-color=\"${bg2}\"/>\n </linearGradient>\n </defs>\n <rect width=\"${W}\" height=\"${H}\" fill=\"url(#bg)\"/>\n <rect x=\"0\" y=\"0\" width=\"8\" height=\"${H}\" fill=\"${accentColor}\"/>\n ${multilineText(titleLines, 80, 300, titleSize, textColor, \"700\")}\n ${eventDate ? `<text x=\"80\" y=\"410\" font-family=\"${FONT}\" font-size=\"28\" fill=\"rgba(255,255,255,0.55)\">${esc(eventDate)}</text>` : \"\"}\n ${brand ? `<text x=\"${W - 60}\" y=\"${H - 36}\" font-family=\"${FONT}\" font-size=\"24\" fill=\"rgba(255,255,255,0.3)\" text-anchor=\"end\">${esc(brand)}</text>` : \"\"}\n</svg>`.trim();\n}\n\n// ─── Public API ───────────────────────────────────────────────\n\nexport function createOGImageSVG(options: OGImageOptions): string {\n switch (options.template) {\n case \"article\":\n return createArticleSVG(options);\n case \"sports-event\":\n return createSportsEventSVG(options);\n default:\n return createDefaultSVG(options);\n }\n}\n"]}
@@ -0,0 +1,61 @@
1
+ var a="system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif";function g(t){return t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function p(t,s,i){let l=t.split(" "),n=[],e="";for(let f of l){if(n.length>=i)break;let o=e?`${e} ${f}`:f;if(o.length>s&&e){if(n.length===i-1){n.push(`${e.slice(0,s-1)}\u2026`),e="";break}n.push(e),e=f;}else !e&&o.length>s?n.push(`${o.slice(0,s-1)}\u2026`):e=o;}return e&&n.length<i&&n.push(e),n}function u(t,s,i,l,n,e="400",f="start"){if(!t.length)return "";let o=Math.round(l*1.35),$=t.map((r,c)=>`<tspan x="${s}" dy="${c===0?0:o}">${g(r)}</tspan>`).join("");return `<text x="${s}" y="${i}" font-family="${a}" font-size="${l}" font-weight="${e}" fill="${n}" text-anchor="${f}">${$}</text>`}function H(t){let{title:s,description:i,brand:l,textColor:n="#ffffff",accentColor:e="#6366f1"}=t,f=t.backgroundColor??"#0f0c29",o=t.backgroundColor??"#24243e",$=p(s,24,2),r=i?p(i,55,2):[],c=68,m=34,h=Math.round(c*1.35),y=Math.round(m*1.4),d=28,x=$.length*h+(r.length?d+r.length*y:0),w=Math.round((630-x)/2)+c,b=w+$.length*h+d;return `<?xml version="1.0" encoding="UTF-8"?>
2
+ <svg width="1200" height="630" viewBox="0 0 1200 630" xmlns="http://www.w3.org/2000/svg">
3
+ <defs>
4
+ <linearGradient id="bg" x1="0" y1="0" x2="1200" y2="630" gradientUnits="userSpaceOnUse">
5
+ <stop offset="0%" stop-color="${f}"/>
6
+ <stop offset="100%" stop-color="${o}"/>
7
+ </linearGradient>
8
+ </defs>
9
+ <rect width="1200" height="630" fill="url(#bg)"/>
10
+ <rect x="0" y="0" width="8" height="630" fill="${e}"/>
11
+ <rect x="80" y="${w-c-24}" width="56" height="5" fill="${e}" rx="2.5"/>
12
+ ${u($,80,w,c,n,"700")}
13
+ ${r.length?u(r,80,b,m,"rgba(255,255,255,0.72)"):""}
14
+ ${l?`<text x="1140" y="590" font-family="${a}" font-size="26" fill="rgba(255,255,255,0.38)" text-anchor="end">${g(l)}</text>`:""}
15
+ </svg>`.trim()}function v(t){let{title:s,description:i,brand:l,category:n,author:e,dateString:f,accentColor:o="#e63946"}=t,$=t.backgroundColor??"#f8f9fa",r=t.textColor??"#1a1a2e",c=p(s,38,3),m=i?p(i,68,2):[],h=52,y=28,d=Math.round(h*1.3),x=n?200:170,w=x+c.length*d+24;return `<?xml version="1.0" encoding="UTF-8"?>
16
+ <svg width="1200" height="630" viewBox="0 0 1200 630" xmlns="http://www.w3.org/2000/svg">
17
+ <rect width="1200" height="630" fill="${$}"/>
18
+ <rect x="0" y="0" width="1200" height="8" fill="${o}"/>
19
+ ${n?`<rect x="60" y="120" width="${n.length*13+32}" height="36" fill="${o}" rx="18"/>
20
+ <text x="76" y="144" font-family="${a}" font-size="18" font-weight="600" fill="#ffffff">${g(n.toUpperCase())}</text>`:""}
21
+ ${u(c,60,x,h,r,"700")}
22
+ ${m.length?u(m,60,w,y,`${r}b3`):""}
23
+ <rect x="60" y="550" width="1080" height="1" fill="rgba(0,0,0,0.1)"/>
24
+ ${e?`<text x="60" y="586" font-family="${a}" font-size="22" fill="${r}99">${g(e)}</text>`:""}
25
+ ${f?`<text x="1140" y="586" font-family="${a}" font-size="22" fill="${r}66" text-anchor="end">${g(f)}</text>`:""}
26
+ ${l?`<text x="${1200/2}" y="586" font-family="${a}" font-size="22" font-weight="600" fill="${o}" text-anchor="middle">${g(l)}</text>`:""}
27
+ </svg>`.trim()}function W(t){let{title:s,brand:i,homeTeam:l,awayTeam:n,eventDate:e,accentColor:f="#ffd700"}=t,o=t.backgroundColor??"#0a0a1a",$=t.backgroundColor??"#1a1a3e",r=t.textColor??"#ffffff";if(l&&n){let h=p(l.toUpperCase(),12,2),y=p(n.toUpperCase(),12,2),d=64,x=h.length>1?300:330;return `<?xml version="1.0" encoding="UTF-8"?>
28
+ <svg width="1200" height="630" viewBox="0 0 1200 630" xmlns="http://www.w3.org/2000/svg">
29
+ <defs>
30
+ <linearGradient id="bg" x1="0" y1="0" x2="1200" y2="630" gradientUnits="userSpaceOnUse">
31
+ <stop offset="0%" stop-color="${o}"/>
32
+ <stop offset="100%" stop-color="${$}"/>
33
+ </linearGradient>
34
+ <linearGradient id="div" x1="0" y1="0" x2="0" y2="1">
35
+ <stop offset="0%" stop-color="rgba(255,215,0,0)"/>
36
+ <stop offset="50%" stop-color="rgba(255,215,0,0.25)"/>
37
+ <stop offset="100%" stop-color="rgba(255,215,0,0)"/>
38
+ </linearGradient>
39
+ </defs>
40
+ <rect width="1200" height="630" fill="url(#bg)"/>
41
+ <rect x="591" y="60" width="18" height="510" fill="url(#div)" rx="9"/>
42
+ ${u(h,300,x,d,r,"800","middle")}
43
+ <text x="600" y="345" font-family="${a}" font-size="56" font-weight="900" fill="${f}" text-anchor="middle">VS</text>
44
+ ${u(y,900,x,d,r,"800","middle")}
45
+ ${e?`<text x="${1200/2}" y="510" font-family="${a}" font-size="26" fill="rgba(255,255,255,0.55)" text-anchor="middle">${g(e)}</text>`:""}
46
+ ${i?`<text x="1140" y="594" font-family="${a}" font-size="24" fill="rgba(255,255,255,0.3)" text-anchor="end">${g(i)}</text>`:""}
47
+ </svg>`.trim()}let c=p(s,28,2);return `<?xml version="1.0" encoding="UTF-8"?>
48
+ <svg width="1200" height="630" viewBox="0 0 1200 630" xmlns="http://www.w3.org/2000/svg">
49
+ <defs>
50
+ <linearGradient id="bg" x1="0" y1="0" x2="1200" y2="630" gradientUnits="userSpaceOnUse">
51
+ <stop offset="0%" stop-color="${o}"/>
52
+ <stop offset="100%" stop-color="${$}"/>
53
+ </linearGradient>
54
+ </defs>
55
+ <rect width="1200" height="630" fill="url(#bg)"/>
56
+ <rect x="0" y="0" width="8" height="630" fill="${f}"/>
57
+ ${u(c,80,300,64,r,"700")}
58
+ ${e?`<text x="80" y="410" font-family="${a}" font-size="28" fill="rgba(255,255,255,0.55)">${g(e)}</text>`:""}
59
+ ${i?`<text x="1140" y="594" font-family="${a}" font-size="24" fill="rgba(255,255,255,0.3)" text-anchor="end">${g(i)}</text>`:""}
60
+ </svg>`.trim()}function G(t){switch(t.template){case "article":return v(t);case "sports-event":return W(t);default:return H(t)}}export{G as a};//# sourceMappingURL=chunk-WM5VVPED.js.map
61
+ //# sourceMappingURL=chunk-WM5VVPED.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/og/index.ts"],"names":["FONT","esc","str","wrapText","text","maxChars","maxLines","words","lines","line","word","test","multilineText","x","y","fontSize","fill","fontWeight","textAnchor","lh","tspans","l","i","createDefaultSVG","options","title","description","brand","textColor","accentColor","bg1","bg2","titleLines","descLines","titleSize","descSize","titleLH","descLH","gap","totalH","startY","descY","createArticleSVG","category","author","dateString","bg","titleY","createSportsEventSVG","homeTeam","awayTeam","eventDate","homeLines","awayLines","teamSize","teamY","createOGImageSVG"],"mappings":"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","file":"chunk-WM5VVPED.js","sourcesContent":["import type { OGImageOptions } from \"../types/index.js\";\n\nconst W = 1200;\nconst H = 630;\nconst FONT = \"system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif\";\n\nfunction esc(str: string): string {\n return str\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#39;\");\n}\n\nfunction wrapText(text: string, maxChars: number, maxLines: number): string[] {\n const words = text.split(\" \");\n const lines: string[] = [];\n let line = \"\";\n\n for (const word of words) {\n if (lines.length >= maxLines) break;\n const test = line ? `${line} ${word}` : word;\n if (test.length > maxChars && line) {\n if (lines.length === maxLines - 1) {\n lines.push(`${line.slice(0, maxChars - 1)}…`);\n line = \"\";\n break;\n }\n lines.push(line);\n line = word;\n } else if (!line && test.length > maxChars) {\n lines.push(`${test.slice(0, maxChars - 1)}…`);\n } else {\n line = test;\n }\n }\n if (line && lines.length < maxLines) lines.push(line);\n return lines;\n}\n\nfunction multilineText(\n lines: string[],\n x: number,\n y: number,\n fontSize: number,\n fill: string,\n fontWeight = \"400\",\n textAnchor = \"start\"\n): string {\n if (!lines.length) return \"\";\n const lh = Math.round(fontSize * 1.35);\n const tspans = lines\n .map((l, i) => `<tspan x=\"${x}\" dy=\"${i === 0 ? 0 : lh}\">${esc(l)}</tspan>`)\n .join(\"\");\n return `<text x=\"${x}\" y=\"${y}\" font-family=\"${FONT}\" font-size=\"${fontSize}\" font-weight=\"${fontWeight}\" fill=\"${fill}\" text-anchor=\"${textAnchor}\">${tspans}</text>`;\n}\n\n// ─── Default template ─────────────────────────────────────────\n\nfunction createDefaultSVG(options: OGImageOptions): string {\n const {\n title,\n description,\n brand,\n textColor = \"#ffffff\",\n accentColor = \"#6366f1\",\n } = options;\n\n const bg1 = options.backgroundColor ?? \"#0f0c29\";\n const bg2 = options.backgroundColor ?? \"#24243e\";\n\n const titleLines = wrapText(title, 24, 2);\n const descLines = description ? wrapText(description, 55, 2) : [];\n\n const titleSize = 68;\n const descSize = 34;\n const titleLH = Math.round(titleSize * 1.35);\n const descLH = Math.round(descSize * 1.4);\n const gap = 28;\n\n const totalH =\n titleLines.length * titleLH +\n (descLines.length ? gap + descLines.length * descLH : 0);\n\n const startY = Math.round((H - totalH) / 2) + titleSize;\n const descY = startY + titleLines.length * titleLH + gap;\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<svg width=\"${W}\" height=\"${H}\" viewBox=\"0 0 ${W} ${H}\" xmlns=\"http://www.w3.org/2000/svg\">\n <defs>\n <linearGradient id=\"bg\" x1=\"0\" y1=\"0\" x2=\"${W}\" y2=\"${H}\" gradientUnits=\"userSpaceOnUse\">\n <stop offset=\"0%\" stop-color=\"${bg1}\"/>\n <stop offset=\"100%\" stop-color=\"${bg2}\"/>\n </linearGradient>\n </defs>\n <rect width=\"${W}\" height=\"${H}\" fill=\"url(#bg)\"/>\n <rect x=\"0\" y=\"0\" width=\"8\" height=\"${H}\" fill=\"${accentColor}\"/>\n <rect x=\"80\" y=\"${startY - titleSize - 24}\" width=\"56\" height=\"5\" fill=\"${accentColor}\" rx=\"2.5\"/>\n ${multilineText(titleLines, 80, startY, titleSize, textColor, \"700\")}\n ${descLines.length ? multilineText(descLines, 80, descY, descSize, \"rgba(255,255,255,0.72)\") : \"\"}\n ${brand ? `<text x=\"${W - 60}\" y=\"${H - 40}\" font-family=\"${FONT}\" font-size=\"26\" fill=\"rgba(255,255,255,0.38)\" text-anchor=\"end\">${esc(brand)}</text>` : \"\"}\n</svg>`.trim();\n}\n\n// ─── Article template ─────────────────────────────────────────\n\nfunction createArticleSVG(options: OGImageOptions): string {\n const {\n title,\n description,\n brand,\n category,\n author,\n dateString,\n accentColor = \"#e63946\",\n } = options;\n\n const bg = options.backgroundColor ?? \"#f8f9fa\";\n const textColor = options.textColor ?? \"#1a1a2e\";\n\n const titleLines = wrapText(title, 38, 3);\n const descLines = description ? wrapText(description, 68, 2) : [];\n\n const titleSize = 52;\n const descSize = 28;\n const titleLH = Math.round(titleSize * 1.3);\n\n const titleY = category ? 200 : 170;\n const descY = titleY + titleLines.length * titleLH + 24;\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<svg width=\"${W}\" height=\"${H}\" viewBox=\"0 0 ${W} ${H}\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect width=\"${W}\" height=\"${H}\" fill=\"${bg}\"/>\n <rect x=\"0\" y=\"0\" width=\"${W}\" height=\"8\" fill=\"${accentColor}\"/>\n ${\n category\n ? `<rect x=\"60\" y=\"120\" width=\"${category.length * 13 + 32}\" height=\"36\" fill=\"${accentColor}\" rx=\"18\"/>\n <text x=\"${60 + 16}\" y=\"144\" font-family=\"${FONT}\" font-size=\"18\" font-weight=\"600\" fill=\"#ffffff\">${esc(category.toUpperCase())}</text>`\n : \"\"\n }\n ${multilineText(titleLines, 60, titleY, titleSize, textColor, \"700\")}\n ${descLines.length ? multilineText(descLines, 60, descY, descSize, `${textColor}b3`) : \"\"}\n <rect x=\"60\" y=\"${H - 80}\" width=\"${W - 120}\" height=\"1\" fill=\"rgba(0,0,0,0.1)\"/>\n ${\n author\n ? `<text x=\"60\" y=\"${H - 44}\" font-family=\"${FONT}\" font-size=\"22\" fill=\"${textColor}99\">${esc(author)}</text>`\n : \"\"\n }\n ${\n dateString\n ? `<text x=\"${W - 60}\" y=\"${H - 44}\" font-family=\"${FONT}\" font-size=\"22\" fill=\"${textColor}66\" text-anchor=\"end\">${esc(dateString)}</text>`\n : \"\"\n }\n ${\n brand\n ? `<text x=\"${W / 2}\" y=\"${H - 44}\" font-family=\"${FONT}\" font-size=\"22\" font-weight=\"600\" fill=\"${accentColor}\" text-anchor=\"middle\">${esc(brand)}</text>`\n : \"\"\n }\n</svg>`.trim();\n}\n\n// ─── Sports event template ────────────────────────────────────\n\nfunction createSportsEventSVG(options: OGImageOptions): string {\n const {\n title,\n brand,\n homeTeam,\n awayTeam,\n eventDate,\n accentColor = \"#ffd700\",\n } = options;\n\n const bg1 = options.backgroundColor ?? \"#0a0a1a\";\n const bg2 = options.backgroundColor ?? \"#1a1a3e\";\n const textColor = options.textColor ?? \"#ffffff\";\n\n if (homeTeam && awayTeam) {\n const homeLines = wrapText(homeTeam.toUpperCase(), 12, 2);\n const awayLines = wrapText(awayTeam.toUpperCase(), 12, 2);\n const teamSize = 64;\n const teamY = homeLines.length > 1 ? 300 : 330;\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<svg width=\"${W}\" height=\"${H}\" viewBox=\"0 0 ${W} ${H}\" xmlns=\"http://www.w3.org/2000/svg\">\n <defs>\n <linearGradient id=\"bg\" x1=\"0\" y1=\"0\" x2=\"${W}\" y2=\"${H}\" gradientUnits=\"userSpaceOnUse\">\n <stop offset=\"0%\" stop-color=\"${bg1}\"/>\n <stop offset=\"100%\" stop-color=\"${bg2}\"/>\n </linearGradient>\n <linearGradient id=\"div\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" stop-color=\"rgba(255,215,0,0)\"/>\n <stop offset=\"50%\" stop-color=\"rgba(255,215,0,0.25)\"/>\n <stop offset=\"100%\" stop-color=\"rgba(255,215,0,0)\"/>\n </linearGradient>\n </defs>\n <rect width=\"${W}\" height=\"${H}\" fill=\"url(#bg)\"/>\n <rect x=\"591\" y=\"60\" width=\"18\" height=\"510\" fill=\"url(#div)\" rx=\"9\"/>\n ${multilineText(homeLines, 300, teamY, teamSize, textColor, \"800\", \"middle\")}\n <text x=\"600\" y=\"345\" font-family=\"${FONT}\" font-size=\"56\" font-weight=\"900\" fill=\"${accentColor}\" text-anchor=\"middle\">VS</text>\n ${multilineText(awayLines, 900, teamY, teamSize, textColor, \"800\", \"middle\")}\n ${eventDate ? `<text x=\"${W / 2}\" y=\"510\" font-family=\"${FONT}\" font-size=\"26\" fill=\"rgba(255,255,255,0.55)\" text-anchor=\"middle\">${esc(eventDate)}</text>` : \"\"}\n ${brand ? `<text x=\"${W - 60}\" y=\"${H - 36}\" font-family=\"${FONT}\" font-size=\"24\" fill=\"rgba(255,255,255,0.3)\" text-anchor=\"end\">${esc(brand)}</text>` : \"\"}\n</svg>`.trim();\n }\n\n const titleLines = wrapText(title, 28, 2);\n const titleSize = 64;\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<svg width=\"${W}\" height=\"${H}\" viewBox=\"0 0 ${W} ${H}\" xmlns=\"http://www.w3.org/2000/svg\">\n <defs>\n <linearGradient id=\"bg\" x1=\"0\" y1=\"0\" x2=\"${W}\" y2=\"${H}\" gradientUnits=\"userSpaceOnUse\">\n <stop offset=\"0%\" stop-color=\"${bg1}\"/>\n <stop offset=\"100%\" stop-color=\"${bg2}\"/>\n </linearGradient>\n </defs>\n <rect width=\"${W}\" height=\"${H}\" fill=\"url(#bg)\"/>\n <rect x=\"0\" y=\"0\" width=\"8\" height=\"${H}\" fill=\"${accentColor}\"/>\n ${multilineText(titleLines, 80, 300, titleSize, textColor, \"700\")}\n ${eventDate ? `<text x=\"80\" y=\"410\" font-family=\"${FONT}\" font-size=\"28\" fill=\"rgba(255,255,255,0.55)\">${esc(eventDate)}</text>` : \"\"}\n ${brand ? `<text x=\"${W - 60}\" y=\"${H - 36}\" font-family=\"${FONT}\" font-size=\"24\" fill=\"rgba(255,255,255,0.3)\" text-anchor=\"end\">${esc(brand)}</text>` : \"\"}\n</svg>`.trim();\n}\n\n// ─── Public API ───────────────────────────────────────────────\n\nexport function createOGImageSVG(options: OGImageOptions): string {\n switch (options.template) {\n case \"article\":\n return createArticleSVG(options);\n case \"sports-event\":\n return createSportsEventSVG(options);\n default:\n return createDefaultSVG(options);\n }\n}\n"]}
@@ -0,0 +1,5 @@
1
+ function c(e){let i=[];for(let n of e){let s=n.name??n.path,t=n.seo??{};t.title?t.title.length>60&&i.push({page:s,field:"title",message:`Title is ${t.title.length} chars (recommended \u226460)`,severity:"warning"}):i.push({page:s,field:"title",message:"Missing page title",severity:"error"}),t.description?t.description.length>160&&i.push({page:s,field:"description",message:`Description is ${t.description.length} chars (recommended \u2264160)`,severity:"warning"}):i.push({page:s,field:"description",message:"Missing meta description",severity:"error"}),t.canonical||i.push({page:s,field:"canonical",message:"Missing canonical URL",severity:"warning"}),t.openGraph?.images?.length||i.push({page:s,field:"og:image",message:"Missing og:image",severity:"warning"}),!t.openGraph?.title&&!t.title&&i.push({page:s,field:"og:title",message:"Missing og:title",severity:"warning"}),t.twitter?.card||i.push({page:s,field:"twitter:card",message:"Missing twitter:card",severity:"warning"}),t.jsonLd||i.push({page:s,field:"structured-data",message:"No structured data (JSON-LD)",severity:"warning"});}return i}function p(e){if(!e.length)return "\u2705 All pages passed SEO validation.";let i=new Map;for(let s of e){let t=i.get(s.page)??[];t.push(s),i.set(s.page,t);}let n=[];for(let[s,t]of i){n.push(`
2
+ ${s}:`);for(let a of t){let o=a.severity==="error"?"\u274C":"\u26A0\uFE0F ";n.push(` ${o} [${a.field}] ${a.message}`);}}return n.join(`
3
+ `).trim()}var r=[{name:"Title",field:"title",maxPoints:20,check:e=>e.title?e.title.length>60?{passed:true,points:10,message:`Too long (${e.title.length}/60 chars)`}:{passed:true,points:20}:{passed:false,points:0,message:"Missing title"}},{name:"Description",field:"description",maxPoints:15,check:e=>e.description?e.description.length>160?{passed:true,points:8,message:`Too long (${e.description.length}/160 chars)`}:{passed:true,points:15}:{passed:false,points:0,message:"Missing description"}},{name:"Canonical URL",field:"canonical",maxPoints:10,check:e=>e.canonical?{passed:true,points:10}:{passed:false,points:0,message:"Missing canonical URL"}},{name:"og:image",field:"og:image",maxPoints:15,check:e=>e.openGraph?.images?.length?{passed:true,points:15}:{passed:false,points:0,message:"Missing og:image"}},{name:"og:title",field:"og:title",maxPoints:5,check:e=>e.openGraph?.title||e.title?{passed:true,points:5}:{passed:false,points:0,message:"Missing og:title"}},{name:"og:description",field:"og:description",maxPoints:5,check:e=>e.openGraph?.description||e.description?{passed:true,points:5}:{passed:false,points:0,message:"Missing og:description"}},{name:"Twitter Card",field:"twitter:card",maxPoints:10,check:e=>e.twitter?.card?{passed:true,points:10}:{passed:false,points:0,message:"Missing twitter:card"}},{name:"Structured Data",field:"structured-data",maxPoints:10,check:e=>e.jsonLd?{passed:true,points:10}:{passed:false,points:0,message:"No JSON-LD structured data"}},{name:"Robots Directives",field:"robots",maxPoints:5,check:e=>e.robots!==void 0?{passed:true,points:5}:{passed:false,points:0,message:"No robots directives"}},{name:"Hreflang",field:"hreflang",maxPoints:5,check:e=>e.alternates?.length?{passed:true,points:5}:{passed:false,points:0,message:"No hreflang alternates"}}];function g(e,i="Page"){let n=0,s=r.reduce((a,o)=>a+o.maxPoints,0),t=[];for(let a of r){let o=a.check(e);n+=o.points,t.push({name:a.name,passed:o.passed,points:o.points,maxPoints:a.maxPoints,message:o.message});}return {page:i,score:n,maxScore:s,percentage:Math.round(n/s*100),checks:t}}function d(e){let n=[`${e.percentage>=80?"\u{1F7E2}":e.percentage>=50?"\u{1F7E1}":"\u{1F534}"} SEO Score: ${e.page} ${e.score}/${e.maxScore} (${e.percentage}%)`];for(let s of e.checks){let t=s.passed?"\u2705":"\u274C",a=s.message?` \u2014 ${s.message}`:"",o=s.passed?"":` (-${s.maxPoints})`;n.push(` ${t} ${s.name}${o}${a}`);}return n.join(`
4
+ `)}export{c as a,p as b,g as c,d};//# sourceMappingURL=chunk-X535MU7Z.js.map
5
+ //# sourceMappingURL=chunk-X535MU7Z.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/validation/index.ts"],"names":["validateSEO","routes","issues","route","page","seo","printValidationReport","byPage","issue","list","lines","pageIssues","icon","SCORE_CHECKS","c","getSEOScore","config","pageName","score","maxScore","sum","checks","def","result","formatSEOScore","check","detail","pts"],"mappings":"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","file":"chunk-X535MU7Z.js","sourcesContent":["import type {\n SEOConfig,\n SEOValidationIssue,\n RouteWithSEO,\n SEOScoreResult,\n SEOScoreCheck,\n} from \"../types/index.js\";\n\n// ─── Validate SEO ─────────────────────────────────────────────\n\nexport function validateSEO(routes: RouteWithSEO[]): SEOValidationIssue[] {\n const issues: SEOValidationIssue[] = [];\n\n for (const route of routes) {\n const page = route.name ?? route.path;\n const seo = route.seo ?? {};\n\n if (!seo.title) {\n issues.push({ page, field: \"title\", message: \"Missing page title\", severity: \"error\" });\n } else if (seo.title.length > 60) {\n issues.push({\n page,\n field: \"title\",\n message: `Title is ${seo.title.length} chars (recommended ≤60)`,\n severity: \"warning\",\n });\n }\n\n if (!seo.description) {\n issues.push({ page, field: \"description\", message: \"Missing meta description\", severity: \"error\" });\n } else if (seo.description.length > 160) {\n issues.push({\n page,\n field: \"description\",\n message: `Description is ${seo.description.length} chars (recommended ≤160)`,\n severity: \"warning\",\n });\n }\n\n if (!seo.canonical) {\n issues.push({ page, field: \"canonical\", message: \"Missing canonical URL\", severity: \"warning\" });\n }\n\n if (!seo.openGraph?.images?.length) {\n issues.push({ page, field: \"og:image\", message: \"Missing og:image\", severity: \"warning\" });\n }\n\n if (!seo.openGraph?.title && !seo.title) {\n issues.push({ page, field: \"og:title\", message: \"Missing og:title\", severity: \"warning\" });\n }\n\n if (!seo.twitter?.card) {\n issues.push({ page, field: \"twitter:card\", message: \"Missing twitter:card\", severity: \"warning\" });\n }\n\n if (!seo.jsonLd) {\n issues.push({\n page,\n field: \"structured-data\",\n message: \"No structured data (JSON-LD)\",\n severity: \"warning\",\n });\n }\n }\n\n return issues;\n}\n\nexport function printValidationReport(issues: SEOValidationIssue[]): string {\n if (!issues.length) return \"✅ All pages passed SEO validation.\";\n\n const byPage = new Map<string, SEOValidationIssue[]>();\n for (const issue of issues) {\n const list = byPage.get(issue.page) ?? [];\n list.push(issue);\n byPage.set(issue.page, list);\n }\n\n const lines: string[] = [];\n for (const [page, pageIssues] of byPage) {\n lines.push(`\\n${page}:`);\n for (const issue of pageIssues) {\n const icon = issue.severity === \"error\" ? \"❌\" : \"⚠️ \";\n lines.push(` ${icon} [${issue.field}] ${issue.message}`);\n }\n }\n return lines.join(\"\\n\").trim();\n}\n\n// ─── SEO Score ────────────────────────────────────────────────\n\nconst SCORE_CHECKS: Array<{\n name: string;\n field: string;\n maxPoints: number;\n check: (config: SEOConfig) => { passed: boolean; points: number; message?: string };\n}> = [\n {\n name: \"Title\",\n field: \"title\",\n maxPoints: 20,\n check: (c) => {\n if (!c.title) return { passed: false, points: 0, message: \"Missing title\" };\n if (c.title.length > 60)\n return { passed: true, points: 10, message: `Too long (${c.title.length}/60 chars)` };\n return { passed: true, points: 20 };\n },\n },\n {\n name: \"Description\",\n field: \"description\",\n maxPoints: 15,\n check: (c) => {\n if (!c.description) return { passed: false, points: 0, message: \"Missing description\" };\n if (c.description.length > 160)\n return { passed: true, points: 8, message: `Too long (${c.description.length}/160 chars)` };\n return { passed: true, points: 15 };\n },\n },\n {\n name: \"Canonical URL\",\n field: \"canonical\",\n maxPoints: 10,\n check: (c) =>\n c.canonical\n ? { passed: true, points: 10 }\n : { passed: false, points: 0, message: \"Missing canonical URL\" },\n },\n {\n name: \"og:image\",\n field: \"og:image\",\n maxPoints: 15,\n check: (c) =>\n c.openGraph?.images?.length\n ? { passed: true, points: 15 }\n : { passed: false, points: 0, message: \"Missing og:image\" },\n },\n {\n name: \"og:title\",\n field: \"og:title\",\n maxPoints: 5,\n check: (c) =>\n c.openGraph?.title || c.title\n ? { passed: true, points: 5 }\n : { passed: false, points: 0, message: \"Missing og:title\" },\n },\n {\n name: \"og:description\",\n field: \"og:description\",\n maxPoints: 5,\n check: (c) =>\n c.openGraph?.description || c.description\n ? { passed: true, points: 5 }\n : { passed: false, points: 0, message: \"Missing og:description\" },\n },\n {\n name: \"Twitter Card\",\n field: \"twitter:card\",\n maxPoints: 10,\n check: (c) =>\n c.twitter?.card\n ? { passed: true, points: 10 }\n : { passed: false, points: 0, message: \"Missing twitter:card\" },\n },\n {\n name: \"Structured Data\",\n field: \"structured-data\",\n maxPoints: 10,\n check: (c) =>\n c.jsonLd\n ? { passed: true, points: 10 }\n : { passed: false, points: 0, message: \"No JSON-LD structured data\" },\n },\n {\n name: \"Robots Directives\",\n field: \"robots\",\n maxPoints: 5,\n check: (c) =>\n c.robots !== undefined\n ? { passed: true, points: 5 }\n : { passed: false, points: 0, message: \"No robots directives\" },\n },\n {\n name: \"Hreflang\",\n field: \"hreflang\",\n maxPoints: 5,\n check: (c) =>\n c.alternates?.length\n ? { passed: true, points: 5 }\n : { passed: false, points: 0, message: \"No hreflang alternates\" },\n },\n];\n\nexport function getSEOScore(config: SEOConfig, pageName = \"Page\"): SEOScoreResult {\n let score = 0;\n const maxScore = SCORE_CHECKS.reduce((sum, c) => sum + c.maxPoints, 0);\n const checks: SEOScoreCheck[] = [];\n\n for (const def of SCORE_CHECKS) {\n const result = def.check(config);\n score += result.points;\n checks.push({\n name: def.name,\n passed: result.passed,\n points: result.points,\n maxPoints: def.maxPoints,\n message: result.message,\n });\n }\n\n return {\n page: pageName,\n score,\n maxScore,\n percentage: Math.round((score / maxScore) * 100),\n checks,\n };\n}\n\nexport function formatSEOScore(result: SEOScoreResult): string {\n const bar = result.percentage >= 80 ? \"🟢\" : result.percentage >= 50 ? \"🟡\" : \"🔴\";\n const lines = [\n `${bar} SEO Score: ${result.page} ${result.score}/${result.maxScore} (${result.percentage}%)`,\n ];\n\n for (const check of result.checks) {\n const icon = check.passed ? \"✅\" : \"❌\";\n const detail = check.message ? ` — ${check.message}` : \"\";\n const pts = !check.passed ? ` (-${check.maxPoints})` : \"\";\n lines.push(` ${icon} ${check.name}${pts}${detail}`);\n }\n\n return lines.join(\"\\n\");\n}\n"]}
@@ -0,0 +1,5 @@
1
+ 'use strict';function d(s){return s.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")}function h(s,t){return t.endsWith("/*")?s.startsWith(t.slice(0,-2)+"/")||s===t.slice(0,-2):t.endsWith("*")?s.startsWith(t.slice(0,-1)):t.startsWith("*")?s.endsWith(t.slice(1)):s===t}function w(s){let{routes:t,baseUrl:o,exclude:i=[],defaultChangefreq:n,defaultPriority:c}=s,e=o.replace(/\/+$/,"");return ['<?xml version="1.0" encoding="UTF-8"?>','<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',...t.map(r=>typeof r=="string"?{path:r}:r).filter(r=>!i.some(m=>h(r.path,m))).map(r=>{let m=r.path==="/"?e:`${e}${r.path}`,p=[" <url>",` <loc>${d(m)}</loc>`,` <lastmod>${r.lastmod??new Date().toISOString().split("T")[0]}</lastmod>`],g=r.changefreq??n;g&&p.push(` <changefreq>${g}</changefreq>`);let f=r.priority??c;return f!==void 0&&p.push(` <priority>${f.toFixed(1)}</priority>`),p.push(" </url>"),p.join(`
2
+ `)}),"</urlset>"].join(`
3
+ `)}function b(s){let{rules:t=[],sitemap:o}=s,i=Array.isArray(t)?t:[t],n=[];for(let e of i){let a=e.userAgent?Array.isArray(e.userAgent)?e.userAgent:[e.userAgent]:["*"];for(let r of a)n.push(`User-agent: ${r}`);let l=e.allow?Array.isArray(e.allow)?e.allow:[e.allow]:[];for(let r of l)n.push(`Allow: ${r}`);let u=e.disallow?Array.isArray(e.disallow)?e.disallow:[e.disallow]:[];for(let r of u)n.push(`Disallow: ${r}`);e.crawlDelay!==void 0&&n.push(`Crawl-delay: ${e.crawlDelay}`),n.push("");}let c=o?Array.isArray(o)?o:[o]:[];for(let e of c)n.push(`Sitemap: ${e}`);return n.join(`
4
+ `).trim()}function y(s){return s.replace(/[-_]/g," ").replace(/\b\w/g,t=>t.toUpperCase())}function A(s,t={}){let{baseUrl:o="",labels:i={},formatLabel:n=y}=t,c=s.split("/").filter(Boolean),e=[{name:i[""]??i["/"]??"Home",url:`${o}/`}],a="";for(let l of c){a+=`/${l}`;let u=i[a]??i[l]??n(l);e.push({name:u,url:`${o}${a}`});}return e}exports.a=w;exports.b=b;exports.c=A;//# sourceMappingURL=chunk-ZADUJHVR.cjs.map
5
+ //# sourceMappingURL=chunk-ZADUJHVR.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/sitemap/index.ts"],"names":["escapeXml","str","matchesExclude","path","pattern","generateSitemap","options","routes","baseUrl","exclude","defaultChangefreq","defaultPriority","normalizedBase","route","loc","lines","changefreq","priority","generateRobots","rules","sitemap","normalizedRules","rule","agents","agent","allows","disallows","sitemaps","sm","defaultFormatLabel","segment","c","autoBreadcrumb","currentPath","labels","formatLabel","segments","items","accPath","name"],"mappings":"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","file":"chunk-ZADUJHVR.cjs","sourcesContent":["import type {\n BreadcrumbItem,\n GenerateSitemapOptions,\n GenerateRobotsOptions,\n SitemapRoute,\n} from \"../types/index.js\";\n\n// ─── Sitemap ──────────────────────────────────────────────────\n\nfunction escapeXml(str: string): string {\n return str\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&apos;\");\n}\n\nfunction matchesExclude(path: string, pattern: string): boolean {\n if (pattern.endsWith(\"/*\")) {\n return path.startsWith(pattern.slice(0, -2) + \"/\") || path === pattern.slice(0, -2);\n }\n if (pattern.endsWith(\"*\")) {\n return path.startsWith(pattern.slice(0, -1));\n }\n if (pattern.startsWith(\"*\")) {\n return path.endsWith(pattern.slice(1));\n }\n return path === pattern;\n}\n\nexport function generateSitemap(options: GenerateSitemapOptions): string {\n const { routes, baseUrl, exclude = [], defaultChangefreq, defaultPriority } = options;\n const normalizedBase = baseUrl.replace(/\\/+$/, \"\");\n\n const normalized: SitemapRoute[] = routes.map((r) =>\n typeof r === \"string\" ? { path: r } : r\n );\n\n const filtered = normalized.filter(\n (route) => !exclude.some((pattern) => matchesExclude(route.path, pattern))\n );\n\n const urlEntries = filtered.map((route) => {\n const loc = route.path === \"/\" ? normalizedBase : `${normalizedBase}${route.path}`;\n const lines: string[] = [\n ` <url>`,\n ` <loc>${escapeXml(loc)}</loc>`,\n ` <lastmod>${route.lastmod ?? new Date().toISOString().split(\"T\")[0]}</lastmod>`,\n ];\n\n const changefreq = route.changefreq ?? defaultChangefreq;\n if (changefreq) lines.push(` <changefreq>${changefreq}</changefreq>`);\n\n const priority = route.priority ?? defaultPriority;\n if (priority !== undefined) lines.push(` <priority>${priority.toFixed(1)}</priority>`);\n\n lines.push(` </url>`);\n return lines.join(\"\\n\");\n });\n\n return [\n `<?xml version=\"1.0\" encoding=\"UTF-8\"?>`,\n `<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">`,\n ...urlEntries,\n `</urlset>`,\n ].join(\"\\n\");\n}\n\n// ─── Robots.txt ───────────────────────────────────────────────\n\nexport function generateRobots(options: GenerateRobotsOptions): string {\n const { rules = [], sitemap } = options;\n const normalizedRules = Array.isArray(rules) ? rules : [rules];\n const lines: string[] = [];\n\n for (const rule of normalizedRules) {\n const agents = rule.userAgent\n ? Array.isArray(rule.userAgent)\n ? rule.userAgent\n : [rule.userAgent]\n : [\"*\"];\n\n for (const agent of agents) {\n lines.push(`User-agent: ${agent}`);\n }\n\n const allows = rule.allow\n ? Array.isArray(rule.allow)\n ? rule.allow\n : [rule.allow]\n : [];\n for (const path of allows) lines.push(`Allow: ${path}`);\n\n const disallows = rule.disallow\n ? Array.isArray(rule.disallow)\n ? rule.disallow\n : [rule.disallow]\n : [];\n for (const path of disallows) lines.push(`Disallow: ${path}`);\n\n if (rule.crawlDelay !== undefined) lines.push(`Crawl-delay: ${rule.crawlDelay}`);\n\n lines.push(\"\");\n }\n\n const sitemaps = sitemap\n ? Array.isArray(sitemap)\n ? sitemap\n : [sitemap]\n : [];\n for (const sm of sitemaps) lines.push(`Sitemap: ${sm}`);\n\n return lines.join(\"\\n\").trim();\n}\n\n// ─── Auto Breadcrumb ──────────────────────────────────────────\n\nexport interface AutoBreadcrumbOptions {\n baseUrl?: string;\n labels?: Record<string, string>;\n formatLabel?: (segment: string) => string;\n}\n\nfunction defaultFormatLabel(segment: string): string {\n return segment\n .replace(/[-_]/g, \" \")\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n\nexport function autoBreadcrumb(\n currentPath: string,\n options: AutoBreadcrumbOptions = {}\n): BreadcrumbItem[] {\n const { baseUrl = \"\", labels = {}, formatLabel = defaultFormatLabel } = options;\n\n const segments = currentPath.split(\"/\").filter(Boolean);\n const items: BreadcrumbItem[] = [\n { name: labels[\"\"] ?? labels[\"/\"] ?? \"Home\", url: `${baseUrl}/` },\n ];\n\n let accPath = \"\";\n for (const segment of segments) {\n accPath += `/${segment}`;\n const name = labels[accPath] ?? labels[segment] ?? formatLabel(segment);\n items.push({ name, url: `${baseUrl}${accPath}` });\n }\n\n return items;\n}\n"]}