@power-seo/core
Advanced tools
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/title-template.ts","../src/meta-validators.ts","../src/url-utils.ts","../src/text-stats.ts","../src/keyword-density.ts","../src/robots-builder.ts","../src/meta-builder.ts","../src/rate-limit.ts"],"sourcesContent":["// @power-seo/core — Main Entry Point\n// ----------------------------------------------------------------------------\n\n// Types\nexport type {\n MetaTag,\n LinkTag,\n OpenGraphType,\n OpenGraphImage,\n OpenGraphVideo,\n OpenGraphArticle,\n OpenGraphProfile,\n OpenGraphBook,\n OpenGraphConfig,\n TwitterCardType,\n TwitterCardConfig,\n RobotsDirective,\n HreflangConfig,\n SEOConfig,\n ContentAnalysisInput,\n AnalysisStatus,\n AnalysisResult,\n ContentAnalysisOutput,\n ReadabilityInput,\n ReadabilityOutput,\n TextStatistics,\n SitemapURL,\n SitemapImage,\n SitemapVideo,\n SitemapNews,\n SitemapConfig,\n RedirectStatusCode,\n RedirectRule,\n SchemaBase,\n SchemaGraphConfig,\n} from './types.js';\n\n// Constants\nexport {\n TITLE_MAX_PIXELS,\n TITLE_MAX_LENGTH,\n TITLE_MIN_LENGTH,\n META_DESCRIPTION_MAX_PIXELS,\n META_DESCRIPTION_MAX_LENGTH,\n META_DESCRIPTION_MIN_LENGTH,\n MIN_WORD_COUNT,\n RECOMMENDED_WORD_COUNT,\n OG_IMAGE,\n TWITTER_IMAGE,\n MAX_URL_LENGTH,\n KEYWORD_DENSITY,\n READABILITY,\n AI_CRAWLERS,\n CHAR_PIXEL_WIDTHS,\n DEFAULT_CHAR_WIDTH,\n SCHEMA_TYPES,\n} from './constants.js';\nexport type { SchemaType } from './constants.js';\n\n// Title Template Engine\nexport { applyTitleTemplate, createTitleTemplate } from './title-template.js';\nexport type { TitleTemplateVars } from './title-template.js';\n\n// Meta Validators\nexport { calculatePixelWidth, validateTitle, validateMetaDescription } from './meta-validators.js';\nexport type { ValidationSeverity, ValidationResult } from './meta-validators.js';\n\n// URL Utilities\nexport {\n resolveCanonical,\n normalizeUrl,\n ensureTrailingSlash,\n removeTrailingSlash,\n stripQueryParams,\n stripTrackingParams,\n extractSlug,\n isAbsoluteUrl,\n toSlug,\n} from './url-utils.js';\n\n// Text Statistics\nexport {\n stripHtml,\n extractFirstTagContent,\n extractTagContents,\n getWords,\n getSentences,\n getParagraphs,\n countSyllables,\n countTotalSyllables,\n getTextStatistics,\n} from './text-stats.js';\n\n// Keyword Density\nexport {\n calculateKeywordDensity,\n countKeywordOccurrences,\n analyzeKeyphraseOccurrences,\n} from './keyword-density.js';\nexport type { KeywordDensityResult, KeyphraseOccurrences } from './keyword-density.js';\n\n// Robots Builder\nexport { buildRobotsContent, parseRobotsContent } from './robots-builder.js';\n\n// Meta Builder\nexport {\n buildMetaTags,\n buildLinkTags,\n buildOpenGraphTags,\n buildTwitterTags,\n buildHreflangTags,\n resolveTitle,\n} from './meta-builder.js';\n\n// Rate Limiting\nexport { createTokenBucket, consumeToken, getWaitTime, sleep } from './rate-limit.js';\nexport type { TokenBucket } from './rate-limit.js';\n","// @power-seo/core — SEO Constants\n// ----------------------------------------------------------------------------\n\n/** Google SERP title max display width in pixels */\nexport const TITLE_MAX_PIXELS = 580;\n\n/** Google SERP title recommended max character length */\nexport const TITLE_MAX_LENGTH = 60;\n\n/** Google SERP title minimum recommended character length */\nexport const TITLE_MIN_LENGTH = 50;\n\n/** Google SERP meta description max display width in pixels (desktop) */\nexport const META_DESCRIPTION_MAX_PIXELS = 920;\n\n/** Meta description recommended max character length */\nexport const META_DESCRIPTION_MAX_LENGTH = 160;\n\n/** Meta description recommended min character length */\nexport const META_DESCRIPTION_MIN_LENGTH = 120;\n\n/** Recommended minimum word count (to avoid thin content) */\nexport const MIN_WORD_COUNT = 300;\n\n/** Recommended word count for blog posts */\nexport const RECOMMENDED_WORD_COUNT = 1000;\n\n/** Open Graph recommended image dimensions */\nexport const OG_IMAGE = {\n WIDTH: 1200,\n HEIGHT: 630,\n MIN_WIDTH: 200,\n MIN_HEIGHT: 200,\n ASPECT_RATIO: 1.91,\n} as const;\n\n/** Twitter Card recommended image dimensions */\nexport const TWITTER_IMAGE = {\n SUMMARY: { WIDTH: 144, HEIGHT: 144 },\n SUMMARY_LARGE: { WIDTH: 800, HEIGHT: 418 },\n} as const;\n\n/** Maximum URL length recommended for SEO */\nexport const MAX_URL_LENGTH = 75;\n\n/** Optimal keyword density range (percentage) */\nexport const KEYWORD_DENSITY = {\n MIN: 0.5,\n MAX: 2.5,\n OPTIMAL: 1.5,\n} as const;\n\n/** Readability thresholds */\nexport const READABILITY = {\n FLESCH_EASE_GOOD: 60,\n FLESCH_EASE_FAIR: 30,\n MAX_SENTENCE_LENGTH: 20,\n MAX_PARAGRAPH_WORDS: 150,\n MAX_PASSIVE_VOICE_PERCENT: 10,\n MIN_TRANSITION_WORD_PERCENT: 30,\n} as const;\n\n/** Common AI crawler user agents */\nexport const AI_CRAWLERS = [\n 'GPTBot',\n 'ChatGPT-User',\n 'CCBot',\n 'Google-Extended',\n 'ClaudeBot',\n 'anthropic-ai',\n 'Bytespider',\n 'PerplexityBot',\n 'Applebot-Extended',\n 'FacebookBot',\n 'cohere-ai',\n] as const;\n\n/** Approximate character widths for Google SERP pixel calculation (Arial font) */\nexport const CHAR_PIXEL_WIDTHS: Record<string, number> = {\n // Lowercase\n a: 6.67,\n b: 6.67,\n c: 6.0,\n d: 6.67,\n e: 6.67,\n f: 3.33,\n g: 6.67,\n h: 6.67,\n i: 2.67,\n j: 2.67,\n k: 6.0,\n l: 2.67,\n m: 10.0,\n n: 6.67,\n o: 6.67,\n p: 6.67,\n q: 6.67,\n r: 4.0,\n s: 6.0,\n t: 3.67,\n u: 6.67,\n v: 6.0,\n w: 9.33,\n x: 6.0,\n y: 6.0,\n z: 6.0,\n // Uppercase\n A: 8.0,\n B: 8.0,\n C: 8.67,\n D: 8.67,\n E: 8.0,\n F: 7.33,\n G: 9.33,\n H: 8.67,\n I: 3.33,\n J: 6.0,\n K: 8.0,\n L: 6.67,\n M: 10.0,\n N: 8.67,\n O: 9.33,\n P: 8.0,\n Q: 9.33,\n R: 8.67,\n S: 8.0,\n T: 7.33,\n U: 8.67,\n V: 8.0,\n W: 11.33,\n X: 8.0,\n Y: 8.0,\n Z: 7.33,\n // Numbers\n '0': 6.67,\n '1': 6.67,\n '2': 6.67,\n '3': 6.67,\n '4': 6.67,\n '5': 6.67,\n '6': 6.67,\n '7': 6.67,\n '8': 6.67,\n '9': 6.67,\n // Common punctuation\n ' ': 3.33,\n '.': 3.33,\n ',': 3.33,\n '!': 3.33,\n '?': 6.67,\n ':': 3.33,\n ';': 3.33,\n '-': 4.0,\n _: 6.67,\n '(': 4.0,\n ')': 4.0,\n '[': 3.33,\n ']': 3.33,\n '/': 3.33,\n '|': 3.11,\n '&': 8.0,\n '@': 12.17,\n '#': 6.67,\n $: 6.67,\n '%': 10.67,\n '+': 7.0,\n '=': 7.0,\n '\"': 4.27,\n \"'\": 2.3,\n};\n\n/** Default pixel width for unknown characters */\nexport const DEFAULT_CHAR_WIDTH = 6.67;\n\n/** Common Schema.org types used in SEO */\nexport const SCHEMA_TYPES = [\n 'Article',\n 'BlogPosting',\n 'NewsArticle',\n 'Product',\n 'FAQPage',\n 'HowTo',\n 'LocalBusiness',\n 'Organization',\n 'Person',\n 'WebSite',\n 'WebPage',\n 'BreadcrumbList',\n 'Event',\n 'Recipe',\n 'VideoObject',\n 'Course',\n 'JobPosting',\n 'SoftwareApplication',\n 'Review',\n 'AggregateRating',\n 'ItemList',\n 'CollectionPage',\n 'SearchAction',\n 'Service',\n 'MedicalWebPage',\n] as const;\n\nexport type SchemaType = (typeof SCHEMA_TYPES)[number];\n","// @power-seo/core — Title Template Engine\n// ----------------------------------------------------------------------------\n\nexport interface TitleTemplateVars {\n title?: string;\n siteName?: string;\n separator?: string;\n tagline?: string;\n page?: number;\n [key: string]: string | number | undefined;\n}\n\nconst DEFAULT_SEPARATOR = '|';\n\n/**\n * Apply a title template with variable substitution.\n *\n * Supports patterns like:\n * - `%title% | %siteName%`\n * - `%title% - %siteName%`\n * - `%title% | %siteName% - Page %page%`\n *\n * @example\n * ```ts\n * applyTitleTemplate('%title% | %siteName%', {\n * title: 'My Page',\n * siteName: 'My Site'\n * });\n * // => \"My Page | My Site\"\n * ```\n */\nexport function applyTitleTemplate(template: string, vars: TitleTemplateVars): string {\n if (!template) {\n return vars.title ?? '';\n }\n\n let result = template;\n\n // Replace all %variable% patterns\n result = result.replace(/%(\\w+)%/g, (_match, key: string) => {\n const value = vars[key];\n if (value === undefined || value === null) {\n return '';\n }\n return String(value);\n });\n\n // If no template used, fall back to simple separator pattern\n if (result === template && vars.title) {\n const separator = vars.separator ?? DEFAULT_SEPARATOR;\n if (vars.siteName) {\n return `${vars.title} ${separator} ${vars.siteName}`;\n }\n return vars.title;\n }\n\n // Clean up multiple consecutive spaces\n result = result.replace(/\\s{2,}/g, ' ').trim();\n\n // Clean up dangling separators at start/end\n result = result.replace(/^[\\s|·—–-]+|[\\s|·—–-]+$/g, '').trim();\n\n return result;\n}\n\n/**\n * Create a title template function with pre-configured defaults.\n *\n * @example\n * ```ts\n * const makeTitle = createTitleTemplate({\n * siteName: 'My Site',\n * separator: '—'\n * });\n *\n * makeTitle('About Us');\n * // => \"About Us — My Site\"\n * ```\n */\nexport function createTitleTemplate(\n defaults: Omit<TitleTemplateVars, 'title'> & { template?: string },\n) {\n const { template = '%title% %separator% %siteName%', ...defaultVars } = defaults;\n\n return (title: string, overrides?: Partial<TitleTemplateVars>): string => {\n return applyTitleTemplate(template, {\n separator: DEFAULT_SEPARATOR,\n ...defaultVars,\n ...overrides,\n title,\n });\n };\n}\n","// @power-seo/core — Meta Tag Validators\n// ----------------------------------------------------------------------------\n\nimport {\n TITLE_MAX_LENGTH,\n TITLE_MIN_LENGTH,\n TITLE_MAX_PIXELS,\n META_DESCRIPTION_MAX_LENGTH,\n META_DESCRIPTION_MIN_LENGTH,\n META_DESCRIPTION_MAX_PIXELS,\n CHAR_PIXEL_WIDTHS,\n DEFAULT_CHAR_WIDTH,\n} from './constants.js';\n\nexport type ValidationSeverity = 'error' | 'warning' | 'info';\n\nexport interface ValidationResult {\n valid: boolean;\n severity: ValidationSeverity;\n message: string;\n charCount?: number;\n pixelWidth?: number;\n}\n\n/**\n * Calculate the pixel width of a string using Google SERP font metrics.\n */\nexport function calculatePixelWidth(text: string): number {\n let width = 0;\n for (const char of text) {\n width += CHAR_PIXEL_WIDTHS[char] ?? DEFAULT_CHAR_WIDTH;\n }\n return Math.round(width * 100) / 100;\n}\n\n/**\n * Validate a page title for SEO best practices.\n *\n * Checks character length and pixel width against Google SERP limits.\n */\nexport function validateTitle(title: string): ValidationResult {\n if (!title || title.trim().length === 0) {\n return {\n valid: false,\n severity: 'error',\n message: 'Title is empty. Every page must have a unique title.',\n charCount: 0,\n pixelWidth: 0,\n };\n }\n\n const trimmed = title.trim();\n const charCount = trimmed.length;\n const pixelWidth = calculatePixelWidth(trimmed);\n\n if (pixelWidth > TITLE_MAX_PIXELS) {\n return {\n valid: false,\n severity: 'warning',\n message: `Title is ${charCount} characters (${pixelWidth}px) and will be truncated in Google search results. Keep it under ${TITLE_MAX_PIXELS}px (~${TITLE_MAX_LENGTH} characters).`,\n charCount,\n pixelWidth,\n };\n }\n\n if (charCount < TITLE_MIN_LENGTH) {\n return {\n valid: true,\n severity: 'warning',\n message: `Title is only ${charCount} characters. Consider making it at least ${TITLE_MIN_LENGTH} characters for better SEO.`,\n charCount,\n pixelWidth,\n };\n }\n\n if (charCount > TITLE_MAX_LENGTH) {\n return {\n valid: true,\n severity: 'info',\n message: `Title is ${charCount} characters but fits within ${TITLE_MAX_PIXELS}px. The pixel width (${pixelWidth}px) is what matters for truncation.`,\n charCount,\n pixelWidth,\n };\n }\n\n return {\n valid: true,\n severity: 'info',\n message: `Title is ${charCount} characters (${pixelWidth}px). Good length.`,\n charCount,\n pixelWidth,\n };\n}\n\n/**\n * Validate a meta description for SEO best practices.\n *\n * Checks character length and pixel width against Google SERP limits.\n */\nexport function validateMetaDescription(description: string): ValidationResult {\n if (!description || description.trim().length === 0) {\n return {\n valid: false,\n severity: 'warning',\n message:\n 'Meta description is empty. Without one, Google will auto-generate a snippet from page content.',\n charCount: 0,\n pixelWidth: 0,\n };\n }\n\n const trimmed = description.trim();\n const charCount = trimmed.length;\n const pixelWidth = calculatePixelWidth(trimmed);\n\n if (pixelWidth > META_DESCRIPTION_MAX_PIXELS) {\n return {\n valid: false,\n severity: 'warning',\n message: `Meta description is ${charCount} characters (${pixelWidth}px) and will be truncated. Keep it under ${META_DESCRIPTION_MAX_PIXELS}px (~${META_DESCRIPTION_MAX_LENGTH} characters).`,\n charCount,\n pixelWidth,\n };\n }\n\n if (charCount < META_DESCRIPTION_MIN_LENGTH) {\n return {\n valid: true,\n severity: 'warning',\n message: `Meta description is only ${charCount} characters. Aim for ${META_DESCRIPTION_MIN_LENGTH}-${META_DESCRIPTION_MAX_LENGTH} characters to maximize SERP real estate.`,\n charCount,\n pixelWidth,\n };\n }\n\n return {\n valid: true,\n severity: 'info',\n message: `Meta description is ${charCount} characters (${pixelWidth}px). Good length.`,\n charCount,\n pixelWidth,\n };\n}\n","// @power-seo/core — URL Utilities\n// ----------------------------------------------------------------------------\n\n/**\n * Resolve a canonical URL from a base URL and path.\n *\n * @example\n * ```ts\n * resolveCanonical('https://example.com', '/blog/post');\n * // => \"https://example.com/blog/post\"\n * ```\n */\nexport function resolveCanonical(baseUrl: string, path?: string): string {\n if (!path) {\n return normalizeUrl(baseUrl);\n }\n\n // If path is already absolute URL, return it normalized\n if (path.startsWith('http://') || path.startsWith('https://')) {\n return normalizeUrl(path);\n }\n\n let base = baseUrl;\n while (base.endsWith('/')) base = base.slice(0, -1);\n const cleanPath = path.startsWith('/') ? path : `/${path}`;\n\n return normalizeUrl(`${base}${cleanPath}`);\n}\n\n/**\n * Normalize a URL by removing default ports, double slashes, etc.\n */\nexport function normalizeUrl(url: string): string {\n try {\n const parsed = new globalThis.URL(url);\n\n // Remove default ports\n if (\n (parsed.protocol === 'https:' && parsed.port === '443') ||\n (parsed.protocol === 'http:' && parsed.port === '80')\n ) {\n parsed.port = '';\n }\n\n // Remove trailing slash from path (except root)\n if (parsed.pathname !== '/' && parsed.pathname.endsWith('/')) {\n parsed.pathname = parsed.pathname.replace(/\\/+$/, '');\n }\n\n // Remove double slashes in path\n parsed.pathname = parsed.pathname.replace(/\\/\\/+/g, '/');\n\n return parsed.toString();\n } catch {\n return url;\n }\n}\n\n/**\n * Ensure a URL has a trailing slash (useful for directories).\n */\nexport function ensureTrailingSlash(url: string): string {\n if (url.endsWith('/')) return url;\n\n try {\n const parsed = new globalThis.URL(url);\n // Don't add trailing slash to URLs with file extensions\n const lastSegment = parsed.pathname.split('/').pop() ?? '';\n if (lastSegment.includes('.')) return url;\n parsed.pathname += '/';\n return parsed.toString();\n } catch {\n return url.endsWith('/') ? url : `${url}/`;\n }\n}\n\n/**\n * Remove trailing slash from a URL.\n */\nexport function removeTrailingSlash(url: string): string {\n try {\n const parsed = new globalThis.URL(url);\n if (parsed.pathname !== '/' && parsed.pathname.endsWith('/')) {\n parsed.pathname = parsed.pathname.replace(/\\/+$/, '');\n }\n return parsed.toString();\n } catch {\n return url.endsWith('/') && url !== '/' ? url.slice(0, -1) : url;\n }\n}\n\n/**\n * Strip query parameters from a URL, optionally keeping specific ones.\n *\n * @example\n * ```ts\n * stripQueryParams('https://example.com/page?utm_source=twitter&id=123', ['id']);\n * // => \"https://example.com/page?id=123\"\n * ```\n */\nexport function stripQueryParams(url: string, keepParams?: string[]): string {\n try {\n const parsed = new globalThis.URL(url);\n\n if (!keepParams || keepParams.length === 0) {\n parsed.search = '';\n return parsed.toString();\n }\n\n const newParams = new globalThis.URLSearchParams();\n for (const param of keepParams) {\n const value = parsed.searchParams.get(param);\n if (value !== null) {\n newParams.set(param, value);\n }\n }\n\n parsed.search = newParams.toString() ? `?${newParams.toString()}` : '';\n return parsed.toString();\n } catch {\n return url;\n }\n}\n\n/**\n * Strip common tracking parameters (UTM, fbclid, gclid, etc.) from a URL.\n */\nexport function stripTrackingParams(url: string): string {\n const trackingParams = [\n 'utm_source',\n 'utm_medium',\n 'utm_campaign',\n 'utm_term',\n 'utm_content',\n 'fbclid',\n 'gclid',\n 'gclsrc',\n 'dclid',\n 'msclkid',\n 'ref',\n 'mc_cid',\n 'mc_eid',\n '_ga',\n '_gl',\n 'yclid',\n 'twclid',\n 'igshid',\n ];\n\n try {\n const parsed = new globalThis.URL(url);\n for (const param of trackingParams) {\n parsed.searchParams.delete(param);\n }\n parsed.search = parsed.searchParams.toString() ? `?${parsed.searchParams.toString()}` : '';\n return parsed.toString();\n } catch {\n return url;\n }\n}\n\n/**\n * Extract the slug from a URL path.\n *\n * @example\n * ```ts\n * extractSlug('https://example.com/blog/my-post');\n * // => \"my-post\"\n * ```\n */\nexport function extractSlug(url: string): string {\n try {\n const parsed = new globalThis.URL(url);\n const segments = parsed.pathname.split('/').filter(Boolean);\n return segments[segments.length - 1] ?? '';\n } catch {\n const segments = url.split('/').filter(Boolean);\n return segments[segments.length - 1] ?? '';\n }\n}\n\n/**\n * Check if a URL is relative or absolute.\n */\nexport function isAbsoluteUrl(url: string): boolean {\n return /^https?:\\/\\//i.test(url);\n}\n\n/**\n * Convert a string to a URL-safe slug.\n *\n * @example\n * ```ts\n * toSlug('My Blog Post Title!');\n * // => \"my-blog-post-title\"\n * ```\n */\nexport function toSlug(text: string): string {\n return text\n .toLowerCase()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '') // Remove diacritics\n .replace(/[^a-z0-9\\s-]/g, '') // Remove non-alphanumeric\n .replace(/\\s+/g, '-') // Spaces to hyphens\n .replace(/-+/g, '-') // Collapse multiple hyphens\n .replace(/^-|-$/g, ''); // Trim hyphens\n}\n","// @power-seo/core — Text Statistics Engine\n// ----------------------------------------------------------------------------\n\nimport type { TextStatistics } from './types.js';\n\n/**\n * Remove all occurrences of a paired HTML block tag (e.g. script, style)\n * using plain string search to avoid regex ReDoS on crafted input.\n */\nfunction removeBlockTag(html: string, tag: string): string {\n const open = `<${tag}`;\n const close = `</${tag}>`;\n let result = html;\n let start = result.toLowerCase().indexOf(open);\n while (start !== -1) {\n const end = result.toLowerCase().indexOf(close, start);\n if (end === -1) {\n // No closing tag found — remove everything from the opening tag onwards.\n result = result.slice(0, start);\n break;\n }\n result = result.slice(0, start) + result.slice(end + close.length);\n start = result.toLowerCase().indexOf(open, start);\n }\n return result;\n}\n\n/**\n * Remove HTML tags using a character-by-character scanner (no regex).\n * Avoids ReDoS from patterns like /<[^>]+>/g on crafted inputs.\n */\nfunction removeHtmlTags(html: string): string {\n let result = '';\n let i = 0;\n while (i < html.length) {\n if (html[i] === '<') {\n const end = html.indexOf('>', i + 1);\n if (end === -1) break; // malformed — stop\n result += ' ';\n i = end + 1;\n } else {\n result += html[i]!;\n i++;\n }\n }\n return result;\n}\n\n/**\n * Extract the inner HTML of the first occurrence of a tag (e.g. 'p', 'h1').\n * Uses plain string search — no regex, no ReDoS.\n */\nexport function extractFirstTagContent(html: string, tag: string): string | null {\n const lc = html.toLowerCase();\n const openTag = `<${tag}`;\n const closeTag = `</${tag}>`;\n const start = lc.indexOf(openTag);\n if (start === -1) return null;\n const contentStart = lc.indexOf('>', start);\n if (contentStart === -1) return null;\n const end = lc.indexOf(closeTag, contentStart + 1);\n if (end === -1) return null;\n return html.slice(contentStart + 1, end);\n}\n\n/**\n * Extract inner HTML of all occurrences of a tag (e.g. 'h2').\n * Uses plain string search — no regex, no ReDoS.\n */\nexport function extractTagContents(html: string, tag: string): string[] {\n const results: string[] = [];\n const lc = html.toLowerCase();\n const openTag = `<${tag}`;\n const closeTag = `</${tag}>`;\n let pos = 0;\n while (pos < lc.length) {\n const start = lc.indexOf(openTag, pos);\n if (start === -1) break;\n const contentStart = lc.indexOf('>', start);\n if (contentStart === -1) break;\n const end = lc.indexOf(closeTag, contentStart + 1);\n if (end === -1) break;\n results.push(html.slice(contentStart + 1, end));\n pos = end + closeTag.length;\n }\n return results;\n}\n\n/**\n * Strip HTML tags from content, returning plain text.\n */\nexport function stripHtml(html: string): string {\n // Remove script and style blocks without regex to avoid ReDoS.\n let result = removeBlockTag(html, 'script');\n result = removeBlockTag(result, 'style');\n\n // Remove remaining tags with a character scanner (no regex) to avoid ReDoS.\n result = removeHtmlTags(result);\n\n return result\n .replace(/ /g, ' ')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n .replace(/&/g, '&') // decode & last to prevent double-unescaping (&lt; → < → <)\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\n/**\n * Split text into words.\n */\nexport function getWords(text: string): string[] {\n const cleaned = stripHtml(text);\n if (!cleaned) return [];\n return cleaned.split(/\\s+/).filter((w) => w.length > 0);\n}\n\n/**\n * Split text into sentences using common sentence boundary rules.\n */\nexport function getSentences(text: string): string[] {\n const cleaned = stripHtml(text);\n if (!cleaned) return [];\n\n // Split on sentence-ending punctuation followed by space or end of string\n const sentences = cleaned\n .split(/(?<=[.!?])\\s+/)\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n\n return sentences;\n}\n\n/**\n * Split HTML content into paragraphs.\n */\nexport function getParagraphs(html: string): string[] {\n // Split by paragraph tags or double newlines\n const blocks = html\n .split(/<\\/p>|<br\\s*\\/?>.*<br\\s*\\/?>/gi)\n .map((block) => stripHtml(block).trim())\n .filter((block) => block.length > 0);\n\n if (blocks.length === 0) {\n // Fall back to double newline splitting\n return stripHtml(html)\n .split(/\\n\\s*\\n/)\n .map((p) => p.trim())\n .filter((p) => p.length > 0);\n }\n\n return blocks;\n}\n\n/**\n * Count syllables in a word using a heuristic approach.\n *\n * This is a simplified English syllable counter. For production use with\n * high accuracy, consider using the `syllable` npm package.\n */\nexport function countSyllables(word: string): number {\n const w = word.toLowerCase().replace(/[^a-z]/g, '');\n if (w.length <= 2) return 1;\n\n // Special suffixes\n let count = 0;\n const vowels = 'aeiouy';\n let prevIsVowel = false;\n\n for (let i = 0; i < w.length; i++) {\n const isVowel = vowels.includes(w[i]!);\n if (isVowel && !prevIsVowel) {\n count++;\n }\n prevIsVowel = isVowel;\n }\n\n // Handle silent 'e' at end\n if (w.endsWith('e') && !w.endsWith('le') && count > 1) {\n count--;\n }\n\n // Handle common suffixes\n if (w.endsWith('es') || w.endsWith('ed')) {\n // Don't count 'es' or 'ed' as extra unless preceded by 't' or 'd'\n const beforeSuffix = w[w.length - (w.endsWith('es') ? 3 : 3)];\n if (beforeSuffix !== 't' && beforeSuffix !== 'd') {\n // Already handled by vowel counting\n }\n }\n\n return Math.max(1, count);\n}\n\n/**\n * Count total syllables in text.\n */\nexport function countTotalSyllables(text: string): number {\n const words = getWords(text);\n return words.reduce((total, word) => total + countSyllables(word), 0);\n}\n\n/**\n * Get comprehensive text statistics.\n *\n * @example\n * ```ts\n * const stats = getTextStatistics('<p>Hello world. This is a test.</p>');\n * // => { wordCount: 7, sentenceCount: 2, ... }\n * ```\n */\nexport function getTextStatistics(content: string): TextStatistics {\n const words = getWords(content);\n const sentences = getSentences(content);\n const paragraphs = getParagraphs(content);\n const syllableCount = countTotalSyllables(content);\n const plainText = stripHtml(content);\n\n const wordCount = words.length;\n const sentenceCount = sentences.length;\n const paragraphCount = paragraphs.length;\n\n return {\n wordCount,\n sentenceCount,\n paragraphCount,\n syllableCount,\n characterCount: plainText.length,\n avgWordsPerSentence: sentenceCount > 0 ? Math.round((wordCount / sentenceCount) * 10) / 10 : 0,\n avgSyllablesPerWord: wordCount > 0 ? Math.round((syllableCount / wordCount) * 100) / 100 : 0,\n };\n}\n","// @power-seo/core — Keyword Density Calculator\n// ----------------------------------------------------------------------------\n\nimport { getWords, stripHtml, extractFirstTagContent, extractTagContents } from './text-stats.js';\n\nexport interface KeywordDensityResult {\n keyword: string;\n count: number;\n density: number;\n totalWords: number;\n}\n\nexport interface KeyphraseOccurrences {\n inTitle: boolean;\n inMetaDescription: boolean;\n inFirstParagraph: boolean;\n inH1: boolean;\n inHeadings: number;\n inContent: number;\n inSlug: boolean;\n inAltText: number;\n density: number;\n totalWords: number;\n}\n\n/**\n * Calculate keyword density as a percentage.\n *\n * @example\n * ```ts\n * calculateKeywordDensity('react seo', 'Learn React SEO for your React application. React SEO matters.');\n * // => { keyword: 'react seo', count: 2, density: 20.0, totalWords: 10 }\n * ```\n */\nexport function calculateKeywordDensity(keyword: string, content: string): KeywordDensityResult {\n const plainText = stripHtml(content).toLowerCase();\n const words = getWords(content);\n const normalizedKeyword = keyword.toLowerCase().trim();\n\n if (!normalizedKeyword || words.length === 0) {\n return { keyword, count: 0, density: 0, totalWords: words.length };\n }\n\n // Count occurrences of the keyphrase\n const count = countKeywordOccurrences(plainText, normalizedKeyword);\n\n // Density = (keyword occurrences / total words) * 100\n // For multi-word keyphrases, we count phrase occurrences\n const keywordWordCount = normalizedKeyword.split(/\\s+/).length;\n const density =\n words.length > 0 ? Math.round(((count * keywordWordCount) / words.length) * 1000) / 10 : 0;\n\n return {\n keyword,\n count,\n density,\n totalWords: words.length,\n };\n}\n\n/**\n * Count occurrences of a keyword/keyphrase in text (case-insensitive).\n */\nexport function countKeywordOccurrences(text: string, keyword: string): number {\n const normalizedText = text.toLowerCase();\n const normalizedKeyword = keyword.toLowerCase().trim();\n\n if (!normalizedKeyword) return 0;\n\n // Use word boundary matching for single words\n const escaped = normalizedKeyword.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const regex = new RegExp(`\\\\b${escaped}\\\\b`, 'gi');\n const matches = normalizedText.match(regex);\n\n return matches?.length ?? 0;\n}\n\n/**\n * Analyze where a focus keyphrase appears throughout page content.\n */\nexport function analyzeKeyphraseOccurrences(config: {\n keyphrase: string;\n title?: string;\n metaDescription?: string;\n content: string;\n slug?: string;\n images?: Array<{ alt?: string }>;\n}): KeyphraseOccurrences {\n const { keyphrase, title, metaDescription, content, slug, images } = config;\n const kp = keyphrase.toLowerCase().trim();\n\n const plainContent = stripHtml(content).toLowerCase();\n const words = getWords(content);\n\n // Check title\n const inTitle = title ? title.toLowerCase().includes(kp) : false;\n\n // Check meta description\n const inMetaDescription = metaDescription ? metaDescription.toLowerCase().includes(kp) : false;\n\n // Check first paragraph — use string-based extraction to avoid regex ReDoS\n const firstParaInner = extractFirstTagContent(content, 'p');\n const firstParagraph = firstParaInner !== null\n ? stripHtml(firstParaInner).toLowerCase()\n : (plainContent.split(/\\n\\n/)[0]?.toLowerCase() ?? '');\n const inFirstParagraph = firstParagraph.includes(kp);\n\n // Check H1 — use string-based extraction to avoid regex ReDoS\n const h1Inner = extractFirstTagContent(content, 'h1');\n const inH1 = h1Inner !== null\n ? stripHtml(h1Inner).toLowerCase().includes(kp)\n : false;\n\n // Count in headings (h2-h6) — use string-based extraction to avoid regex ReDoS\n let inHeadings = 0;\n for (let level = 2; level <= 6; level++) {\n for (const inner of extractTagContents(content, `h${level}`)) {\n if (stripHtml(inner).toLowerCase().includes(kp)) {\n inHeadings++;\n }\n }\n }\n\n // Count in content\n const inContent = countKeywordOccurrences(plainContent, kp);\n\n // Check slug\n const inSlug = slug ? slug.toLowerCase().includes(kp.replace(/\\s+/g, '-')) : false;\n\n // Count in alt text\n const inAltText = images ? images.filter((img) => img.alt?.toLowerCase().includes(kp)).length : 0;\n\n // Calculate density\n const keywordWordCount = kp.split(/\\s+/).length;\n const density =\n words.length > 0 ? Math.round(((inContent * keywordWordCount) / words.length) * 1000) / 10 : 0;\n\n return {\n inTitle,\n inMetaDescription,\n inFirstParagraph,\n inH1,\n inHeadings,\n inContent,\n inSlug,\n inAltText,\n density,\n totalWords: words.length,\n };\n}\n","// @power-seo/core — Robots Directive Builder\n// ----------------------------------------------------------------------------\n\nimport type { RobotsDirective } from './types.js';\n\n/**\n * Build a robots meta content string from a RobotsDirective config.\n *\n * @example\n * ```ts\n * buildRobotsContent({ index: false, follow: true, maxSnippet: 150 });\n * // => \"noindex, follow, max-snippet:150\"\n * ```\n */\nexport function buildRobotsContent(directive: RobotsDirective): string {\n const parts: string[] = [];\n\n if (directive.index === false) {\n parts.push('noindex');\n } else if (directive.index === true) {\n parts.push('index');\n }\n\n if (directive.follow === false) {\n parts.push('nofollow');\n } else if (directive.follow === true) {\n parts.push('follow');\n }\n\n if (directive.noarchive) parts.push('noarchive');\n if (directive.nosnippet) parts.push('nosnippet');\n if (directive.noimageindex) parts.push('noimageindex');\n if (directive.notranslate) parts.push('notranslate');\n\n if (directive.maxSnippet !== undefined) {\n parts.push(`max-snippet:${directive.maxSnippet}`);\n }\n\n if (directive.maxImagePreview !== undefined) {\n parts.push(`max-image-preview:${directive.maxImagePreview}`);\n }\n\n if (directive.maxVideoPreview !== undefined) {\n parts.push(`max-video-preview:${directive.maxVideoPreview}`);\n }\n\n if (directive.unavailableAfter) {\n parts.push(`unavailable_after:${directive.unavailableAfter}`);\n }\n\n return parts.join(', ');\n}\n\n/**\n * Parse a robots meta content string into a RobotsDirective.\n */\nexport function parseRobotsContent(content: string): RobotsDirective {\n const directive: RobotsDirective = {};\n const parts = content.split(',').map((p) => p.trim().toLowerCase());\n\n for (const part of parts) {\n if (part === 'noindex') directive.index = false;\n else if (part === 'index') directive.index = true;\n else if (part === 'nofollow') directive.follow = false;\n else if (part === 'follow') directive.follow = true;\n else if (part === 'noarchive') directive.noarchive = true;\n else if (part === 'nosnippet') directive.nosnippet = true;\n else if (part === 'noimageindex') directive.noimageindex = true;\n else if (part === 'notranslate') directive.notranslate = true;\n else if (part.startsWith('max-snippet:')) {\n directive.maxSnippet = parseInt(part.split(':')[1] ?? '0', 10);\n } else if (part.startsWith('max-image-preview:')) {\n const value = part.split(':')[1] as 'none' | 'standard' | 'large';\n directive.maxImagePreview = value;\n } else if (part.startsWith('max-video-preview:')) {\n directive.maxVideoPreview = parseInt(part.split(':')[1] ?? '0', 10);\n } else if (part.startsWith('unavailable_after:')) {\n directive.unavailableAfter = part.substring('unavailable_after:'.length);\n }\n }\n\n return directive;\n}\n","// @power-seo/core — Meta Tag Builder\n// ----------------------------------------------------------------------------\n\nimport type {\n MetaTag,\n LinkTag,\n SEOConfig,\n OpenGraphConfig,\n TwitterCardConfig,\n HreflangConfig,\n} from './types.js';\nimport { buildRobotsContent } from './robots-builder.js';\n\n/**\n * Build an array of meta tags from an SEO configuration.\n * This is the framework-agnostic engine — frameworks render these as actual HTML.\n */\nexport function buildMetaTags(config: SEOConfig): MetaTag[] {\n const tags: MetaTag[] = [];\n\n // Description\n if (config.description) {\n tags.push({ name: 'description', content: config.description });\n }\n\n // Robots\n if (config.noindex || config.nofollow || config.robots) {\n const robotsContent = buildRobotsContent({\n index: config.noindex ? false : config.robots?.index,\n follow: config.nofollow ? false : config.robots?.follow,\n ...config.robots,\n });\n if (robotsContent) {\n tags.push({ name: 'robots', content: robotsContent });\n }\n }\n\n // Open Graph\n if (config.openGraph) {\n tags.push(...buildOpenGraphTags(config.openGraph));\n }\n\n // Twitter Card\n if (config.twitter) {\n tags.push(...buildTwitterTags(config.twitter));\n }\n\n // Additional meta tags\n if (config.additionalMetaTags) {\n tags.push(...config.additionalMetaTags);\n }\n\n return tags;\n}\n\n/**\n * Build an array of link tags from an SEO configuration.\n */\nexport function buildLinkTags(config: SEOConfig): LinkTag[] {\n const tags: LinkTag[] = [];\n\n // Canonical\n if (config.canonical) {\n tags.push({ rel: 'canonical', href: config.canonical });\n }\n\n // Language alternates (hreflang)\n if (config.languageAlternates) {\n tags.push(...buildHreflangTags(config.languageAlternates));\n }\n\n // Additional link tags\n if (config.additionalLinkTags) {\n tags.push(...config.additionalLinkTags);\n }\n\n return tags;\n}\n\n/**\n * Build Open Graph meta tags.\n */\nexport function buildOpenGraphTags(og: OpenGraphConfig): MetaTag[] {\n const tags: MetaTag[] = [];\n\n if (og.type) tags.push({ property: 'og:type', content: og.type });\n if (og.url) tags.push({ property: 'og:url', content: og.url });\n if (og.title) tags.push({ property: 'og:title', content: og.title });\n if (og.description) tags.push({ property: 'og:description', content: og.description });\n if (og.siteName) tags.push({ property: 'og:site_name', content: og.siteName });\n if (og.locale) tags.push({ property: 'og:locale', content: og.locale });\n\n if (og.localeAlternate) {\n for (const alt of og.localeAlternate) {\n tags.push({ property: 'og:locale:alternate', content: alt });\n }\n }\n\n if (og.images) {\n for (const image of og.images) {\n tags.push({ property: 'og:image', content: image.url });\n if (image.secureUrl) tags.push({ property: 'og:image:secure_url', content: image.secureUrl });\n if (image.type) tags.push({ property: 'og:image:type', content: image.type });\n if (image.width) tags.push({ property: 'og:image:width', content: String(image.width) });\n if (image.height) tags.push({ property: 'og:image:height', content: String(image.height) });\n if (image.alt) tags.push({ property: 'og:image:alt', content: image.alt });\n }\n }\n\n if (og.videos) {\n for (const video of og.videos) {\n tags.push({ property: 'og:video', content: video.url });\n if (video.secureUrl) tags.push({ property: 'og:video:secure_url', content: video.secureUrl });\n if (video.type) tags.push({ property: 'og:video:type', content: video.type });\n if (video.width) tags.push({ property: 'og:video:width', content: String(video.width) });\n if (video.height) tags.push({ property: 'og:video:height', content: String(video.height) });\n }\n }\n\n // Article-specific tags\n if (og.article) {\n if (og.article.publishedTime)\n tags.push({ property: 'article:published_time', content: og.article.publishedTime });\n if (og.article.modifiedTime)\n tags.push({ property: 'article:modified_time', content: og.article.modifiedTime });\n if (og.article.expirationTime)\n tags.push({ property: 'article:expiration_time', content: og.article.expirationTime });\n if (og.article.section) tags.push({ property: 'article:section', content: og.article.section });\n if (og.article.authors) {\n for (const author of og.article.authors) {\n tags.push({ property: 'article:author', content: author });\n }\n }\n if (og.article.tags) {\n for (const tag of og.article.tags) {\n tags.push({ property: 'article:tag', content: tag });\n }\n }\n }\n\n // Profile-specific tags\n if (og.profile) {\n if (og.profile.firstName)\n tags.push({ property: 'profile:first_name', content: og.profile.firstName });\n if (og.profile.lastName)\n tags.push({ property: 'profile:last_name', content: og.profile.lastName });\n if (og.profile.username)\n tags.push({ property: 'profile:username', content: og.profile.username });\n if (og.profile.gender) tags.push({ property: 'profile:gender', content: og.profile.gender });\n }\n\n return tags;\n}\n\n/**\n * Build Twitter Card meta tags.\n */\nexport function buildTwitterTags(twitter: TwitterCardConfig): MetaTag[] {\n const tags: MetaTag[] = [];\n\n if (twitter.cardType) tags.push({ name: 'twitter:card', content: twitter.cardType });\n if (twitter.site) tags.push({ name: 'twitter:site', content: twitter.site });\n if (twitter.creator) tags.push({ name: 'twitter:creator', content: twitter.creator });\n if (twitter.title) tags.push({ name: 'twitter:title', content: twitter.title });\n if (twitter.description) tags.push({ name: 'twitter:description', content: twitter.description });\n if (twitter.image) tags.push({ name: 'twitter:image', content: twitter.image });\n if (twitter.imageAlt) tags.push({ name: 'twitter:image:alt', content: twitter.imageAlt });\n\n // Player card\n if (twitter.player) {\n tags.push({ name: 'twitter:player', content: twitter.player });\n if (twitter.playerWidth)\n tags.push({ name: 'twitter:player:width', content: String(twitter.playerWidth) });\n if (twitter.playerHeight)\n tags.push({ name: 'twitter:player:height', content: String(twitter.playerHeight) });\n if (twitter.playerStream)\n tags.push({ name: 'twitter:player:stream', content: twitter.playerStream });\n }\n\n // App card\n if (twitter.appIdIphone)\n tags.push({ name: 'twitter:app:id:iphone', content: twitter.appIdIphone });\n if (twitter.appIdIpad) tags.push({ name: 'twitter:app:id:ipad', content: twitter.appIdIpad });\n if (twitter.appIdGooglePlay)\n tags.push({ name: 'twitter:app:id:googleplay', content: twitter.appIdGooglePlay });\n if (twitter.appUrlIphone)\n tags.push({ name: 'twitter:app:url:iphone', content: twitter.appUrlIphone });\n if (twitter.appUrlIpad) tags.push({ name: 'twitter:app:url:ipad', content: twitter.appUrlIpad });\n if (twitter.appUrlGooglePlay)\n tags.push({ name: 'twitter:app:url:googleplay', content: twitter.appUrlGooglePlay });\n if (twitter.appNameIphone)\n tags.push({ name: 'twitter:app:name:iphone', content: twitter.appNameIphone });\n if (twitter.appNameIpad)\n tags.push({ name: 'twitter:app:name:ipad', content: twitter.appNameIpad });\n if (twitter.appNameGooglePlay)\n tags.push({ name: 'twitter:app:name:googleplay', content: twitter.appNameGooglePlay });\n\n return tags;\n}\n\n/**\n * Build hreflang link tags.\n */\nexport function buildHreflangTags(alternates: HreflangConfig[]): LinkTag[] {\n return alternates.map((alt) => ({\n rel: 'alternate',\n hreflang: alt.hrefLang,\n href: alt.href,\n }));\n}\n\n/**\n * Resolve the page title using template, default, and page-specific title.\n */\nexport function resolveTitle(config: SEOConfig): string | undefined {\n if (config.title && config.titleTemplate) {\n return config.titleTemplate.replace(/%s/g, config.title);\n }\n\n return config.title ?? config.defaultTitle;\n}\n","// @power-seo/core — Rate Limiting (Token Bucket)\n// ----------------------------------------------------------------------------\n\nexport interface TokenBucket {\n tokens: number;\n lastRefill: number;\n maxTokens: number;\n refillRate: number;\n}\n\nexport function createTokenBucket(requestsPerMinute: number): TokenBucket {\n return {\n tokens: requestsPerMinute,\n lastRefill: Date.now(),\n maxTokens: requestsPerMinute,\n refillRate: requestsPerMinute / 60_000,\n };\n}\n\nexport function consumeToken(bucket: TokenBucket): boolean {\n const now = Date.now();\n const elapsed = now - bucket.lastRefill;\n bucket.tokens = Math.min(bucket.maxTokens, bucket.tokens + elapsed * bucket.refillRate);\n bucket.lastRefill = now;\n\n if (bucket.tokens >= 1) {\n bucket.tokens -= 1;\n return true;\n }\n return false;\n}\n\nexport function getWaitTime(bucket: TokenBucket): number {\n if (bucket.tokens >= 1) return 0;\n return Math.ceil((1 - bucket.tokens) / bucket.refillRate);\n}\n\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n globalThis.setTimeout(resolve, ms);\n });\n}\n"],"mappings":"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","names":[]} | ||
| {"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/title-template.ts","../src/meta-validators.ts","../src/url-utils.ts","../src/text-stats.ts","../src/keyword-density.ts","../src/robots-builder.ts","../src/meta-builder.ts","../src/rate-limit.ts"],"sourcesContent":["// @power-seo/core — Main Entry Point\n// ----------------------------------------------------------------------------\n\n// Types\nexport type {\n MetaTag,\n LinkTag,\n OpenGraphType,\n OpenGraphImage,\n OpenGraphVideo,\n OpenGraphArticle,\n OpenGraphProfile,\n OpenGraphBook,\n OpenGraphConfig,\n TwitterCardType,\n TwitterCardConfig,\n RobotsDirective,\n HreflangConfig,\n SEOConfig,\n AuthorSocialProfile,\n AuthorCredential,\n AuthorEducation,\n AuthorPublication,\n AuthorInfo,\n EditorialReviewer,\n PreviouslyUsedKeyphrase,\n ContentAnalysisInput,\n AnalysisStatus,\n AnalysisResult,\n ContentAnalysisOutput,\n ReadabilityInput,\n ReadabilityOutput,\n TextStatistics,\n SitemapURL,\n SitemapImage,\n SitemapVideo,\n SitemapNews,\n SitemapConfig,\n RedirectStatusCode,\n RedirectRule,\n SchemaBase,\n SchemaGraphConfig,\n} from './types.js';\n\n// Constants\nexport {\n TITLE_MAX_PIXELS,\n TITLE_MAX_LENGTH,\n TITLE_MIN_LENGTH,\n META_DESCRIPTION_MAX_PIXELS,\n META_DESCRIPTION_MAX_LENGTH,\n META_DESCRIPTION_MIN_LENGTH,\n MIN_WORD_COUNT,\n RECOMMENDED_WORD_COUNT,\n OG_IMAGE,\n TWITTER_IMAGE,\n MAX_URL_LENGTH,\n KEYWORD_DENSITY,\n READABILITY,\n AI_CRAWLERS,\n CHAR_PIXEL_WIDTHS,\n DEFAULT_CHAR_WIDTH,\n SCHEMA_TYPES,\n} from './constants.js';\nexport type { SchemaType } from './constants.js';\n\n// Title Template Engine\nexport { applyTitleTemplate, createTitleTemplate } from './title-template.js';\nexport type { TitleTemplateVars } from './title-template.js';\n\n// Meta Validators\nexport { calculatePixelWidth, validateTitle, validateMetaDescription } from './meta-validators.js';\nexport type { ValidationSeverity, ValidationResult } from './meta-validators.js';\n\n// URL Utilities\nexport {\n resolveCanonical,\n normalizeUrl,\n ensureTrailingSlash,\n removeTrailingSlash,\n stripQueryParams,\n stripTrackingParams,\n extractSlug,\n isAbsoluteUrl,\n toSlug,\n} from './url-utils.js';\n\n// Text Statistics\nexport {\n stripHtml,\n extractFirstTagContent,\n extractTagContents,\n getWords,\n getSentences,\n getParagraphs,\n countSyllables,\n countTotalSyllables,\n getTextStatistics,\n} from './text-stats.js';\n\n// Keyword Density\nexport {\n calculateKeywordDensity,\n countKeywordOccurrences,\n analyzeKeyphraseOccurrences,\n} from './keyword-density.js';\nexport type { KeywordDensityResult, KeyphraseOccurrences } from './keyword-density.js';\n\n// Robots Builder\nexport { buildRobotsContent, parseRobotsContent } from './robots-builder.js';\n\n// Meta Builder\nexport {\n buildMetaTags,\n buildLinkTags,\n buildOpenGraphTags,\n buildTwitterTags,\n buildHreflangTags,\n resolveTitle,\n} from './meta-builder.js';\n\n// Rate Limiting\nexport { createTokenBucket, consumeToken, getWaitTime, sleep } from './rate-limit.js';\nexport type { TokenBucket } from './rate-limit.js';\n","// @power-seo/core — SEO Constants\n// ----------------------------------------------------------------------------\n\n/** Google SERP title max display width in pixels */\nexport const TITLE_MAX_PIXELS = 580;\n\n/** Google SERP title recommended max character length */\nexport const TITLE_MAX_LENGTH = 60;\n\n/** Google SERP title minimum recommended character length */\nexport const TITLE_MIN_LENGTH = 50;\n\n/** Google SERP meta description max display width in pixels (desktop) */\nexport const META_DESCRIPTION_MAX_PIXELS = 920;\n\n/** Meta description recommended max character length */\nexport const META_DESCRIPTION_MAX_LENGTH = 160;\n\n/** Meta description recommended min character length */\nexport const META_DESCRIPTION_MIN_LENGTH = 120;\n\n/** Recommended minimum word count (to avoid thin content) */\nexport const MIN_WORD_COUNT = 300;\n\n/** Recommended word count for blog posts */\nexport const RECOMMENDED_WORD_COUNT = 1000;\n\n/** Open Graph recommended image dimensions */\nexport const OG_IMAGE = {\n WIDTH: 1200,\n HEIGHT: 630,\n MIN_WIDTH: 200,\n MIN_HEIGHT: 200,\n ASPECT_RATIO: 1.91,\n} as const;\n\n/** Twitter Card recommended image dimensions */\nexport const TWITTER_IMAGE = {\n SUMMARY: { WIDTH: 144, HEIGHT: 144 },\n SUMMARY_LARGE: { WIDTH: 800, HEIGHT: 418 },\n} as const;\n\n/** Maximum URL length recommended for SEO */\nexport const MAX_URL_LENGTH = 75;\n\n/** Optimal keyword density range (percentage) */\nexport const KEYWORD_DENSITY = {\n MIN: 0.5,\n MAX: 2.5,\n OPTIMAL: 1.5,\n} as const;\n\n/** Readability thresholds */\nexport const READABILITY = {\n FLESCH_EASE_GOOD: 60,\n FLESCH_EASE_FAIR: 30,\n MAX_SENTENCE_LENGTH: 20,\n MAX_PARAGRAPH_WORDS: 150,\n MAX_PASSIVE_VOICE_PERCENT: 10,\n MIN_TRANSITION_WORD_PERCENT: 30,\n} as const;\n\n/** Common AI crawler user agents */\nexport const AI_CRAWLERS = [\n 'GPTBot',\n 'ChatGPT-User',\n 'CCBot',\n 'Google-Extended',\n 'ClaudeBot',\n 'anthropic-ai',\n 'Bytespider',\n 'PerplexityBot',\n 'Applebot-Extended',\n 'FacebookBot',\n 'cohere-ai',\n] as const;\n\n/** Approximate character widths for Google SERP pixel calculation (Arial font) */\nexport const CHAR_PIXEL_WIDTHS: Record<string, number> = {\n // Lowercase\n a: 6.67,\n b: 6.67,\n c: 6.0,\n d: 6.67,\n e: 6.67,\n f: 3.33,\n g: 6.67,\n h: 6.67,\n i: 2.67,\n j: 2.67,\n k: 6.0,\n l: 2.67,\n m: 10.0,\n n: 6.67,\n o: 6.67,\n p: 6.67,\n q: 6.67,\n r: 4.0,\n s: 6.0,\n t: 3.67,\n u: 6.67,\n v: 6.0,\n w: 9.33,\n x: 6.0,\n y: 6.0,\n z: 6.0,\n // Uppercase\n A: 8.0,\n B: 8.0,\n C: 8.67,\n D: 8.67,\n E: 8.0,\n F: 7.33,\n G: 9.33,\n H: 8.67,\n I: 3.33,\n J: 6.0,\n K: 8.0,\n L: 6.67,\n M: 10.0,\n N: 8.67,\n O: 9.33,\n P: 8.0,\n Q: 9.33,\n R: 8.67,\n S: 8.0,\n T: 7.33,\n U: 8.67,\n V: 8.0,\n W: 11.33,\n X: 8.0,\n Y: 8.0,\n Z: 7.33,\n // Numbers\n '0': 6.67,\n '1': 6.67,\n '2': 6.67,\n '3': 6.67,\n '4': 6.67,\n '5': 6.67,\n '6': 6.67,\n '7': 6.67,\n '8': 6.67,\n '9': 6.67,\n // Common punctuation\n ' ': 3.33,\n '.': 3.33,\n ',': 3.33,\n '!': 3.33,\n '?': 6.67,\n ':': 3.33,\n ';': 3.33,\n '-': 4.0,\n _: 6.67,\n '(': 4.0,\n ')': 4.0,\n '[': 3.33,\n ']': 3.33,\n '/': 3.33,\n '|': 3.11,\n '&': 8.0,\n '@': 12.17,\n '#': 6.67,\n $: 6.67,\n '%': 10.67,\n '+': 7.0,\n '=': 7.0,\n '\"': 4.27,\n \"'\": 2.3,\n};\n\n/** Default pixel width for unknown characters */\nexport const DEFAULT_CHAR_WIDTH = 6.67;\n\n/** Common Schema.org types used in SEO */\nexport const SCHEMA_TYPES = [\n 'Article',\n 'BlogPosting',\n 'NewsArticle',\n 'Product',\n 'FAQPage',\n 'HowTo',\n 'LocalBusiness',\n 'Organization',\n 'Person',\n 'WebSite',\n 'WebPage',\n 'BreadcrumbList',\n 'Event',\n 'Recipe',\n 'VideoObject',\n 'Course',\n 'JobPosting',\n 'SoftwareApplication',\n 'Review',\n 'AggregateRating',\n 'ItemList',\n 'CollectionPage',\n 'SearchAction',\n 'Service',\n 'MedicalWebPage',\n] as const;\n\nexport type SchemaType = (typeof SCHEMA_TYPES)[number];\n","// @power-seo/core — Title Template Engine\n// ----------------------------------------------------------------------------\n\nexport interface TitleTemplateVars {\n title?: string;\n siteName?: string;\n separator?: string;\n tagline?: string;\n page?: number;\n [key: string]: string | number | undefined;\n}\n\nconst DEFAULT_SEPARATOR = '|';\n\n/**\n * Apply a title template with variable substitution.\n *\n * Supports patterns like:\n * - `%title% | %siteName%`\n * - `%title% - %siteName%`\n * - `%title% | %siteName% - Page %page%`\n *\n * @example\n * ```ts\n * applyTitleTemplate('%title% | %siteName%', {\n * title: 'My Page',\n * siteName: 'My Site'\n * });\n * // => \"My Page | My Site\"\n * ```\n */\nexport function applyTitleTemplate(template: string, vars: TitleTemplateVars): string {\n if (!template) {\n return vars.title ?? '';\n }\n\n let result = template;\n\n // Replace all %variable% patterns\n result = result.replace(/%(\\w+)%/g, (_match, key: string) => {\n const value = vars[key];\n if (value === undefined || value === null) {\n return '';\n }\n return String(value);\n });\n\n // If no template used, fall back to simple separator pattern\n if (result === template && vars.title) {\n const separator = vars.separator ?? DEFAULT_SEPARATOR;\n if (vars.siteName) {\n return `${vars.title} ${separator} ${vars.siteName}`;\n }\n return vars.title;\n }\n\n // Clean up multiple consecutive spaces\n result = result.replace(/\\s{2,}/g, ' ').trim();\n\n // Clean up dangling separators at start/end\n result = result.replace(/^[\\s|·—–-]+|[\\s|·—–-]+$/g, '').trim();\n\n return result;\n}\n\n/**\n * Create a title template function with pre-configured defaults.\n *\n * @example\n * ```ts\n * const makeTitle = createTitleTemplate({\n * siteName: 'My Site',\n * separator: '—'\n * });\n *\n * makeTitle('About Us');\n * // => \"About Us — My Site\"\n * ```\n */\nexport function createTitleTemplate(\n defaults: Omit<TitleTemplateVars, 'title'> & { template?: string },\n) {\n const { template = '%title% %separator% %siteName%', ...defaultVars } = defaults;\n\n return (title: string, overrides?: Partial<TitleTemplateVars>): string => {\n return applyTitleTemplate(template, {\n separator: DEFAULT_SEPARATOR,\n ...defaultVars,\n ...overrides,\n title,\n });\n };\n}\n","// @power-seo/core — Meta Tag Validators\n// ----------------------------------------------------------------------------\n\nimport {\n TITLE_MAX_LENGTH,\n TITLE_MIN_LENGTH,\n TITLE_MAX_PIXELS,\n META_DESCRIPTION_MAX_LENGTH,\n META_DESCRIPTION_MIN_LENGTH,\n META_DESCRIPTION_MAX_PIXELS,\n CHAR_PIXEL_WIDTHS,\n DEFAULT_CHAR_WIDTH,\n} from './constants.js';\n\nexport type ValidationSeverity = 'error' | 'warning' | 'info';\n\nexport interface ValidationResult {\n valid: boolean;\n severity: ValidationSeverity;\n message: string;\n charCount?: number;\n pixelWidth?: number;\n}\n\n/**\n * Calculate the pixel width of a string using Google SERP font metrics.\n */\nexport function calculatePixelWidth(text: string): number {\n let width = 0;\n for (const char of text) {\n width += CHAR_PIXEL_WIDTHS[char] ?? DEFAULT_CHAR_WIDTH;\n }\n return Math.round(width * 100) / 100;\n}\n\n/**\n * Validate a page title for SEO best practices.\n *\n * Checks character length and pixel width against Google SERP limits.\n */\nexport function validateTitle(title: string): ValidationResult {\n if (!title || title.trim().length === 0) {\n return {\n valid: false,\n severity: 'error',\n message: 'Title is empty. Every page must have a unique title.',\n charCount: 0,\n pixelWidth: 0,\n };\n }\n\n const trimmed = title.trim();\n const charCount = trimmed.length;\n const pixelWidth = calculatePixelWidth(trimmed);\n\n if (pixelWidth > TITLE_MAX_PIXELS) {\n return {\n valid: false,\n severity: 'warning',\n message: `Title is ${charCount} characters (${pixelWidth}px) and will be truncated in Google search results. Keep it under ${TITLE_MAX_PIXELS}px (~${TITLE_MAX_LENGTH} characters).`,\n charCount,\n pixelWidth,\n };\n }\n\n if (charCount < TITLE_MIN_LENGTH) {\n return {\n valid: true,\n severity: 'warning',\n message: `Title is only ${charCount} characters. Consider making it at least ${TITLE_MIN_LENGTH} characters for better SEO.`,\n charCount,\n pixelWidth,\n };\n }\n\n if (charCount > TITLE_MAX_LENGTH) {\n return {\n valid: true,\n severity: 'info',\n message: `Title is ${charCount} characters but fits within ${TITLE_MAX_PIXELS}px. The pixel width (${pixelWidth}px) is what matters for truncation.`,\n charCount,\n pixelWidth,\n };\n }\n\n return {\n valid: true,\n severity: 'info',\n message: `Title is ${charCount} characters (${pixelWidth}px). Good length.`,\n charCount,\n pixelWidth,\n };\n}\n\n/**\n * Validate a meta description for SEO best practices.\n *\n * Checks character length and pixel width against Google SERP limits.\n */\nexport function validateMetaDescription(description: string): ValidationResult {\n if (!description || description.trim().length === 0) {\n return {\n valid: false,\n severity: 'warning',\n message:\n 'Meta description is empty. Without one, Google will auto-generate a snippet from page content.',\n charCount: 0,\n pixelWidth: 0,\n };\n }\n\n const trimmed = description.trim();\n const charCount = trimmed.length;\n const pixelWidth = calculatePixelWidth(trimmed);\n\n if (pixelWidth > META_DESCRIPTION_MAX_PIXELS) {\n return {\n valid: false,\n severity: 'warning',\n message: `Meta description is ${charCount} characters (${pixelWidth}px) and will be truncated. Keep it under ${META_DESCRIPTION_MAX_PIXELS}px (~${META_DESCRIPTION_MAX_LENGTH} characters).`,\n charCount,\n pixelWidth,\n };\n }\n\n if (charCount < META_DESCRIPTION_MIN_LENGTH) {\n return {\n valid: true,\n severity: 'warning',\n message: `Meta description is only ${charCount} characters. Aim for ${META_DESCRIPTION_MIN_LENGTH}-${META_DESCRIPTION_MAX_LENGTH} characters to maximize SERP real estate.`,\n charCount,\n pixelWidth,\n };\n }\n\n return {\n valid: true,\n severity: 'info',\n message: `Meta description is ${charCount} characters (${pixelWidth}px). Good length.`,\n charCount,\n pixelWidth,\n };\n}\n","// @power-seo/core — URL Utilities\n// ----------------------------------------------------------------------------\n\n/**\n * Resolve a canonical URL from a base URL and path.\n *\n * @example\n * ```ts\n * resolveCanonical('https://example.com', '/blog/post');\n * // => \"https://example.com/blog/post\"\n * ```\n */\nexport function resolveCanonical(baseUrl: string, path?: string): string {\n if (!path) {\n return normalizeUrl(baseUrl);\n }\n\n // If path is already absolute URL, return it normalized\n if (path.startsWith('http://') || path.startsWith('https://')) {\n return normalizeUrl(path);\n }\n\n let base = baseUrl;\n while (base.endsWith('/')) base = base.slice(0, -1);\n const cleanPath = path.startsWith('/') ? path : `/${path}`;\n\n return normalizeUrl(`${base}${cleanPath}`);\n}\n\n/**\n * Normalize a URL by removing default ports, double slashes, etc.\n */\nexport function normalizeUrl(url: string): string {\n try {\n const parsed = new globalThis.URL(url);\n\n // Remove default ports\n if (\n (parsed.protocol === 'https:' && parsed.port === '443') ||\n (parsed.protocol === 'http:' && parsed.port === '80')\n ) {\n parsed.port = '';\n }\n\n // Remove trailing slash from path (except root)\n if (parsed.pathname !== '/' && parsed.pathname.endsWith('/')) {\n parsed.pathname = parsed.pathname.replace(/\\/+$/, '');\n }\n\n // Remove double slashes in path\n parsed.pathname = parsed.pathname.replace(/\\/\\/+/g, '/');\n\n return parsed.toString();\n } catch {\n return url;\n }\n}\n\n/**\n * Ensure a URL has a trailing slash (useful for directories).\n */\nexport function ensureTrailingSlash(url: string): string {\n if (url.endsWith('/')) return url;\n\n try {\n const parsed = new globalThis.URL(url);\n // Don't add trailing slash to URLs with file extensions\n const lastSegment = parsed.pathname.split('/').pop() ?? '';\n if (lastSegment.includes('.')) return url;\n parsed.pathname += '/';\n return parsed.toString();\n } catch {\n return url.endsWith('/') ? url : `${url}/`;\n }\n}\n\n/**\n * Remove trailing slash from a URL.\n */\nexport function removeTrailingSlash(url: string): string {\n try {\n const parsed = new globalThis.URL(url);\n if (parsed.pathname !== '/' && parsed.pathname.endsWith('/')) {\n parsed.pathname = parsed.pathname.replace(/\\/+$/, '');\n }\n return parsed.toString();\n } catch {\n return url.endsWith('/') && url !== '/' ? url.slice(0, -1) : url;\n }\n}\n\n/**\n * Strip query parameters from a URL, optionally keeping specific ones.\n *\n * @example\n * ```ts\n * stripQueryParams('https://example.com/page?utm_source=twitter&id=123', ['id']);\n * // => \"https://example.com/page?id=123\"\n * ```\n */\nexport function stripQueryParams(url: string, keepParams?: string[]): string {\n try {\n const parsed = new globalThis.URL(url);\n\n if (!keepParams || keepParams.length === 0) {\n parsed.search = '';\n return parsed.toString();\n }\n\n const newParams = new globalThis.URLSearchParams();\n for (const param of keepParams) {\n const value = parsed.searchParams.get(param);\n if (value !== null) {\n newParams.set(param, value);\n }\n }\n\n parsed.search = newParams.toString() ? `?${newParams.toString()}` : '';\n return parsed.toString();\n } catch {\n return url;\n }\n}\n\n/**\n * Strip common tracking parameters (UTM, fbclid, gclid, etc.) from a URL.\n */\nexport function stripTrackingParams(url: string): string {\n const trackingParams = [\n 'utm_source',\n 'utm_medium',\n 'utm_campaign',\n 'utm_term',\n 'utm_content',\n 'fbclid',\n 'gclid',\n 'gclsrc',\n 'dclid',\n 'msclkid',\n 'ref',\n 'mc_cid',\n 'mc_eid',\n '_ga',\n '_gl',\n 'yclid',\n 'twclid',\n 'igshid',\n ];\n\n try {\n const parsed = new globalThis.URL(url);\n for (const param of trackingParams) {\n parsed.searchParams.delete(param);\n }\n parsed.search = parsed.searchParams.toString() ? `?${parsed.searchParams.toString()}` : '';\n return parsed.toString();\n } catch {\n return url;\n }\n}\n\n/**\n * Extract the slug from a URL path.\n *\n * @example\n * ```ts\n * extractSlug('https://example.com/blog/my-post');\n * // => \"my-post\"\n * ```\n */\nexport function extractSlug(url: string): string {\n try {\n const parsed = new globalThis.URL(url);\n const segments = parsed.pathname.split('/').filter(Boolean);\n return segments[segments.length - 1] ?? '';\n } catch {\n const segments = url.split('/').filter(Boolean);\n return segments[segments.length - 1] ?? '';\n }\n}\n\n/**\n * Check if a URL is relative or absolute.\n */\nexport function isAbsoluteUrl(url: string): boolean {\n return /^https?:\\/\\//i.test(url);\n}\n\n/**\n * Convert a string to a URL-safe slug.\n *\n * @example\n * ```ts\n * toSlug('My Blog Post Title!');\n * // => \"my-blog-post-title\"\n * ```\n */\nexport function toSlug(text: string): string {\n return text\n .toLowerCase()\n .normalize('NFD')\n .replace(/[\\u0300-\\u036f]/g, '') // Remove diacritics\n .replace(/[^a-z0-9\\s-]/g, '') // Remove non-alphanumeric\n .replace(/\\s+/g, '-') // Spaces to hyphens\n .replace(/-+/g, '-') // Collapse multiple hyphens\n .replace(/^-|-$/g, ''); // Trim hyphens\n}\n","// @power-seo/core — Text Statistics Engine\n// ----------------------------------------------------------------------------\n\nimport type { TextStatistics } from './types.js';\n\n/**\n * Remove all occurrences of a paired HTML block tag (e.g. script, style)\n * using plain string search to avoid regex ReDoS on crafted input.\n */\nfunction removeBlockTag(html: string, tag: string): string {\n const open = `<${tag}`;\n const close = `</${tag}>`;\n let result = html;\n let start = result.toLowerCase().indexOf(open);\n while (start !== -1) {\n const end = result.toLowerCase().indexOf(close, start);\n if (end === -1) {\n // No closing tag found — remove everything from the opening tag onwards.\n result = result.slice(0, start);\n break;\n }\n result = result.slice(0, start) + result.slice(end + close.length);\n start = result.toLowerCase().indexOf(open, start);\n }\n return result;\n}\n\n/**\n * Remove HTML tags using a character-by-character scanner (no regex).\n * Avoids ReDoS from patterns like /<[^>]+>/g on crafted inputs.\n */\nfunction removeHtmlTags(html: string): string {\n let result = '';\n let i = 0;\n while (i < html.length) {\n if (html[i] === '<') {\n const end = html.indexOf('>', i + 1);\n if (end === -1) break; // malformed — stop\n result += ' ';\n i = end + 1;\n } else {\n result += html[i]!;\n i++;\n }\n }\n return result;\n}\n\n/**\n * Extract the inner HTML of the first occurrence of a tag (e.g. 'p', 'h1').\n * Uses plain string search — no regex, no ReDoS.\n */\nexport function extractFirstTagContent(html: string, tag: string): string | null {\n const lc = html.toLowerCase();\n const openTag = `<${tag}`;\n const closeTag = `</${tag}>`;\n const start = lc.indexOf(openTag);\n if (start === -1) return null;\n const contentStart = lc.indexOf('>', start);\n if (contentStart === -1) return null;\n const end = lc.indexOf(closeTag, contentStart + 1);\n if (end === -1) return null;\n return html.slice(contentStart + 1, end);\n}\n\n/**\n * Extract inner HTML of all occurrences of a tag (e.g. 'h2').\n * Uses plain string search — no regex, no ReDoS.\n */\nexport function extractTagContents(html: string, tag: string): string[] {\n const results: string[] = [];\n const lc = html.toLowerCase();\n const openTag = `<${tag}`;\n const closeTag = `</${tag}>`;\n let pos = 0;\n while (pos < lc.length) {\n const start = lc.indexOf(openTag, pos);\n if (start === -1) break;\n const contentStart = lc.indexOf('>', start);\n if (contentStart === -1) break;\n const end = lc.indexOf(closeTag, contentStart + 1);\n if (end === -1) break;\n results.push(html.slice(contentStart + 1, end));\n pos = end + closeTag.length;\n }\n return results;\n}\n\n/**\n * Strip HTML tags from content, returning plain text.\n */\nexport function stripHtml(html: string): string {\n // Remove script and style blocks without regex to avoid ReDoS.\n let result = removeBlockTag(html, 'script');\n result = removeBlockTag(result, 'style');\n\n // Remove remaining tags with a character scanner (no regex) to avoid ReDoS.\n result = removeHtmlTags(result);\n\n return result\n .replace(/ /g, ' ')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n .replace(/&/g, '&') // decode & last to prevent double-unescaping (&lt; → < → <)\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\n/**\n * Split text into words.\n */\nexport function getWords(text: string): string[] {\n const cleaned = stripHtml(text);\n if (!cleaned) return [];\n return cleaned.split(/\\s+/).filter((w) => w.length > 0);\n}\n\n/**\n * Split text into sentences using common sentence boundary rules.\n */\nexport function getSentences(text: string): string[] {\n const cleaned = stripHtml(text);\n if (!cleaned) return [];\n\n // Split on sentence-ending punctuation followed by space or end of string\n const sentences = cleaned\n .split(/(?<=[.!?])\\s+/)\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n\n return sentences;\n}\n\n/**\n * Split HTML content into paragraphs.\n */\nexport function getParagraphs(html: string): string[] {\n // Split by paragraph tags or double newlines\n const blocks = html\n .split(/<\\/p>|<br\\s*\\/?>.*<br\\s*\\/?>/gi)\n .map((block) => stripHtml(block).trim())\n .filter((block) => block.length > 0);\n\n if (blocks.length === 0) {\n // Fall back to double newline splitting\n return stripHtml(html)\n .split(/\\n\\s*\\n/)\n .map((p) => p.trim())\n .filter((p) => p.length > 0);\n }\n\n return blocks;\n}\n\n/**\n * Count syllables in a word using a heuristic approach.\n *\n * This is a simplified English syllable counter. For production use with\n * high accuracy, consider using the `syllable` npm package.\n */\nexport function countSyllables(word: string): number {\n const w = word.toLowerCase().replace(/[^a-z]/g, '');\n if (w.length <= 2) return 1;\n\n // Special suffixes\n let count = 0;\n const vowels = 'aeiouy';\n let prevIsVowel = false;\n\n for (let i = 0; i < w.length; i++) {\n const isVowel = vowels.includes(w[i]!);\n if (isVowel && !prevIsVowel) {\n count++;\n }\n prevIsVowel = isVowel;\n }\n\n // Handle silent 'e' at end\n if (w.endsWith('e') && !w.endsWith('le') && count > 1) {\n count--;\n }\n\n // Handle common suffixes\n if (w.endsWith('es') || w.endsWith('ed')) {\n // Don't count 'es' or 'ed' as extra unless preceded by 't' or 'd'\n const beforeSuffix = w[w.length - (w.endsWith('es') ? 3 : 3)];\n if (beforeSuffix !== 't' && beforeSuffix !== 'd') {\n // Already handled by vowel counting\n }\n }\n\n return Math.max(1, count);\n}\n\n/**\n * Count total syllables in text.\n */\nexport function countTotalSyllables(text: string): number {\n const words = getWords(text);\n return words.reduce((total, word) => total + countSyllables(word), 0);\n}\n\n/**\n * Get comprehensive text statistics.\n *\n * @example\n * ```ts\n * const stats = getTextStatistics('<p>Hello world. This is a test.</p>');\n * // => { wordCount: 7, sentenceCount: 2, ... }\n * ```\n */\nexport function getTextStatistics(content: string): TextStatistics {\n const words = getWords(content);\n const sentences = getSentences(content);\n const paragraphs = getParagraphs(content);\n const syllableCount = countTotalSyllables(content);\n const plainText = stripHtml(content);\n\n const wordCount = words.length;\n const sentenceCount = sentences.length;\n const paragraphCount = paragraphs.length;\n\n return {\n wordCount,\n sentenceCount,\n paragraphCount,\n syllableCount,\n characterCount: plainText.length,\n avgWordsPerSentence: sentenceCount > 0 ? Math.round((wordCount / sentenceCount) * 10) / 10 : 0,\n avgSyllablesPerWord: wordCount > 0 ? Math.round((syllableCount / wordCount) * 100) / 100 : 0,\n };\n}\n","// @power-seo/core — Keyword Density Calculator\n// ----------------------------------------------------------------------------\n\nimport { getWords, stripHtml, extractFirstTagContent, extractTagContents } from './text-stats.js';\n\nexport interface KeywordDensityResult {\n keyword: string;\n count: number;\n density: number;\n totalWords: number;\n}\n\nexport interface KeyphraseOccurrences {\n inTitle: boolean;\n inMetaDescription: boolean;\n inFirstParagraph: boolean;\n inH1: boolean;\n inHeadings: number;\n inContent: number;\n inSlug: boolean;\n inAltText: number;\n density: number;\n totalWords: number;\n}\n\n/**\n * Calculate keyword density as a percentage.\n *\n * @example\n * ```ts\n * calculateKeywordDensity('react seo', 'Learn React SEO for your React application. React SEO matters.');\n * // => { keyword: 'react seo', count: 2, density: 20.0, totalWords: 10 }\n * ```\n */\nexport function calculateKeywordDensity(keyword: string, content: string): KeywordDensityResult {\n const plainText = stripHtml(content).toLowerCase();\n const words = getWords(content);\n const normalizedKeyword = keyword.toLowerCase().trim();\n\n if (!normalizedKeyword || words.length === 0) {\n return { keyword, count: 0, density: 0, totalWords: words.length };\n }\n\n // Count occurrences of the keyphrase\n const count = countKeywordOccurrences(plainText, normalizedKeyword);\n\n // Density = (keyword occurrences / total words) * 100\n // For multi-word keyphrases, we count phrase occurrences\n const keywordWordCount = normalizedKeyword.split(/\\s+/).length;\n const density =\n words.length > 0 ? Math.round(((count * keywordWordCount) / words.length) * 1000) / 10 : 0;\n\n return {\n keyword,\n count,\n density,\n totalWords: words.length,\n };\n}\n\n/**\n * Count occurrences of a keyword/keyphrase in text (case-insensitive).\n */\nexport function countKeywordOccurrences(text: string, keyword: string): number {\n const normalizedText = text.toLowerCase();\n const normalizedKeyword = keyword.toLowerCase().trim();\n\n if (!normalizedKeyword) return 0;\n\n // Use word boundary matching for single words\n const escaped = normalizedKeyword.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const regex = new RegExp(`\\\\b${escaped}\\\\b`, 'gi');\n const matches = normalizedText.match(regex);\n\n return matches?.length ?? 0;\n}\n\n/**\n * Analyze where a focus keyphrase appears throughout page content.\n */\nexport function analyzeKeyphraseOccurrences(config: {\n keyphrase: string;\n title?: string;\n metaDescription?: string;\n content: string;\n slug?: string;\n images?: Array<{ alt?: string }>;\n}): KeyphraseOccurrences {\n const { keyphrase, title, metaDescription, content, slug, images } = config;\n const kp = keyphrase.toLowerCase().trim();\n\n const plainContent = stripHtml(content).toLowerCase();\n const words = getWords(content);\n\n // Check title\n const inTitle = title ? title.toLowerCase().includes(kp) : false;\n\n // Check meta description\n const inMetaDescription = metaDescription ? metaDescription.toLowerCase().includes(kp) : false;\n\n // Check first paragraph — use string-based extraction to avoid regex ReDoS\n const firstParaInner = extractFirstTagContent(content, 'p');\n const firstParagraph = firstParaInner !== null\n ? stripHtml(firstParaInner).toLowerCase()\n : (plainContent.split(/\\n\\n/)[0]?.toLowerCase() ?? '');\n const inFirstParagraph = firstParagraph.includes(kp);\n\n // Check H1 — use string-based extraction to avoid regex ReDoS\n const h1Inner = extractFirstTagContent(content, 'h1');\n const inH1 = h1Inner !== null\n ? stripHtml(h1Inner).toLowerCase().includes(kp)\n : false;\n\n // Count in headings (h2-h6) — use string-based extraction to avoid regex ReDoS\n let inHeadings = 0;\n for (let level = 2; level <= 6; level++) {\n for (const inner of extractTagContents(content, `h${level}`)) {\n if (stripHtml(inner).toLowerCase().includes(kp)) {\n inHeadings++;\n }\n }\n }\n\n // Count in content\n const inContent = countKeywordOccurrences(plainContent, kp);\n\n // Check slug\n const inSlug = slug ? slug.toLowerCase().includes(kp.replace(/\\s+/g, '-')) : false;\n\n // Count in alt text\n const inAltText = images ? images.filter((img) => img.alt?.toLowerCase().includes(kp)).length : 0;\n\n // Calculate density\n const keywordWordCount = kp.split(/\\s+/).length;\n const density =\n words.length > 0 ? Math.round(((inContent * keywordWordCount) / words.length) * 1000) / 10 : 0;\n\n return {\n inTitle,\n inMetaDescription,\n inFirstParagraph,\n inH1,\n inHeadings,\n inContent,\n inSlug,\n inAltText,\n density,\n totalWords: words.length,\n };\n}\n","// @power-seo/core — Robots Directive Builder\n// ----------------------------------------------------------------------------\n\nimport type { RobotsDirective } from './types.js';\n\n/**\n * Build a robots meta content string from a RobotsDirective config.\n *\n * @example\n * ```ts\n * buildRobotsContent({ index: false, follow: true, maxSnippet: 150 });\n * // => \"noindex, follow, max-snippet:150\"\n * ```\n */\nexport function buildRobotsContent(directive: RobotsDirective): string {\n const parts: string[] = [];\n\n if (directive.index === false) {\n parts.push('noindex');\n } else if (directive.index === true) {\n parts.push('index');\n }\n\n if (directive.follow === false) {\n parts.push('nofollow');\n } else if (directive.follow === true) {\n parts.push('follow');\n }\n\n if (directive.noarchive) parts.push('noarchive');\n if (directive.nosnippet) parts.push('nosnippet');\n if (directive.noimageindex) parts.push('noimageindex');\n if (directive.notranslate) parts.push('notranslate');\n\n if (directive.maxSnippet !== undefined) {\n parts.push(`max-snippet:${directive.maxSnippet}`);\n }\n\n if (directive.maxImagePreview !== undefined) {\n parts.push(`max-image-preview:${directive.maxImagePreview}`);\n }\n\n if (directive.maxVideoPreview !== undefined) {\n parts.push(`max-video-preview:${directive.maxVideoPreview}`);\n }\n\n if (directive.unavailableAfter) {\n parts.push(`unavailable_after:${directive.unavailableAfter}`);\n }\n\n return parts.join(', ');\n}\n\n/**\n * Parse a robots meta content string into a RobotsDirective.\n */\nexport function parseRobotsContent(content: string): RobotsDirective {\n const directive: RobotsDirective = {};\n const parts = content.split(',').map((p) => p.trim().toLowerCase());\n\n for (const part of parts) {\n if (part === 'noindex') directive.index = false;\n else if (part === 'index') directive.index = true;\n else if (part === 'nofollow') directive.follow = false;\n else if (part === 'follow') directive.follow = true;\n else if (part === 'noarchive') directive.noarchive = true;\n else if (part === 'nosnippet') directive.nosnippet = true;\n else if (part === 'noimageindex') directive.noimageindex = true;\n else if (part === 'notranslate') directive.notranslate = true;\n else if (part.startsWith('max-snippet:')) {\n directive.maxSnippet = parseInt(part.split(':')[1] ?? '0', 10);\n } else if (part.startsWith('max-image-preview:')) {\n const value = part.split(':')[1] as 'none' | 'standard' | 'large';\n directive.maxImagePreview = value;\n } else if (part.startsWith('max-video-preview:')) {\n directive.maxVideoPreview = parseInt(part.split(':')[1] ?? '0', 10);\n } else if (part.startsWith('unavailable_after:')) {\n directive.unavailableAfter = part.substring('unavailable_after:'.length);\n }\n }\n\n return directive;\n}\n","// @power-seo/core — Meta Tag Builder\n// ----------------------------------------------------------------------------\n\nimport type {\n MetaTag,\n LinkTag,\n SEOConfig,\n OpenGraphConfig,\n TwitterCardConfig,\n HreflangConfig,\n} from './types.js';\nimport { buildRobotsContent } from './robots-builder.js';\n\n/**\n * Build an array of meta tags from an SEO configuration.\n * This is the framework-agnostic engine — frameworks render these as actual HTML.\n */\nexport function buildMetaTags(config: SEOConfig): MetaTag[] {\n const tags: MetaTag[] = [];\n\n // Description\n if (config.description) {\n tags.push({ name: 'description', content: config.description });\n }\n\n // Robots\n if (config.noindex || config.nofollow || config.robots) {\n const robotsContent = buildRobotsContent({\n index: config.noindex ? false : config.robots?.index,\n follow: config.nofollow ? false : config.robots?.follow,\n ...config.robots,\n });\n if (robotsContent) {\n tags.push({ name: 'robots', content: robotsContent });\n }\n }\n\n // Open Graph\n if (config.openGraph) {\n tags.push(...buildOpenGraphTags(config.openGraph));\n }\n\n // Twitter Card\n if (config.twitter) {\n tags.push(...buildTwitterTags(config.twitter));\n }\n\n // Additional meta tags\n if (config.additionalMetaTags) {\n tags.push(...config.additionalMetaTags);\n }\n\n return tags;\n}\n\n/**\n * Build an array of link tags from an SEO configuration.\n */\nexport function buildLinkTags(config: SEOConfig): LinkTag[] {\n const tags: LinkTag[] = [];\n\n // Canonical\n if (config.canonical) {\n tags.push({ rel: 'canonical', href: config.canonical });\n }\n\n // Language alternates (hreflang)\n if (config.languageAlternates) {\n tags.push(...buildHreflangTags(config.languageAlternates));\n }\n\n // Additional link tags\n if (config.additionalLinkTags) {\n tags.push(...config.additionalLinkTags);\n }\n\n return tags;\n}\n\n/**\n * Build Open Graph meta tags.\n */\nexport function buildOpenGraphTags(og: OpenGraphConfig): MetaTag[] {\n const tags: MetaTag[] = [];\n\n if (og.type) tags.push({ property: 'og:type', content: og.type });\n if (og.url) tags.push({ property: 'og:url', content: og.url });\n if (og.title) tags.push({ property: 'og:title', content: og.title });\n if (og.description) tags.push({ property: 'og:description', content: og.description });\n if (og.siteName) tags.push({ property: 'og:site_name', content: og.siteName });\n if (og.locale) tags.push({ property: 'og:locale', content: og.locale });\n\n if (og.localeAlternate) {\n for (const alt of og.localeAlternate) {\n tags.push({ property: 'og:locale:alternate', content: alt });\n }\n }\n\n if (og.images) {\n for (const image of og.images) {\n tags.push({ property: 'og:image', content: image.url });\n if (image.secureUrl) tags.push({ property: 'og:image:secure_url', content: image.secureUrl });\n if (image.type) tags.push({ property: 'og:image:type', content: image.type });\n if (image.width) tags.push({ property: 'og:image:width', content: String(image.width) });\n if (image.height) tags.push({ property: 'og:image:height', content: String(image.height) });\n if (image.alt) tags.push({ property: 'og:image:alt', content: image.alt });\n }\n }\n\n if (og.videos) {\n for (const video of og.videos) {\n tags.push({ property: 'og:video', content: video.url });\n if (video.secureUrl) tags.push({ property: 'og:video:secure_url', content: video.secureUrl });\n if (video.type) tags.push({ property: 'og:video:type', content: video.type });\n if (video.width) tags.push({ property: 'og:video:width', content: String(video.width) });\n if (video.height) tags.push({ property: 'og:video:height', content: String(video.height) });\n }\n }\n\n // Article-specific tags\n if (og.article) {\n if (og.article.publishedTime)\n tags.push({ property: 'article:published_time', content: og.article.publishedTime });\n if (og.article.modifiedTime)\n tags.push({ property: 'article:modified_time', content: og.article.modifiedTime });\n if (og.article.expirationTime)\n tags.push({ property: 'article:expiration_time', content: og.article.expirationTime });\n if (og.article.section) tags.push({ property: 'article:section', content: og.article.section });\n if (og.article.authors) {\n for (const author of og.article.authors) {\n tags.push({ property: 'article:author', content: author });\n }\n }\n if (og.article.tags) {\n for (const tag of og.article.tags) {\n tags.push({ property: 'article:tag', content: tag });\n }\n }\n }\n\n // Profile-specific tags\n if (og.profile) {\n if (og.profile.firstName)\n tags.push({ property: 'profile:first_name', content: og.profile.firstName });\n if (og.profile.lastName)\n tags.push({ property: 'profile:last_name', content: og.profile.lastName });\n if (og.profile.username)\n tags.push({ property: 'profile:username', content: og.profile.username });\n if (og.profile.gender) tags.push({ property: 'profile:gender', content: og.profile.gender });\n }\n\n return tags;\n}\n\n/**\n * Build Twitter Card meta tags.\n */\nexport function buildTwitterTags(twitter: TwitterCardConfig): MetaTag[] {\n const tags: MetaTag[] = [];\n\n if (twitter.cardType) tags.push({ name: 'twitter:card', content: twitter.cardType });\n if (twitter.site) tags.push({ name: 'twitter:site', content: twitter.site });\n if (twitter.creator) tags.push({ name: 'twitter:creator', content: twitter.creator });\n if (twitter.title) tags.push({ name: 'twitter:title', content: twitter.title });\n if (twitter.description) tags.push({ name: 'twitter:description', content: twitter.description });\n if (twitter.image) tags.push({ name: 'twitter:image', content: twitter.image });\n if (twitter.imageAlt) tags.push({ name: 'twitter:image:alt', content: twitter.imageAlt });\n\n // Player card\n if (twitter.player) {\n tags.push({ name: 'twitter:player', content: twitter.player });\n if (twitter.playerWidth)\n tags.push({ name: 'twitter:player:width', content: String(twitter.playerWidth) });\n if (twitter.playerHeight)\n tags.push({ name: 'twitter:player:height', content: String(twitter.playerHeight) });\n if (twitter.playerStream)\n tags.push({ name: 'twitter:player:stream', content: twitter.playerStream });\n }\n\n // App card\n if (twitter.appIdIphone)\n tags.push({ name: 'twitter:app:id:iphone', content: twitter.appIdIphone });\n if (twitter.appIdIpad) tags.push({ name: 'twitter:app:id:ipad', content: twitter.appIdIpad });\n if (twitter.appIdGooglePlay)\n tags.push({ name: 'twitter:app:id:googleplay', content: twitter.appIdGooglePlay });\n if (twitter.appUrlIphone)\n tags.push({ name: 'twitter:app:url:iphone', content: twitter.appUrlIphone });\n if (twitter.appUrlIpad) tags.push({ name: 'twitter:app:url:ipad', content: twitter.appUrlIpad });\n if (twitter.appUrlGooglePlay)\n tags.push({ name: 'twitter:app:url:googleplay', content: twitter.appUrlGooglePlay });\n if (twitter.appNameIphone)\n tags.push({ name: 'twitter:app:name:iphone', content: twitter.appNameIphone });\n if (twitter.appNameIpad)\n tags.push({ name: 'twitter:app:name:ipad', content: twitter.appNameIpad });\n if (twitter.appNameGooglePlay)\n tags.push({ name: 'twitter:app:name:googleplay', content: twitter.appNameGooglePlay });\n\n return tags;\n}\n\n/**\n * Build hreflang link tags.\n */\nexport function buildHreflangTags(alternates: HreflangConfig[]): LinkTag[] {\n return alternates.map((alt) => ({\n rel: 'alternate',\n hreflang: alt.hrefLang,\n href: alt.href,\n }));\n}\n\n/**\n * Resolve the page title using template, default, and page-specific title.\n */\nexport function resolveTitle(config: SEOConfig): string | undefined {\n if (config.title && config.titleTemplate) {\n return config.titleTemplate.replace(/%s/g, config.title);\n }\n\n return config.title ?? config.defaultTitle;\n}\n","// @power-seo/core — Rate Limiting (Token Bucket)\n// ----------------------------------------------------------------------------\n\nexport interface TokenBucket {\n tokens: number;\n lastRefill: number;\n maxTokens: number;\n refillRate: number;\n}\n\nexport function createTokenBucket(requestsPerMinute: number): TokenBucket {\n return {\n tokens: requestsPerMinute,\n lastRefill: Date.now(),\n maxTokens: requestsPerMinute,\n refillRate: requestsPerMinute / 60_000,\n };\n}\n\nexport function consumeToken(bucket: TokenBucket): boolean {\n const now = Date.now();\n const elapsed = now - bucket.lastRefill;\n bucket.tokens = Math.min(bucket.maxTokens, bucket.tokens + elapsed * bucket.refillRate);\n bucket.lastRefill = now;\n\n if (bucket.tokens >= 1) {\n bucket.tokens -= 1;\n return true;\n }\n return false;\n}\n\nexport function getWaitTime(bucket: TokenBucket): number {\n if (bucket.tokens >= 1) return 0;\n return Math.ceil((1 - bucket.tokens) / bucket.refillRate);\n}\n\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n globalThis.setTimeout(resolve, ms);\n });\n}\n"],"mappings":"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","names":[]} |
+64
-2
@@ -121,2 +121,48 @@ interface MetaTag { | ||
| } | ||
| interface AuthorSocialProfile { | ||
| platform: string; | ||
| url: string; | ||
| } | ||
| interface AuthorCredential { | ||
| name: string; | ||
| issuer?: string; | ||
| dateObtained?: string; | ||
| } | ||
| interface AuthorEducation { | ||
| institution: string; | ||
| degree?: string; | ||
| } | ||
| interface AuthorPublication { | ||
| title: string; | ||
| url?: string; | ||
| } | ||
| interface AuthorInfo { | ||
| name?: string; | ||
| bio?: string; | ||
| url?: string; | ||
| image?: string; | ||
| jobTitle?: string; | ||
| worksFor?: { | ||
| name?: string; | ||
| url?: string; | ||
| logo?: string; | ||
| }; | ||
| knowsAbout?: string[]; | ||
| credentials?: AuthorCredential[]; | ||
| education?: AuthorEducation[]; | ||
| socialProfiles?: AuthorSocialProfile[]; | ||
| yearsOfExperience?: number; | ||
| publications?: AuthorPublication[]; | ||
| awards?: string[]; | ||
| } | ||
| interface EditorialReviewer { | ||
| name: string; | ||
| credentials?: string; | ||
| url?: string; | ||
| } | ||
| interface PreviouslyUsedKeyphrase { | ||
| keyphrase: string; | ||
| postTitle: string; | ||
| postUrl?: string; | ||
| } | ||
| interface ContentAnalysisInput { | ||
@@ -127,4 +173,7 @@ title?: string; | ||
| focusKeyphrase?: string; | ||
| secondaryKeyphrases?: string[]; | ||
| slug?: string; | ||
| locale?: string; | ||
| canonicalUrl?: string; | ||
| siteUrl?: string; | ||
| internalLinks?: string[]; | ||
@@ -136,4 +185,16 @@ externalLinks?: string[]; | ||
| }>; | ||
| author?: AuthorInfo; | ||
| publishDate?: string | Date; | ||
| modifiedDate?: string | Date; | ||
| contentCategory?: string; | ||
| isSponsored?: boolean; | ||
| hasAffiliateLinks?: boolean; | ||
| editorialReviewer?: EditorialReviewer; | ||
| correctionPolicyUrl?: string; | ||
| privacyPolicyUrl?: string; | ||
| previouslyUsedKeyphrases?: PreviouslyUsedKeyphrase[]; | ||
| inboundInternalLinkCount?: number; | ||
| isPillarContent?: boolean; | ||
| } | ||
| type AnalysisStatus = 'good' | 'ok' | 'poor'; | ||
| type AnalysisStatus = 'good' | 'ok' | 'poor' | 'na'; | ||
| interface AnalysisResult { | ||
@@ -167,2 +228,3 @@ id: string; | ||
| transitionWordPercentage: number; | ||
| consecutiveSentenceGroups: number; | ||
| results: AnalysisResult[]; | ||
@@ -580,2 +642,2 @@ recommendations: string[]; | ||
| export { AI_CRAWLERS, type AnalysisResult, type AnalysisStatus, CHAR_PIXEL_WIDTHS, type ContentAnalysisInput, type ContentAnalysisOutput, DEFAULT_CHAR_WIDTH, type HreflangConfig, KEYWORD_DENSITY, type KeyphraseOccurrences, type KeywordDensityResult, type LinkTag, MAX_URL_LENGTH, META_DESCRIPTION_MAX_LENGTH, META_DESCRIPTION_MAX_PIXELS, META_DESCRIPTION_MIN_LENGTH, MIN_WORD_COUNT, type MetaTag, OG_IMAGE, type OpenGraphArticle, type OpenGraphBook, type OpenGraphConfig, type OpenGraphImage, type OpenGraphProfile, type OpenGraphType, type OpenGraphVideo, READABILITY, RECOMMENDED_WORD_COUNT, type ReadabilityInput, type ReadabilityOutput, type RedirectRule, type RedirectStatusCode, type RobotsDirective, SCHEMA_TYPES, type SEOConfig, type SchemaBase, type SchemaGraphConfig, type SchemaType, type SitemapConfig, type SitemapImage, type SitemapNews, type SitemapURL, type SitemapVideo, TITLE_MAX_LENGTH, TITLE_MAX_PIXELS, TITLE_MIN_LENGTH, TWITTER_IMAGE, type TextStatistics, type TitleTemplateVars, type TokenBucket, type TwitterCardConfig, type TwitterCardType, type ValidationResult, type ValidationSeverity, analyzeKeyphraseOccurrences, applyTitleTemplate, buildHreflangTags, buildLinkTags, buildMetaTags, buildOpenGraphTags, buildRobotsContent, buildTwitterTags, calculateKeywordDensity, calculatePixelWidth, consumeToken, countKeywordOccurrences, countSyllables, countTotalSyllables, createTitleTemplate, createTokenBucket, ensureTrailingSlash, extractFirstTagContent, extractSlug, extractTagContents, getParagraphs, getSentences, getTextStatistics, getWaitTime, getWords, isAbsoluteUrl, normalizeUrl, parseRobotsContent, removeTrailingSlash, resolveCanonical, resolveTitle, sleep, stripHtml, stripQueryParams, stripTrackingParams, toSlug, validateMetaDescription, validateTitle }; | ||
| export { AI_CRAWLERS, type AnalysisResult, type AnalysisStatus, type AuthorCredential, type AuthorEducation, type AuthorInfo, type AuthorPublication, type AuthorSocialProfile, CHAR_PIXEL_WIDTHS, type ContentAnalysisInput, type ContentAnalysisOutput, DEFAULT_CHAR_WIDTH, type EditorialReviewer, type HreflangConfig, KEYWORD_DENSITY, type KeyphraseOccurrences, type KeywordDensityResult, type LinkTag, MAX_URL_LENGTH, META_DESCRIPTION_MAX_LENGTH, META_DESCRIPTION_MAX_PIXELS, META_DESCRIPTION_MIN_LENGTH, MIN_WORD_COUNT, type MetaTag, OG_IMAGE, type OpenGraphArticle, type OpenGraphBook, type OpenGraphConfig, type OpenGraphImage, type OpenGraphProfile, type OpenGraphType, type OpenGraphVideo, type PreviouslyUsedKeyphrase, READABILITY, RECOMMENDED_WORD_COUNT, type ReadabilityInput, type ReadabilityOutput, type RedirectRule, type RedirectStatusCode, type RobotsDirective, SCHEMA_TYPES, type SEOConfig, type SchemaBase, type SchemaGraphConfig, type SchemaType, type SitemapConfig, type SitemapImage, type SitemapNews, type SitemapURL, type SitemapVideo, TITLE_MAX_LENGTH, TITLE_MAX_PIXELS, TITLE_MIN_LENGTH, TWITTER_IMAGE, type TextStatistics, type TitleTemplateVars, type TokenBucket, type TwitterCardConfig, type TwitterCardType, type ValidationResult, type ValidationSeverity, analyzeKeyphraseOccurrences, applyTitleTemplate, buildHreflangTags, buildLinkTags, buildMetaTags, buildOpenGraphTags, buildRobotsContent, buildTwitterTags, calculateKeywordDensity, calculatePixelWidth, consumeToken, countKeywordOccurrences, countSyllables, countTotalSyllables, createTitleTemplate, createTokenBucket, ensureTrailingSlash, extractFirstTagContent, extractSlug, extractTagContents, getParagraphs, getSentences, getTextStatistics, getWaitTime, getWords, isAbsoluteUrl, normalizeUrl, parseRobotsContent, removeTrailingSlash, resolveCanonical, resolveTitle, sleep, stripHtml, stripQueryParams, stripTrackingParams, toSlug, validateMetaDescription, validateTitle }; |
+64
-2
@@ -121,2 +121,48 @@ interface MetaTag { | ||
| } | ||
| interface AuthorSocialProfile { | ||
| platform: string; | ||
| url: string; | ||
| } | ||
| interface AuthorCredential { | ||
| name: string; | ||
| issuer?: string; | ||
| dateObtained?: string; | ||
| } | ||
| interface AuthorEducation { | ||
| institution: string; | ||
| degree?: string; | ||
| } | ||
| interface AuthorPublication { | ||
| title: string; | ||
| url?: string; | ||
| } | ||
| interface AuthorInfo { | ||
| name?: string; | ||
| bio?: string; | ||
| url?: string; | ||
| image?: string; | ||
| jobTitle?: string; | ||
| worksFor?: { | ||
| name?: string; | ||
| url?: string; | ||
| logo?: string; | ||
| }; | ||
| knowsAbout?: string[]; | ||
| credentials?: AuthorCredential[]; | ||
| education?: AuthorEducation[]; | ||
| socialProfiles?: AuthorSocialProfile[]; | ||
| yearsOfExperience?: number; | ||
| publications?: AuthorPublication[]; | ||
| awards?: string[]; | ||
| } | ||
| interface EditorialReviewer { | ||
| name: string; | ||
| credentials?: string; | ||
| url?: string; | ||
| } | ||
| interface PreviouslyUsedKeyphrase { | ||
| keyphrase: string; | ||
| postTitle: string; | ||
| postUrl?: string; | ||
| } | ||
| interface ContentAnalysisInput { | ||
@@ -127,4 +173,7 @@ title?: string; | ||
| focusKeyphrase?: string; | ||
| secondaryKeyphrases?: string[]; | ||
| slug?: string; | ||
| locale?: string; | ||
| canonicalUrl?: string; | ||
| siteUrl?: string; | ||
| internalLinks?: string[]; | ||
@@ -136,4 +185,16 @@ externalLinks?: string[]; | ||
| }>; | ||
| author?: AuthorInfo; | ||
| publishDate?: string | Date; | ||
| modifiedDate?: string | Date; | ||
| contentCategory?: string; | ||
| isSponsored?: boolean; | ||
| hasAffiliateLinks?: boolean; | ||
| editorialReviewer?: EditorialReviewer; | ||
| correctionPolicyUrl?: string; | ||
| privacyPolicyUrl?: string; | ||
| previouslyUsedKeyphrases?: PreviouslyUsedKeyphrase[]; | ||
| inboundInternalLinkCount?: number; | ||
| isPillarContent?: boolean; | ||
| } | ||
| type AnalysisStatus = 'good' | 'ok' | 'poor'; | ||
| type AnalysisStatus = 'good' | 'ok' | 'poor' | 'na'; | ||
| interface AnalysisResult { | ||
@@ -167,2 +228,3 @@ id: string; | ||
| transitionWordPercentage: number; | ||
| consecutiveSentenceGroups: number; | ||
| results: AnalysisResult[]; | ||
@@ -580,2 +642,2 @@ recommendations: string[]; | ||
| export { AI_CRAWLERS, type AnalysisResult, type AnalysisStatus, CHAR_PIXEL_WIDTHS, type ContentAnalysisInput, type ContentAnalysisOutput, DEFAULT_CHAR_WIDTH, type HreflangConfig, KEYWORD_DENSITY, type KeyphraseOccurrences, type KeywordDensityResult, type LinkTag, MAX_URL_LENGTH, META_DESCRIPTION_MAX_LENGTH, META_DESCRIPTION_MAX_PIXELS, META_DESCRIPTION_MIN_LENGTH, MIN_WORD_COUNT, type MetaTag, OG_IMAGE, type OpenGraphArticle, type OpenGraphBook, type OpenGraphConfig, type OpenGraphImage, type OpenGraphProfile, type OpenGraphType, type OpenGraphVideo, READABILITY, RECOMMENDED_WORD_COUNT, type ReadabilityInput, type ReadabilityOutput, type RedirectRule, type RedirectStatusCode, type RobotsDirective, SCHEMA_TYPES, type SEOConfig, type SchemaBase, type SchemaGraphConfig, type SchemaType, type SitemapConfig, type SitemapImage, type SitemapNews, type SitemapURL, type SitemapVideo, TITLE_MAX_LENGTH, TITLE_MAX_PIXELS, TITLE_MIN_LENGTH, TWITTER_IMAGE, type TextStatistics, type TitleTemplateVars, type TokenBucket, type TwitterCardConfig, type TwitterCardType, type ValidationResult, type ValidationSeverity, analyzeKeyphraseOccurrences, applyTitleTemplate, buildHreflangTags, buildLinkTags, buildMetaTags, buildOpenGraphTags, buildRobotsContent, buildTwitterTags, calculateKeywordDensity, calculatePixelWidth, consumeToken, countKeywordOccurrences, countSyllables, countTotalSyllables, createTitleTemplate, createTokenBucket, ensureTrailingSlash, extractFirstTagContent, extractSlug, extractTagContents, getParagraphs, getSentences, getTextStatistics, getWaitTime, getWords, isAbsoluteUrl, normalizeUrl, parseRobotsContent, removeTrailingSlash, resolveCanonical, resolveTitle, sleep, stripHtml, stripQueryParams, stripTrackingParams, toSlug, validateMetaDescription, validateTitle }; | ||
| export { AI_CRAWLERS, type AnalysisResult, type AnalysisStatus, type AuthorCredential, type AuthorEducation, type AuthorInfo, type AuthorPublication, type AuthorSocialProfile, CHAR_PIXEL_WIDTHS, type ContentAnalysisInput, type ContentAnalysisOutput, DEFAULT_CHAR_WIDTH, type EditorialReviewer, type HreflangConfig, KEYWORD_DENSITY, type KeyphraseOccurrences, type KeywordDensityResult, type LinkTag, MAX_URL_LENGTH, META_DESCRIPTION_MAX_LENGTH, META_DESCRIPTION_MAX_PIXELS, META_DESCRIPTION_MIN_LENGTH, MIN_WORD_COUNT, type MetaTag, OG_IMAGE, type OpenGraphArticle, type OpenGraphBook, type OpenGraphConfig, type OpenGraphImage, type OpenGraphProfile, type OpenGraphType, type OpenGraphVideo, type PreviouslyUsedKeyphrase, READABILITY, RECOMMENDED_WORD_COUNT, type ReadabilityInput, type ReadabilityOutput, type RedirectRule, type RedirectStatusCode, type RobotsDirective, SCHEMA_TYPES, type SEOConfig, type SchemaBase, type SchemaGraphConfig, type SchemaType, type SitemapConfig, type SitemapImage, type SitemapNews, type SitemapURL, type SitemapVideo, TITLE_MAX_LENGTH, TITLE_MAX_PIXELS, TITLE_MIN_LENGTH, TWITTER_IMAGE, type TextStatistics, type TitleTemplateVars, type TokenBucket, type TwitterCardConfig, type TwitterCardType, type ValidationResult, type ValidationSeverity, analyzeKeyphraseOccurrences, applyTitleTemplate, buildHreflangTags, buildLinkTags, buildMetaTags, buildOpenGraphTags, buildRobotsContent, buildTwitterTags, calculateKeywordDensity, calculatePixelWidth, consumeToken, countKeywordOccurrences, countSyllables, countTotalSyllables, createTitleTemplate, createTokenBucket, ensureTrailingSlash, extractFirstTagContent, extractSlug, extractTagContents, getParagraphs, getSentences, getTextStatistics, getWaitTime, getWords, isAbsoluteUrl, normalizeUrl, parseRobotsContent, removeTrailingSlash, resolveCanonical, resolveTitle, sleep, stripHtml, stripQueryParams, stripTrackingParams, toSlug, validateMetaDescription, validateTitle }; |
+1
-1
| { | ||
| "name": "@power-seo/core", | ||
| "version": "1.0.12", | ||
| "version": "1.0.15", | ||
| "description": "Framework-agnostic SEO analysis engines, types, and utilities", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
257995
1.38%2531
2.55%