@geenius/seo 0.1.0 → 0.3.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 (126) hide show
  1. package/package.json +16 -3
  2. package/packages/convex/dist/index.d.ts +56 -0
  3. package/packages/convex/dist/index.js +133 -0
  4. package/packages/convex/dist/index.js.map +1 -0
  5. package/packages/react/README.md +1 -1
  6. package/packages/react/dist/index.d.ts +156 -0
  7. package/packages/react/dist/index.js +567 -0
  8. package/packages/react/dist/index.js.map +1 -0
  9. package/packages/react-css/README.md +1 -1
  10. package/packages/react-css/dist/index.cjs +571 -0
  11. package/packages/react-css/dist/index.cjs.map +1 -0
  12. package/packages/react-css/{src/seo.css → dist/index.css} +7 -153
  13. package/packages/react-css/dist/index.css.map +1 -0
  14. package/packages/react-css/dist/index.d.cts +53 -0
  15. package/packages/react-css/dist/index.d.ts +53 -0
  16. package/packages/react-css/dist/index.js +539 -0
  17. package/packages/react-css/dist/index.js.map +1 -0
  18. package/packages/shared/README.md +1 -1
  19. package/packages/shared/dist/index.d.ts +262 -0
  20. package/packages/shared/dist/index.js +381 -0
  21. package/packages/shared/dist/index.js.map +1 -0
  22. package/packages/solidjs/README.md +1 -1
  23. package/packages/solidjs/dist/index.d.ts +133 -0
  24. package/packages/solidjs/dist/index.js +416 -0
  25. package/packages/solidjs/dist/index.js.map +1 -0
  26. package/packages/solidjs-css/README.md +1 -1
  27. package/packages/solidjs-css/dist/index.cjs +399 -0
  28. package/packages/solidjs-css/dist/index.cjs.map +1 -0
  29. package/packages/solidjs-css/{src/seo.css → dist/index.css} +7 -153
  30. package/packages/solidjs-css/dist/index.css.map +1 -0
  31. package/packages/solidjs-css/dist/index.d.cts +53 -0
  32. package/packages/solidjs-css/dist/index.d.ts +53 -0
  33. package/packages/solidjs-css/dist/index.js +367 -0
  34. package/packages/solidjs-css/dist/index.js.map +1 -0
  35. package/.changeset/config.json +0 -11
  36. package/.github/CODEOWNERS +0 -1
  37. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -16
  38. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -11
  39. package/.github/PULL_REQUEST_TEMPLATE.md +0 -10
  40. package/.github/dependabot.yml +0 -11
  41. package/.github/workflows/ci.yml +0 -23
  42. package/.github/workflows/release.yml +0 -29
  43. package/.nvmrc +0 -1
  44. package/.project/ACCOUNT.yaml +0 -4
  45. package/.project/IDEAS.yaml +0 -7
  46. package/.project/PROJECT.yaml +0 -11
  47. package/.project/ROADMAP.yaml +0 -15
  48. package/CODE_OF_CONDUCT.md +0 -16
  49. package/CONTRIBUTING.md +0 -26
  50. package/SECURITY.md +0 -15
  51. package/SUPPORT.md +0 -8
  52. package/packages/convex/package.json +0 -42
  53. package/packages/convex/src/functions.ts +0 -5
  54. package/packages/convex/src/mutations.ts +0 -83
  55. package/packages/convex/src/queries.ts +0 -57
  56. package/packages/convex/src/schema.ts +0 -23
  57. package/packages/convex/tsconfig.json +0 -19
  58. package/packages/convex/tsup.config.ts +0 -18
  59. package/packages/react/package.json +0 -49
  60. package/packages/react/src/components/ArticleJsonLd.tsx +0 -42
  61. package/packages/react/src/components/BreadcrumbsJsonLd.tsx +0 -24
  62. package/packages/react/src/components/MetaEditor.tsx +0 -147
  63. package/packages/react/src/components/SEOHead.tsx +0 -107
  64. package/packages/react/src/components/SEOPreview.tsx +0 -42
  65. package/packages/react/src/components/SEOScoreCard.tsx +0 -51
  66. package/packages/react/src/components/SitemapViewer.tsx +0 -36
  67. package/packages/react/src/components/index.ts +0 -7
  68. package/packages/react/src/hooks/index.ts +0 -4
  69. package/packages/react/src/hooks/useSEO.ts +0 -27
  70. package/packages/react/src/hooks/useSEOAdmin.ts +0 -42
  71. package/packages/react/src/hooks/useSEOScore.ts +0 -7
  72. package/packages/react/src/hooks/useSitemap.ts +0 -8
  73. package/packages/react/src/index.ts +0 -51
  74. package/packages/react/src/index.tsx +0 -11
  75. package/packages/react/src/pages/SEOAdminPage.tsx +0 -101
  76. package/packages/react/src/pages/SEOAnalyticsPage.tsx +0 -96
  77. package/packages/react/src/pages/index.ts +0 -2
  78. package/packages/react/tsconfig.json +0 -19
  79. package/packages/react/tsup.config.ts +0 -12
  80. package/packages/react-css/package.json +0 -36
  81. package/packages/react-css/src/components/ArticleJsonLd.tsx +0 -42
  82. package/packages/react-css/src/components/BreadcrumbsJsonLd.tsx +0 -24
  83. package/packages/react-css/src/components/MetaEditor.tsx +0 -147
  84. package/packages/react-css/src/components/SEOHead.tsx +0 -95
  85. package/packages/react-css/src/components/SEOPreview.tsx +0 -42
  86. package/packages/react-css/src/components/SEOScoreCard.tsx +0 -42
  87. package/packages/react-css/src/components/SitemapViewer.tsx +0 -36
  88. package/packages/react-css/src/components/index.ts +0 -7
  89. package/packages/react-css/src/index.ts +0 -9
  90. package/packages/react-css/src/pages/SEOAdminPage.tsx +0 -88
  91. package/packages/react-css/src/pages/SEOAnalyticsPage.tsx +0 -82
  92. package/packages/react-css/src/pages/index.ts +0 -2
  93. package/packages/react-css/tsup.config.ts +0 -2
  94. package/packages/shared/package.json +0 -42
  95. package/packages/shared/src/__tests__/seo.test.ts +0 -70
  96. package/packages/shared/src/config.ts +0 -297
  97. package/packages/shared/src/index.ts +0 -207
  98. package/packages/shared/tsconfig.json +0 -18
  99. package/packages/shared/tsup.config.ts +0 -11
  100. package/packages/shared/vitest.config.ts +0 -4
  101. package/packages/solidjs/package.json +0 -45
  102. package/packages/solidjs/src/components/ArticleJsonLd.tsx +0 -35
  103. package/packages/solidjs/src/components/BreadcrumbsJsonLd.tsx +0 -24
  104. package/packages/solidjs/src/components/MetaEditor.tsx +0 -155
  105. package/packages/solidjs/src/components/SEOHead.tsx +0 -109
  106. package/packages/solidjs/src/components/SEOPreview.tsx +0 -42
  107. package/packages/solidjs/src/components/SEOScoreCard.tsx +0 -57
  108. package/packages/solidjs/src/components/SitemapViewer.tsx +0 -44
  109. package/packages/solidjs/src/components/index.ts +0 -7
  110. package/packages/solidjs/src/index.ts +0 -11
  111. package/packages/solidjs/src/pages/SEOAdminPage.tsx +0 -104
  112. package/packages/solidjs/src/pages/SEOAnalyticsPage.tsx +0 -102
  113. package/packages/solidjs/src/pages/index.ts +0 -2
  114. package/packages/solidjs/src/primitives/index.ts +0 -4
  115. package/packages/solidjs/src/primitives/useSEO.ts +0 -27
  116. package/packages/solidjs/src/primitives/useSEOAdmin.ts +0 -42
  117. package/packages/solidjs/src/primitives/useSEOScore.ts +0 -7
  118. package/packages/solidjs/src/primitives/useSitemap.ts +0 -8
  119. package/packages/solidjs/tsconfig.json +0 -20
  120. package/packages/solidjs/tsup.config.ts +0 -12
  121. package/packages/solidjs-css/package.json +0 -35
  122. package/packages/solidjs-css/src/index.ts +0 -5
  123. package/packages/solidjs-css/src/primitives/index.ts +0 -1
  124. package/packages/solidjs-css/tsup.config.ts +0 -2
  125. package/pnpm-workspace.yaml +0 -2
  126. package/tsconfig.json +0 -23
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/components/SEOHead.tsx","../src/components/SEOPreview.tsx","../src/components/SEOScoreCard.tsx","../src/components/MetaEditor.tsx","../src/components/SitemapViewer.tsx","../src/components/BreadcrumbsJsonLd.tsx","../src/components/ArticleJsonLd.tsx","../src/pages/SEOAdminPage.tsx","../src/pages/SEOAnalyticsPage.tsx"],"sourcesContent":["// Re-export shared types, utilities, hooks, and provider from react package\n// (components are overridden by CSS-styled versions below)\nexport {\n // Types\n type OGMeta,\n type TwitterMeta,\n type SEOMeta,\n type SitemapEntry,\n type RobotsTxt,\n type SEOConfig,\n // Utilities\n buildTitle,\n applyTitleTemplate,\n truncateDescription,\n buildCanonical,\n buildAlternates,\n estimateReadingTime,\n calcSEOScore,\n generateRobotsTxt,\n generateSitemapXml,\n articleSchema,\n productSchema,\n orgSchema,\n breadcrumbSchema,\n faqSchema,\n webApplicationSchema,\n // Config\n SEOConfigBuilder,\n createSEOConfig,\n configureSEO,\n PageMetaBuilder,\n createPageMeta,\n validateSEOMeta,\n seoPresets,\n // Provider\n SeoProvider,\n useSEOContext,\n type SeoProviderProps,\n // Hooks\n useSEO,\n useSEOAdmin,\n useSEOScore,\n useSitemap,\n useCanonical,\n useStructuredData,\n} from '@geenius/seo-react'\n\n// CSS-styled components (override headless versions)\nexport * from './components'\nexport * from './pages'\n\n// CSS Styles\nimport './seo.css'\n","import { useEffect } from 'react'\nimport type { SEOMeta } from '@geenius/seo-shared'\n\ninterface SEOHeadProps {\n meta: SEOMeta\n}\n\nexport function SEOHead({ meta }: SEOHeadProps) {\n useEffect(() => {\n document.title = meta.title\n\n const setMetaTag = (name: string, content: string, isProperty = false) => {\n let tag = document.querySelector(\n `meta[${isProperty ? 'property' : 'name'}=\"${name}\"]`,\n ) as HTMLMetaElement\n if (!tag) {\n tag = document.createElement('meta')\n isProperty ? tag.setAttribute('property', name) : tag.setAttribute('name', name)\n document.head.appendChild(tag)\n }\n tag.content = content\n }\n\n setMetaTag('description', meta.description)\n if (meta.keywords.length > 0) {\n setMetaTag('keywords', meta.keywords.join(', '))\n }\n if (meta.robots) {\n setMetaTag('robots', meta.robots)\n }\n\n setMetaTag('og:title', meta.og.title, true)\n setMetaTag('og:description', meta.og.description, true)\n if (meta.og.image) {\n setMetaTag('og:image', meta.og.image, true)\n if (meta.og.imageAlt) {\n setMetaTag('og:image:alt', meta.og.imageAlt, true)\n }\n }\n setMetaTag('og:type', meta.og.type, true)\n setMetaTag('og:url', meta.og.url, true)\n if (meta.og.siteName) {\n setMetaTag('og:site_name', meta.og.siteName, true)\n }\n\n setMetaTag('twitter:card', meta.twitter.card)\n setMetaTag('twitter:title', meta.twitter.title)\n setMetaTag('twitter:description', meta.twitter.description)\n if (meta.twitter.image) {\n setMetaTag('twitter:image', meta.twitter.image)\n }\n if (meta.twitter.creator) {\n setMetaTag('twitter:creator', meta.twitter.creator)\n }\n\n if (meta.canonical) {\n let canonicalLink = document.querySelector('link[rel=\"canonical\"]') as HTMLLinkElement\n if (!canonicalLink) {\n canonicalLink = document.createElement('link')\n canonicalLink.rel = 'canonical'\n document.head.appendChild(canonicalLink)\n }\n canonicalLink.href = meta.canonical\n }\n\n if (meta.alternates) {\n Object.entries(meta.alternates).forEach(([lang, url]) => {\n let altLink = document.querySelector(\n `link[rel=\"alternate\"][hreflang=\"${lang}\"]`,\n ) as HTMLLinkElement\n if (!altLink) {\n altLink = document.createElement('link')\n altLink.rel = 'alternate'\n altLink.setAttribute('hreflang', lang)\n document.head.appendChild(altLink)\n }\n altLink.href = url\n })\n }\n\n if (meta.jsonLd && meta.jsonLd.length > 0) {\n meta.jsonLd.forEach((schema) => {\n let script = document.querySelector('script[type=\"application/ld+json\"]') as HTMLScriptElement\n if (!script) {\n script = document.createElement('script')\n script.type = 'application/ld+json'\n document.head.appendChild(script)\n }\n script.textContent = JSON.stringify(schema)\n })\n }\n }, [meta])\n\n return <div className=\"seo__head\" />\n}\n","import type { SEOMeta } from '@geenius/seo-shared'\n\ninterface SEOPreviewProps {\n meta: SEOMeta\n}\n\nexport function SEOPreview({ meta }: SEOPreviewProps) {\n return (\n <div className=\"seo__preview\">\n <div>\n <h3 style={{ fontSize: '0.875rem', fontWeight: 600, marginBottom: '0.5rem' }}>\n Google Search Preview\n </h3>\n <div className=\"seo__preview-google\">\n <div className=\"seo__preview-google-url\">{new URL(meta.og.url).hostname}</div>\n <div className=\"seo__preview-google-title\">{meta.title}</div>\n <div className=\"seo__preview-google-desc\">{meta.description}</div>\n </div>\n </div>\n\n <div>\n <h3 style={{ fontSize: '0.875rem', fontWeight: 600, marginBottom: '0.5rem' }}>\n Social Media Preview\n </h3>\n <div className=\"seo__preview-social\">\n {meta.og.image && (\n <img\n src={meta.og.image}\n alt={meta.og.imageAlt || meta.og.title}\n className=\"seo__preview-social-image\"\n />\n )}\n <div className=\"seo__preview-social-info\">\n <div className=\"seo__preview-social-title\">{meta.og.title}</div>\n <div className=\"seo__preview-social-desc\">{meta.og.description}</div>\n <div className=\"seo__preview-social-url\">{meta.og.url}</div>\n </div>\n </div>\n </div>\n </div>\n )\n}\n","import { useSEOScore } from '@geenius/seo-react'\nimport type { SEOMeta } from '@geenius/seo-shared'\n\ninterface SEOScoreCardProps {\n meta: SEOMeta\n}\n\nexport function SEOScoreCard({ meta }: SEOScoreCardProps) {\n const { score, issues } = useSEOScore(meta)\n\n const scoreClass =\n score >= 80 ? 'seo__score-number--good' : score >= 50 ? 'seo__score-number--fair' : 'seo__score-number--poor'\n const barClass =\n score >= 80 ? 'seo__score-bar-fill--good' : score >= 50 ? 'seo__score-bar-fill--fair' : 'seo__score-bar-fill--poor'\n\n return (\n <div className=\"seo__score-card\">\n <div className=\"seo__score-card-header\">\n <div className=\"seo__score-card-title\">SEO Score</div>\n <span className={`seo__score-number ${scoreClass}`}>{score}/100</span>\n </div>\n\n <div className=\"seo__score-bar\">\n <div className={`seo__score-bar-fill ${barClass}`} style={{ width: `${score}%` }} />\n </div>\n\n {issues.length > 0 && (\n <div>\n <div className=\"seo__issue-list-title\">Issues Found</div>\n <ul className=\"seo__issue-list\">\n {issues.map((issue, i) => (\n <li key={i} className=\"seo__issue-item\">\n <span className=\"seo__issue-item-bullet\">•</span>\n {issue}\n </li>\n ))}\n </ul>\n </div>\n )}\n </div>\n )\n}\n","import { useState } from 'react'\nimport type { SEOMeta } from '@geenius/seo-shared'\n\ninterface MetaEditorProps {\n meta: SEOMeta\n onChange: (meta: SEOMeta) => void\n}\n\nexport function MetaEditor({ meta, onChange }: MetaEditorProps) {\n const [localMeta, setLocalMeta] = useState(meta)\n\n const handleChange = (updates: Partial<SEOMeta>) => {\n const updated = { ...localMeta, ...updates }\n setLocalMeta(updated)\n onChange(updated)\n }\n\n return (\n <div className=\"seo__meta-editor\">\n <div className=\"seo__meta-field\">\n <label className=\"seo__meta-field-label\">\n Title ({localMeta.title.length}/60)\n </label>\n <input\n type=\"text\"\n value={localMeta.title}\n onChange={(e) =>\n handleChange({\n title: e.target.value.slice(0, 60),\n })\n }\n className=\"seo__meta-field-input\"\n placeholder=\"Page title\"\n />\n <div className=\"seo__meta-char-count\">\n {localMeta.title.length < 30\n ? 'Too short'\n : localMeta.title.length > 60\n ? 'Too long'\n : 'Good length'}\n </div>\n </div>\n\n <div className=\"seo__meta-field\">\n <label className=\"seo__meta-field-label\">\n Description ({localMeta.description.length}/160)\n </label>\n <textarea\n value={localMeta.description}\n onChange={(e) =>\n handleChange({\n description: e.target.value.slice(0, 160),\n })\n }\n className=\"seo__meta-field-textarea\"\n placeholder=\"Page description\"\n />\n <div className=\"seo__meta-char-count\">\n {localMeta.description.length < 50\n ? 'Too short'\n : localMeta.description.length > 160\n ? 'Too long'\n : 'Good length'}\n </div>\n </div>\n\n <div className=\"seo__meta-field\">\n <label className=\"seo__meta-field-label\">Keywords</label>\n <input\n type=\"text\"\n value={localMeta.keywords.join(', ')}\n onChange={(e) =>\n handleChange({\n keywords: e.target.value.split(',').map((k) => k.trim()),\n })\n }\n className=\"seo__meta-field-input\"\n placeholder=\"keyword1, keyword2, keyword3\"\n />\n </div>\n\n <div className=\"seo__meta-field\">\n <label className=\"seo__meta-field-label\">Canonical URL</label>\n <input\n type=\"url\"\n value={localMeta.canonical || ''}\n onChange={(e) =>\n handleChange({\n canonical: e.target.value,\n })\n }\n className=\"seo__meta-field-input\"\n placeholder=\"https://example.com/page\"\n />\n </div>\n\n <div className=\"seo__meta-field\">\n <label className=\"seo__meta-field-label\">OG Image URL</label>\n <input\n type=\"url\"\n value={localMeta.og.image || ''}\n onChange={(e) =>\n handleChange({\n og: { ...localMeta.og, image: e.target.value },\n })\n }\n className=\"seo__meta-field-input\"\n placeholder=\"https://example.com/image.jpg\"\n />\n </div>\n\n <div className=\"seo__meta-field\">\n <label className=\"seo__meta-field-label\">Twitter Card Type</label>\n <select\n value={localMeta.twitter.card}\n onChange={(e) =>\n handleChange({\n twitter: {\n ...localMeta.twitter,\n card: e.target.value as 'summary' | 'summary_large_image',\n },\n })\n }\n className=\"seo__meta-field-select\"\n >\n <option value=\"summary\">Summary</option>\n <option value=\"summary_large_image\">Summary Large Image</option>\n </select>\n </div>\n\n <div className=\"seo__meta-field\">\n <label className=\"seo__meta-field-label\">Robots Directive</label>\n <input\n type=\"text\"\n value={localMeta.robots || ''}\n onChange={(e) =>\n handleChange({\n robots: e.target.value,\n })\n }\n className=\"seo__meta-field-input\"\n placeholder=\"index, follow\"\n />\n </div>\n </div>\n )\n}\n","import type { SitemapEntry } from '@geenius/seo-shared'\n\ninterface SitemapViewerProps {\n entries: SitemapEntry[]\n}\n\nexport function SitemapViewer({ entries }: SitemapViewerProps) {\n return (\n <div>\n <table className=\"seo__sitemap-table\">\n <thead className=\"seo__sitemap-table-head\">\n <tr>\n <th className=\"seo__sitemap-table-header\">URL</th>\n <th className=\"seo__sitemap-table-header\">Last Modified</th>\n <th className=\"seo__sitemap-table-header\">Change Frequency</th>\n <th className=\"seo__sitemap-table-header\">Priority</th>\n </tr>\n </thead>\n <tbody>\n {entries.map((entry, i) => (\n <tr key={i} className=\"seo__sitemap-row\">\n <td className=\"seo__sitemap-cell\">\n <a href={entry.url} target=\"_blank\" rel=\"noopener noreferrer\" className=\"seo__sitemap-url\">\n {entry.url}\n </a>\n </td>\n <td className=\"seo__sitemap-cell seo__sitemap-cell-muted\">{entry.lastmod || '-'}</td>\n <td className=\"seo__sitemap-cell seo__sitemap-cell-muted\">{entry.changefreq || '-'}</td>\n <td className=\"seo__sitemap-cell seo__sitemap-cell-muted\">{entry.priority || '-'}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )\n}\n","import { useEffect } from 'react'\nimport { breadcrumbSchema } from '@geenius/seo-shared'\n\ninterface BreadcrumbsJsonLdProps {\n items: {\n name: string\n url: string\n }[]\n}\n\nexport function BreadcrumbsJsonLd({ items }: BreadcrumbsJsonLdProps) {\n useEffect(() => {\n const script = document.createElement('script')\n script.type = 'application/ld+json'\n script.textContent = JSON.stringify(breadcrumbSchema(items))\n document.head.appendChild(script)\n\n return () => {\n document.head.removeChild(script)\n }\n }, [items])\n\n return <div className=\"seo__breadcrumbs\" />\n}\n","import { useEffect } from 'react'\nimport { articleSchema } from '@geenius/seo-shared'\n\ninterface ArticleJsonLdProps {\n title: string\n description: string\n author: string\n datePublished: string\n url: string\n image?: string\n}\n\nexport function ArticleJsonLd({\n title,\n description,\n author,\n datePublished,\n url,\n image,\n}: ArticleJsonLdProps) {\n useEffect(() => {\n const script = document.createElement('script')\n script.type = 'application/ld+json'\n script.textContent = JSON.stringify(\n articleSchema({\n title,\n description,\n author,\n datePublished,\n url,\n image,\n }),\n )\n document.head.appendChild(script)\n\n return () => {\n document.head.removeChild(script)\n }\n }, [title, description, author, datePublished, url, image])\n\n return <div className=\"seo__article-jsonld\" />\n}\n","import { useState } from 'react'\nimport { useSEOAdmin } from '@geenius/seo-react'\nimport { MetaEditor } from '../components/MetaEditor'\nimport { SEOPreview } from '../components/SEOPreview'\nimport { SEOScoreCard } from '../components/SEOScoreCard'\nimport type { SEOMeta } from '@geenius/seo-shared'\n\nexport function SEOAdminPage() {\n const { pages, upsertMeta, deleteMeta, isLoading } = useSEOAdmin()\n const [selectedPath, setSelectedPath] = useState<string | null>(null)\n const [editingMeta, setEditingMeta] = useState<SEOMeta | null>(null)\n\n const currentPage = pages.find((p) => p.path === selectedPath)\n\n const handleSave = async () => {\n if (selectedPath && editingMeta) {\n await upsertMeta(selectedPath, editingMeta)\n setEditingMeta(null)\n }\n }\n\n const handleDelete = async () => {\n if (selectedPath) {\n await deleteMeta(selectedPath)\n setSelectedPath(null)\n setEditingMeta(null)\n }\n }\n\n if (isLoading) return <div style={{ padding: '1rem' }}>Loading...</div>\n\n return (\n <div className=\"seo__admin-container\">\n <h1 className=\"seo__admin-title\">SEO Admin</h1>\n\n <div className=\"seo__admin-grid\">\n <div className=\"seo__admin-sidebar\">\n <div className=\"seo__admin-sidebar-title\">Pages</div>\n <div className=\"seo__admin-page-list\">\n {pages.map((page) => (\n <button\n key={page.path}\n onClick={() => {\n setSelectedPath(page.path)\n setEditingMeta(page.meta)\n }}\n className={`seo__admin-page-button ${\n selectedPath === page.path ? 'seo__admin-page-button--active' : ''\n }`}\n >\n {page.path || '/'}\n </button>\n ))}\n </div>\n </div>\n\n {currentPage && editingMeta && (\n <div className=\"seo__admin-editor\">\n <div>\n <h2 style={{ fontWeight: 600, marginBottom: '1rem' }}>Edit Metadata</h2>\n <MetaEditor meta={editingMeta} onChange={setEditingMeta} />\n </div>\n\n <div className=\"seo__space-y\">\n <div>\n <h2 style={{ fontWeight: 600, marginBottom: '1rem' }}>Preview</h2>\n <SEOPreview meta={editingMeta} />\n </div>\n\n <SEOScoreCard meta={editingMeta} />\n </div>\n </div>\n )}\n </div>\n\n {currentPage && editingMeta && (\n <div className=\"seo__admin-actions\">\n <button onClick={handleSave} className=\"seo__button seo__button--success\">\n Save\n </button>\n <button onClick={handleDelete} className=\"seo__button seo__button--danger\">\n Delete\n </button>\n </div>\n )}\n </div>\n )\n}\n","import { useState, useEffect } from 'react'\n\ninterface AnalyticsSummary {\n path: string\n views: number\n avgTimeOnPage: number\n bounceRate: number\n}\n\nexport function SEOAnalyticsPage() {\n const [topPages, setTopPages] = useState<AnalyticsSummary[]>([])\n const [isLoading, setIsLoading] = useState(false)\n\n useEffect(() => {\n setIsLoading(true)\n setIsLoading(false)\n }, [])\n\n const totalViews = topPages.reduce((sum, p) => sum + p.views, 0)\n const avgTimeOnPage =\n topPages.length > 0\n ? topPages.reduce((sum, p) => sum + p.avgTimeOnPage, 0) / topPages.length\n : 0\n const avgBounceRate =\n topPages.length > 0\n ? (topPages.reduce((sum, p) => sum + p.bounceRate, 0) / topPages.length) * 100\n : 0\n\n if (isLoading) return <div style={{ padding: '1rem' }}>Loading...</div>\n\n return (\n <div className=\"seo__analytics-container\">\n <h1 className=\"seo__analytics-title\">SEO Analytics</h1>\n\n <div className=\"seo__analytics-table-wrapper\">\n <table className=\"seo__sitemap-table\">\n <thead className=\"seo__sitemap-table-head\">\n <tr>\n <th className=\"seo__sitemap-table-header\">Page</th>\n <th className=\"seo__sitemap-table-header\">Views</th>\n <th className=\"seo__sitemap-table-header\">Avg Time (s)</th>\n <th className=\"seo__sitemap-table-header\">Bounce Rate</th>\n </tr>\n </thead>\n <tbody>\n {topPages.map((page, i) => (\n <tr key={i} className=\"seo__sitemap-row\">\n <td className=\"seo__sitemap-cell\">{page.path}</td>\n <td className=\"seo__sitemap-cell\">{page.views.toLocaleString()}</td>\n <td className=\"seo__sitemap-cell\">{page.avgTimeOnPage.toFixed(1)}</td>\n <td className=\"seo__sitemap-cell\">{(page.bounceRate * 100).toFixed(1)}%</td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n\n {topPages.length === 0 && (\n <p style={{ textAlign: 'center', color: 'var(--seo-text-muted)', padding: '2rem' }}>\n No analytics data available\n </p>\n )}\n\n <div className=\"seo__analytics-summary\">\n <div className=\"seo__analytics-card\">\n <div className=\"seo__analytics-card-label\">Total Views</div>\n <div className=\"seo__analytics-card-value\">{totalViews.toLocaleString()}</div>\n </div>\n\n <div className=\"seo__analytics-card\">\n <div className=\"seo__analytics-card-label\">Average Time on Page</div>\n <div className=\"seo__analytics-card-value\">{avgTimeOnPage.toFixed(1)}s</div>\n </div>\n\n <div className=\"seo__analytics-card\">\n <div className=\"seo__analytics-card-label\">Average Bounce Rate</div>\n <div className=\"seo__analytics-card-value\">{avgBounceRate.toFixed(1)}%</div>\n </div>\n </div>\n </div>\n )\n}\n"],"mappings":"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","names":["articleSchema","breadcrumbSchema","useSEOAdmin","useSEOScore","jsx","jsx","jsxs","jsx","jsxs","jsx","jsxs","useEffect","jsx","useEffect","jsx","useState","jsx","jsxs","useState","useState","useEffect","jsx","jsxs"]}
@@ -1 +1 @@
1
- # ✦ @geenius-seo/shared\n\n> Geenius Seo — Shared types & Convex schema\n\n---\n\n## Overview\nBuilt with Steve Jobs-level minimalism and Jony Ive-level craftsmanship, this package is designed to deliver unparalleled developer experience (DX) and rock-solid performance.\n\n## Installation\n\n```bash\npnpm add @geenius-seo/shared\n```\n\n## Usage\n\n```typescript\nimport { init } from '@geenius-seo/shared';\n\n// Initialize the module with absolute precision\ninit({\n mode: 'premium',\n});\n```\n\n## Architecture\n- **Zero-config**: It just works.\n- **Strictly Typed**: Fully written in TypeScript for flawless IntelliSense.\n- **Framework Agnostic**: seamlessly integrates into the Geenius ecosystem.\n\n---\n\n*Designed by Antigravity HQ*\n
1
+ # ✦ @geenius/seo-shared\n\n> Geenius Seo — Shared types & Convex schema\n\n---\n\n## Overview\nBuilt with Steve Jobs-level minimalism and Jony Ive-level craftsmanship, this package is designed to deliver unparalleled developer experience (DX) and rock-solid performance.\n\n## Installation\n\n```bash\npnpm add @geenius/seo-shared\n```\n\n## Usage\n\n```typescript\nimport { init } from '@geenius/seo-shared';\n\n// Initialize the module with absolute precision\ninit({\n mode: 'premium',\n});\n```\n\n## Architecture\n- **Zero-config**: It just works.\n- **Strictly Typed**: Fully written in TypeScript for flawless IntelliSense.\n- **Framework Agnostic**: seamlessly integrates into the Geenius ecosystem.\n\n---\n\n*Designed by Antigravity HQ*\n
@@ -0,0 +1,262 @@
1
+ /**
2
+ * SEO configuration and utilities
3
+ */
4
+
5
+ /**
6
+ * SEO configuration builder for type-safe setup
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * const seoConfig = createSEOConfig()
11
+ * .withSiteName('My Site')
12
+ * .withSiteUrl('https://example.com')
13
+ * .withDefaultImage('https://example.com/og-image.jpg')
14
+ * .withTwitterHandle('@mysite')
15
+ * ```
16
+ */
17
+ declare class SEOConfigBuilder {
18
+ private config;
19
+ /**
20
+ * Sets the site name
21
+ */
22
+ withSiteName(name: string): this;
23
+ /**
24
+ * Sets the site URL
25
+ */
26
+ withSiteUrl(url: string): this;
27
+ /**
28
+ * Sets default OG image
29
+ */
30
+ withDefaultImage(url: string): this;
31
+ /**
32
+ * Sets Twitter handle
33
+ */
34
+ withTwitterHandle(handle: string): this;
35
+ /**
36
+ * Sets title template (e.g., '%s — My Site')
37
+ */
38
+ withTitleTemplate(template: string): this;
39
+ /**
40
+ * Sets default locale
41
+ */
42
+ withLocale(locale: string): this;
43
+ /**
44
+ * Sets Google Analytics ID
45
+ */
46
+ withGoogleAnalyticsId(id: string): this;
47
+ /**
48
+ * Sets Google Tag Manager ID
49
+ */
50
+ withGoogleTagManagerId(id: string): this;
51
+ /**
52
+ * Builds the configuration
53
+ */
54
+ build(): SEOConfig;
55
+ }
56
+ /**
57
+ * Creates a new SEO configuration builder
58
+ */
59
+ declare function createSEOConfig(): SEOConfigBuilder;
60
+ /**
61
+ * Creates a validated SEO configuration from a partial config object.
62
+ * Requires siteName and siteUrl at minimum.
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * const config = configureSEO({
67
+ * siteName: 'My Site',
68
+ * siteUrl: 'https://example.com',
69
+ * titleTemplate: '%s — My Site',
70
+ * })
71
+ * ```
72
+ */
73
+ declare function configureSEO(config: Partial<SEOConfig> & {
74
+ siteName: string;
75
+ siteUrl: string;
76
+ }): SEOConfig;
77
+ /**
78
+ * Meta tags builder for individual pages
79
+ *
80
+ * @example
81
+ * ```ts
82
+ * const meta = createPageMeta({
83
+ * title: 'Home',
84
+ * description: 'Welcome to my site'
85
+ * })
86
+ * .withKeywords(['web', 'development'])
87
+ * .withImage('https://example.com/image.jpg')
88
+ * ```
89
+ */
90
+ declare class PageMetaBuilder {
91
+ private config;
92
+ private meta;
93
+ constructor(config: SEOConfig, title: string, description: string);
94
+ /**
95
+ * Adds keywords
96
+ */
97
+ withKeywords(keywords: string[]): this;
98
+ /**
99
+ * Sets canonical URL
100
+ */
101
+ withCanonical(url: string): this;
102
+ /**
103
+ * Sets OG image
104
+ */
105
+ withImage(url: string, alt?: string): this;
106
+ /**
107
+ * Sets OG type
108
+ */
109
+ withType(type: 'website' | 'article' | 'product'): this;
110
+ /**
111
+ * Adds JSON-LD schema
112
+ */
113
+ withJsonLd(schema: Record<string, unknown>): this;
114
+ /**
115
+ * Sets robots meta
116
+ */
117
+ withRobots(robots: string): this;
118
+ /**
119
+ * Adds alternate language links
120
+ */
121
+ withAlternates(alternates: Record<string, string>): this;
122
+ /**
123
+ * Builds the meta tags
124
+ */
125
+ build(): SEOMeta;
126
+ }
127
+ /**
128
+ * Creates page-specific meta tags
129
+ */
130
+ declare function createPageMeta(config: SEOConfig, title: string, description: string): PageMetaBuilder;
131
+ /**
132
+ * Validates SEO meta configuration
133
+ */
134
+ declare function validateSEOMeta(meta: SEOMeta): {
135
+ valid: boolean;
136
+ errors: string[];
137
+ };
138
+ /**
139
+ * Presets for common SEO configurations
140
+ */
141
+ declare const seoPresets: {
142
+ /**
143
+ * Blog article preset
144
+ */
145
+ article: (_config: SEOConfig) => Partial<SEOMeta>;
146
+ /**
147
+ * E-commerce product preset
148
+ */
149
+ product: (_config: SEOConfig) => Partial<SEOMeta>;
150
+ /**
151
+ * Service/company page preset
152
+ */
153
+ business: (_config: SEOConfig) => Partial<SEOMeta>;
154
+ /**
155
+ * Private/unlisted page preset
156
+ */
157
+ private: () => Partial<SEOMeta>;
158
+ };
159
+
160
+ interface OGMeta {
161
+ title: string;
162
+ description: string;
163
+ image?: string;
164
+ imageAlt?: string;
165
+ type: 'website' | 'article' | 'product';
166
+ url: string;
167
+ siteName?: string;
168
+ }
169
+ interface TwitterMeta {
170
+ card: 'summary' | 'summary_large_image';
171
+ title: string;
172
+ description: string;
173
+ image?: string;
174
+ creator?: string;
175
+ }
176
+ interface SEOMeta {
177
+ title: string;
178
+ description: string;
179
+ keywords: string[];
180
+ canonical?: string;
181
+ og: OGMeta;
182
+ twitter: TwitterMeta;
183
+ robots?: string;
184
+ alternates?: Record<string, string>;
185
+ jsonLd?: Record<string, unknown>[];
186
+ }
187
+ interface SitemapEntry {
188
+ url: string;
189
+ lastmod?: string;
190
+ changefreq?: string;
191
+ priority?: number;
192
+ }
193
+ interface RobotsTxt {
194
+ allow: string[];
195
+ disallow: string[];
196
+ sitemap?: string;
197
+ }
198
+ interface SEOConfig {
199
+ siteName: string;
200
+ siteUrl: string;
201
+ titleTemplate?: string;
202
+ defaultImage?: string;
203
+ twitterHandle?: string;
204
+ locale?: string;
205
+ googleAnalyticsId?: string;
206
+ googleTagManagerId?: string;
207
+ }
208
+ declare function buildTitle(pageTitle: string, siteName: string, sep?: string): string;
209
+ declare function applyTitleTemplate(pageTitle: string, template?: string): string;
210
+ declare function truncateDescription(desc: string, max?: number): string;
211
+ declare function articleSchema(data: {
212
+ title: string;
213
+ description: string;
214
+ author: string;
215
+ datePublished: string;
216
+ url: string;
217
+ image?: string;
218
+ }): Record<string, unknown>;
219
+ declare function productSchema(data: {
220
+ name: string;
221
+ description: string;
222
+ price: number;
223
+ currency: string;
224
+ url: string;
225
+ image?: string;
226
+ }): Record<string, unknown>;
227
+ declare function orgSchema(data: {
228
+ name: string;
229
+ url: string;
230
+ logo?: string;
231
+ sameAs?: string[];
232
+ }): Record<string, unknown>;
233
+ declare function breadcrumbSchema(items: {
234
+ name: string;
235
+ url: string;
236
+ }[]): Record<string, unknown>;
237
+ declare function faqSchema(items: {
238
+ question: string;
239
+ answer: string;
240
+ }[]): Record<string, unknown>;
241
+ declare function webApplicationSchema(data: {
242
+ name: string;
243
+ url: string;
244
+ description: string;
245
+ applicationCategory?: string;
246
+ operatingSystem?: string;
247
+ offers?: {
248
+ price: number;
249
+ currency: string;
250
+ };
251
+ }): Record<string, unknown>;
252
+ declare function buildCanonical(path: string, baseUrl: string): string;
253
+ declare function buildAlternates(locales: string[], baseUrl: string, path: string): Record<string, string>;
254
+ declare function generateRobotsTxt(config: RobotsTxt): string;
255
+ declare function generateSitemapXml(entries: SitemapEntry[]): string;
256
+ declare function estimateReadingTime(content: string): number;
257
+ declare function calcSEOScore(meta: SEOMeta): {
258
+ score: number;
259
+ issues: string[];
260
+ };
261
+
262
+ export { type OGMeta, PageMetaBuilder, type RobotsTxt, type SEOConfig, SEOConfigBuilder, type SEOMeta, type SitemapEntry, type TwitterMeta, applyTitleTemplate, articleSchema, breadcrumbSchema, buildAlternates, buildCanonical, buildTitle, calcSEOScore, configureSEO, createPageMeta, createSEOConfig, estimateReadingTime, faqSchema, generateRobotsTxt, generateSitemapXml, orgSchema, productSchema, seoPresets, truncateDescription, validateSEOMeta, webApplicationSchema };