@rankcli/agent-runtime 0.0.31 → 0.0.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -3031,20 +3031,6 @@ declare function buildDeterministicFixes(input: DeterministicFixInput): Determin
3031
3031
  */
3032
3032
  declare function findAppRoot(files: string[]): string;
3033
3033
 
3034
- interface ReplacementConcern {
3035
- /** Machine-readable, for logs and the PR body. */
3036
- code: 'shrunk' | 'lost_closing_tag' | 'lost_script' | 'lost_stylesheet' | 'lost_verification' | 'lost_import' | 'invalid_json' | 'invalid_xml' | 'invalid_json_ld' | 'fewer_sitemap_urls' | 'lastmod_moved_backwards';
3037
- detail: string;
3038
- }
3039
- /**
3040
- * Everything wrong with replacing `original` with `replacement` at `path`. Empty means safe
3041
- * to commit. `original` is null for a file being created, where there is nothing to lose and
3042
- * only the content's own validity matters.
3043
- */
3044
- declare function assessReplacement(path: string, original: string | null, replacement: string): ReplacementConcern[];
3045
- /** One line naming why a file was dropped, for a log line or a PR body. */
3046
- declare function describeConcerns(concerns: ReplacementConcern[]): string;
3047
-
3048
3034
  /**
3049
3035
  * Framework Detection
3050
3036
  *
@@ -3056,7 +3042,7 @@ declare function describeConcerns(concerns: ReplacementConcern[]): string;
3056
3042
  *
3057
3043
  * Supports 25+ frameworks across JavaScript, Ruby, Python, PHP, Java, C#, Go, and Elixir.
3058
3044
  */
3059
- type Framework = 'react' | 'nextjs' | 'vue' | 'nuxt' | 'angular' | 'svelte' | 'sveltekit' | 'astro' | 'remix' | 'gatsby' | 'solidjs' | 'qwik' | 'fresh' | 'rails' | 'django' | 'flask' | 'fastapi' | 'laravel' | 'symfony' | 'wordpress' | 'spring' | 'aspnet' | 'go' | 'phoenix' | 'htmx' | 'hotwire' | 'hugo' | 'jekyll' | 'eleventy' | 'pelican' | 'sphinx' | 'unknown';
3045
+ type Framework = 'react' | 'nextjs' | 'vue' | 'nuxt' | 'angular' | 'svelte' | 'sveltekit' | 'astro' | 'remix' | 'gatsby' | 'solidjs' | 'qwik' | 'fresh' | 'rails' | 'django' | 'flask' | 'fastapi' | 'laravel' | 'symfony' | 'wordpress' | 'spring' | 'aspnet' | 'go' | 'phoenix' | 'htmx' | 'hotwire' | 'hugo' | 'jekyll' | 'eleventy' | 'pelican' | 'sphinx' | 'docusaurus' | 'unknown';
3060
3046
  type Language = 'javascript' | 'typescript' | 'ruby' | 'python' | 'php' | 'java' | 'csharp' | 'go' | 'elixir' | 'rust' | 'unknown';
3061
3047
  interface FrameworkInfo {
3062
3048
  framework: Framework;
@@ -3108,6 +3094,30 @@ declare function detectFramework$1(options: {
3108
3094
  */
3109
3095
  declare function getFrameworkDisplayName(framework: Framework): string;
3110
3096
 
3097
+ /**
3098
+ * Where to write a file that must be served at the site root.
3099
+ *
3100
+ * @param framework what detection identified, or 'unknown'
3101
+ * @param files every path in the repository
3102
+ * @param appRoot the app's directory, with a trailing slash, or ''
3103
+ * @returns a path prefix relative to appRoot: 'public/', 'static/', or '' for the app root
3104
+ */
3105
+ declare function servedDir(framework: Framework | undefined, files: string[], appRoot?: string): string;
3106
+
3107
+ interface ReplacementConcern {
3108
+ /** Machine-readable, for logs and the PR body. */
3109
+ code: 'shrunk' | 'lost_closing_tag' | 'lost_script' | 'lost_stylesheet' | 'lost_verification' | 'lost_import' | 'invalid_json' | 'invalid_xml' | 'invalid_json_ld' | 'fewer_sitemap_urls' | 'lastmod_moved_backwards';
3110
+ detail: string;
3111
+ }
3112
+ /**
3113
+ * Everything wrong with replacing `original` with `replacement` at `path`. Empty means safe
3114
+ * to commit. `original` is null for a file being created, where there is nothing to lose and
3115
+ * only the content's own validity matters.
3116
+ */
3117
+ declare function assessReplacement(path: string, original: string | null, replacement: string): ReplacementConcern[];
3118
+ /** One line naming why a file was dropped, for a log line or a PR body. */
3119
+ declare function describeConcerns(concerns: ReplacementConcern[]): string;
3120
+
3111
3121
  /**
3112
3122
  * Framework-Specific SEO Suggestion Engine
3113
3123
  *
@@ -3131,6 +3141,13 @@ interface FrameworkCheck {
3131
3141
  };
3132
3142
  app_router_only?: boolean;
3133
3143
  pages_router_only?: boolean;
3144
+ /**
3145
+ * Only meaningful on a page search engines are allowed to index. A check that
3146
+ * says "crawlers will not see your content" is not a finding on a page that
3147
+ * asks not to be crawled - every SaaS has a noindex /login, and reporting a
3148
+ * critical SEO error on it is noise that drags the score down.
3149
+ */
3150
+ indexable_only?: boolean;
3134
3151
  }
3135
3152
  interface FrameworkRecipe {
3136
3153
  framework: Framework;
@@ -3158,7 +3175,9 @@ declare function loadRecipe(framework: Framework): Promise<FrameworkRecipe | nul
3158
3175
  /**
3159
3176
  * Get framework-specific issues based on HTML analysis
3160
3177
  */
3161
- declare function getFrameworkIssues(frameworkInfo: FrameworkInfo, html: string, url: string): Promise<AuditIssue[]>;
3178
+ declare function getFrameworkIssues(frameworkInfo: FrameworkInfo, html: string, url: string, options?: {
3179
+ indexable?: boolean;
3180
+ }): Promise<AuditIssue[]>;
3162
3181
  /**
3163
3182
  * Get framework-specific suggestions for fixing issues
3164
3183
  */
@@ -5689,4 +5708,4 @@ declare namespace index {
5689
5708
  export { type index_DetectionResult as DetectionResult, type index_Evidence as Evidence, type index_Framework as Framework, type index_FrameworkCheck as FrameworkCheck, type index_FrameworkInfo as FrameworkInfo, type index_FrameworkRecipe as FrameworkRecipe, index_SCORE_FLOOR as SCORE_FLOOR, index_SIGNALS as SIGNALS, index_SUPPORTED_FRAMEWORKS as SUPPORTED_FRAMEWORKS, type index_Signal as Signal, index_WEIGHT as WEIGHT, detectFramework$1 as detectFramework, index_detectFromEvidence as detectFromEvidence, index_detectFromHeaders as detectFromHeaders, index_detectFromHtml as detectFromHtml, index_detectFromPackageJson as detectFromPackageJson, index_embedRecipe as embedRecipe, index_getFrameworkChecks as getFrameworkChecks, index_getFrameworkDisplayName as getFrameworkDisplayName, index_getFrameworkIssues as getFrameworkIssues, index_getFrameworkSuggestions as getFrameworkSuggestions, index_getFrameworkSummary as getFrameworkSummary, index_getMigrationPaths as getMigrationPaths, index_getPerformanceTips as getPerformanceTips, index_getSeoChallenges as getSeoChallenges, index_getTotalFrameworkChecks as getTotalFrameworkChecks, index_loadRecipe as loadRecipe };
5690
5709
  }
5691
5710
 
5692
- export { type AIContentConfig, type AIKeywordResearchOptions, type AgentDefinition, type AgentTool, type AlertChannel, type AlertConfig, type AlertMessage, type AlertResult, type AlertSeverity, type AlertType, type AuditAlertPayload, type AuditIssue, type AuditIssueData, type AuditOptions, type AuditPRResult, type AuditReport, type AuditReportData, type AuditRunnerOptions, type AuditRunnerResult, type AuditSchedule, type BlogPostConfig, type BrandConfig, type CIAction, type CIKeywordOptions, type CIKeywordResult, COMMIT_TYPES, type ChangelogConfig, type ChangelogResult, type CitationCheckOptions, type CommitConfig, type ComparisonConfig, type ComparisonRow, type CompetitiveInsight, type CompetitiveSearchOptions, type CompetitiveSearchResult, type CompetitorComparison, type CompetitorKeywordResult, type CompetitorOverlap, type CompetitorTool, type CompetitorUrl, type ContentElements, type ContentGeneratorOptions, type ContentRecommendation, type ConventionalCommit, type CrawlResult, type CrawlStats, type CrawledPage, type CreateAuditPROptions, type CreatePROptions, type DataForSEOCredentials, type DeterministicFixInput, type DeterministicFixes, type DiscoveredPage, type EnhancedToolIdea, type ExecOptions, type ExecuteOptions, type ExecutionResult, type FAQItem, type FAQSchemaOptions, type FeaturedSnippetAnalysis, type Fix, type FixFile, type FixGeneratorOptions, type FixResult, type FrameworkInfo$1 as FrameworkInfo, type FreeKeywordResult, type FreeToolIdea, type GA4Config, type GEOAlert, type GEOAlertType, type GEOHistory, type GEOHistoryOptions, type GEOQuery, type GEOReport, type GEOResult, type GEOTrend, type GSCConfig, type GSCCredentials, type GSCPerformanceData, type GSCQueryData, type GSCQueryResult, type GeneratedCode, type GeneratedContent, type GeneratedFix, type HeadingStructure, type HeadlineAnalysis, type HealthScore, ISSUE_DEFINITIONS, type ImageInfo$2 as ImageInfo, type InjectionResult, type IntentAnalysis, type IssueCategory, type IssueDefinition, type IssueSeverity, type KeyFact, type KeyFactsOptions, type KeywordAction, type KeywordCluster, type KeywordData, type KeywordDensityAnalysis, type KeywordLLM, type KeywordOpportunity, type KeywordRecommendation, type KeywordResearchOptions, type KeywordResearchResult, type KeywordStats, type KeywordTopic, type KeywordTrend, type LLMCitationResult, type LLMJudgeOptions, type LLMProvider, LOCATION_CODES, type LSIKeyword, type LinkInfo$1 as LinkInfo, type Mention, type MentionType, type MetaData, type MetaFixOptions, type NGram, type NLPAnalysisResult, OG_IMAGE_SPECS, type OptimizeOptions, type PRConfig, type PRDescription, PRIORITY_WEIGHTS, type PRResult, type PageAudit, type PageData, type PageMeta, type PaidKeywordResult, type ParsedResponse, type PlanTier, type ProviderStats, type QuickWin, type RankCheckOptions, type RankCheckResult, type RankHistory, RankTracker, type RankingResult, type RankingTierLimits, type ReadabilityResult, type ReadmeConfig, type ReadmeResult, type ReportBranding, type ReportConfig, type ReportData, type ReportPageData, type RouteInfo, type SEOAnalysisResult, type SEOFixCommit, type SEOFixSummary, type SEOIssue, type SEORecommendation, type SEOScore, SEO_SCOPES, SITE_PROFILE_QUESTIONS, type ScheduledAuditConfig, type ScheduledAuditResult, type SchemaData, Schemas, type SearchIntent, type Sentiment, type SerpApiConfig, SerpClient, type SerpClientConfig, type SerpFeature, type SiteCrawlResult, type SiteProfile, type SiteSummary, type SnippetRecommendation, type SnippetType, type SocialMetaConfig, type SocialMetaFix, type SummarizerOptions, type TFIDFResult, TIER_LIMITS, type ToolFeasibilityScore, type ToolFunction, type ToolResult, type TopicClusterResult, type TopicModel, type TrackedKeyword, type TrackerConfig, type TrackingOptions, type TrendDirection, type UncertaintyAssessment, type WizardQuestion, type WizardResponse, type WizardResult, type WizardSession, type WorkflowConfig, addTrackingResult, analyzeAnchorText, analyzeCanonicalAdvanced, analyzeClientRendering, analyzeContentFreshness, analyzeDOMStructure, analyzeFeaturedSnippetPotential, analyzeHeadings, analyzeHeadline, analyzeHreflang, analyzeImages$1 as analyzeImages, analyzeKeywordDensity, analyzeKeywords, analyzeLinks, analyzeLocalSEO, analyzeMobile, analyzeModernImages, analyzeOnPage, analyzePagination, analyzePerformance, analyzeReadability, analyzeRedirectChain, analyzeRedirects, analyzeResponsiveImages, analyzeSERPPreview, analyzeSecurity, analyzeSecurityHeaders$1 as analyzeSecurityHeaders, analyzeSocialMeta, analyzeStructuredData$1 as analyzeStructuredData, analyzeTrackerBloat, analyzeUrl, analyzeUrlSafety, index$1 as analyzers, applyFixes, assessReplacement, buildDeterministicFixes, buildFrameworkGuidance, buildGSCApiRequest, buildGSCRequest, calculateAIVisibilityScore, calculateBM25, calculateTFIDF$1 as calculateKeywordTFIDF, calculateNextRun, calculateTFIDF, checkAIBotBlocking, checkAMP, checkAdsTxt, checkAppleTouchIcon, checkBalance, checkCertificate, checkDMARC, checkGitHubCLI, checkInternalRedirects, checkJSRenderingRatio, checkLLMCitations, checkLlmsTxt, checkMobileResources, checkPlaintextEmails, checkRedirects, checkRobots, checkRobotsTxt, checkSPF, checkSitemap, classifyIntent, classifyIntents, clusterKeywordsByEmbedding, compareCompetitorVisibility, comparePeriods, completeWizard, crawlSite, crawlUrl, createAuditPR, createFallbackSummary, createGEOHistory, createPullRequest, createSEOCommit, createSEOCommits, describeConcerns, detectDuplicates, detectFramework, detectGitHubPages, detectMentions, detectSoft404, detectTechnologies, detectVisibilityChanges, discoverCompetitorKeywords, discoverPagesFromLinks, discoverRoutesFromRepo, enhanceSummaryWithCompetitors, enhanceToolIdea, enrichKeywordsWithEstimates, ensureGitRepo, evaluateAndEnhanceToolIdeas, evaluateToolFeasibility, executeAgent, extractContentHash, extractEntityPhrases, extractImages, extractKeyPhrases, extractLinks, extractMeta, extractNgrams, extractSchema, extractSeedKeywords, extractTopics, fetchCoreWebVitals, findAlmostPage1Keywords, findAppRoot, findCTROpportunities, findHtmlEntry, findLSIKeywords, findPageFiles, findTopPerformers, formatAlertMessage, formatCIResult, formatCompetitorReport, formatConventionalCommit, formatFeaturedSnippetReport, formatHeadlineReport, formatIntentReport, formatKeywordDensityReport, formatKeywordReport, formatPRBody, formatPRTitle, formatReadabilityReport, formatReport, formatSEOCommitMessage, formatTopicReport, formatWizardProgress, index as frameworks, generateAICitableContent, generateAllFixes, generateAngularSEOService, generateAspNetCoreSEO, generateAstroBaseHead, generateAstroMeta, generateBlogPost, generateBranchName, generateChangelog, generateCommitSummary, generateComparisonTable, generateCompleteSocialMetaSetup, generateDjangoSEOHelper, generateDuplicateIssues, generateEleventySEO, generateFAQSchema, generateFixes, generateGA4EnvTemplate, generateGA4ReactComponent, generateGA4Script, generateGA4ViteScript, generateGEOReport, generateGSCVerificationTag, generateGitHubActionSetup, generateGoSEO, generateHTMLReport, generateHTMLSocialMeta, generateHTMXSEO, generateHeadlineVariations, generateHugoSEO, generateJekyllSEO, generateJsonReport, generateKeyFacts, generateLaravelSEOHelper, generateMarkdownReport, generateNextAppMetadata, generateNextJsAppRouterMetadata, generateNextJsPagesRouterHead, generateNextPagesHead, generateNuxtSEOHead, generatePDFReport, generatePRDescription, generatePhoenixSEO, generateRailsSEOHelper, generateReactHelmetSocialMeta, generateReactSEOHead, generateRecommendationQueries, generateRemixMeta, generateRemixSEO, generateSecretsDoc, generateSocialMetaFix, generateSpringBootSEO, generateSvelteKitMeta, generateSvelteKitSEOHead, generateUncertaintyQuestions, generateVueSEOHead, generateWizardQuestions, generateWorkflow, getAIVisibilitySummary, getAutocompleteSuggestions, getDateRange, getExpandedSuggestions, getFrameworkSpecificFix, getFrameworkSpecificFixExtended, getGSCSetupInstructions, getGitUser, getKeywordData, getKeywordSuggestions, getMaxKdThreshold, getNextQuestion, getRelatedKeywords, getVisibilityTrend, groupKeywordsByTopic, identifyQuickWins, injectGA4, injectGSCVerification, interpolatePrompt, isGitRepo, keywordLLM, listFiles, loadAgent, loadAgentByName, mergePages, openAIKeywordLLM, optimizeForAI, optimizeReadme, parseGEOResponse, parseGSCResponse, parseJsonReply, parseSitemap, prioritizeKeywords, processWizardResponse, readFile, routesToUrls, runAIKeywordResearch, runAIReadinessChecks, runAdditionalChecks, runAuditWithFixes, runCIKeywordResearch, runCrawlabilityChecks, runDirectAnalysis, runFullAudit, runKeywordResearch, runNLPAnalysis, runScheduledAudit, scoreContentSEO, scoreMention, searchCompetitors, searchFormatConverters, searchHackerNews, sendAlert, sendAlerts, sendDiscordAlert, sendSlackAlert, shouldRunAudit, shouldSendAlert, startWizardSession, suggestSchemaTypes, summarizeSite, tokenize, tools, trackLLMVisibility, transformGSCData, urlSafetyDatabase, wizardResponsesToContext, writeFile, writeGitHubActionFiles };
5711
+ export { type AIContentConfig, type AIKeywordResearchOptions, type AgentDefinition, type AgentTool, type AlertChannel, type AlertConfig, type AlertMessage, type AlertResult, type AlertSeverity, type AlertType, type AuditAlertPayload, type AuditIssue, type AuditIssueData, type AuditOptions, type AuditPRResult, type AuditReport, type AuditReportData, type AuditRunnerOptions, type AuditRunnerResult, type AuditSchedule, type BlogPostConfig, type BrandConfig, type CIAction, type CIKeywordOptions, type CIKeywordResult, COMMIT_TYPES, type ChangelogConfig, type ChangelogResult, type CitationCheckOptions, type CommitConfig, type ComparisonConfig, type ComparisonRow, type CompetitiveInsight, type CompetitiveSearchOptions, type CompetitiveSearchResult, type CompetitorComparison, type CompetitorKeywordResult, type CompetitorOverlap, type CompetitorTool, type CompetitorUrl, type ContentElements, type ContentGeneratorOptions, type ContentRecommendation, type ConventionalCommit, type CrawlResult, type CrawlStats, type CrawledPage, type CreateAuditPROptions, type CreatePROptions, type DataForSEOCredentials, type DeterministicFixInput, type DeterministicFixes, type DiscoveredPage, type EnhancedToolIdea, type ExecOptions, type ExecuteOptions, type ExecutionResult, type FAQItem, type FAQSchemaOptions, type FeaturedSnippetAnalysis, type Fix, type FixFile, type FixGeneratorOptions, type FixResult, type FrameworkInfo$1 as FrameworkInfo, type FreeKeywordResult, type FreeToolIdea, type GA4Config, type GEOAlert, type GEOAlertType, type GEOHistory, type GEOHistoryOptions, type GEOQuery, type GEOReport, type GEOResult, type GEOTrend, type GSCConfig, type GSCCredentials, type GSCPerformanceData, type GSCQueryData, type GSCQueryResult, type GeneratedCode, type GeneratedContent, type GeneratedFix, type HeadingStructure, type HeadlineAnalysis, type HealthScore, ISSUE_DEFINITIONS, type ImageInfo$2 as ImageInfo, type InjectionResult, type IntentAnalysis, type IssueCategory, type IssueDefinition, type IssueSeverity, type KeyFact, type KeyFactsOptions, type KeywordAction, type KeywordCluster, type KeywordData, type KeywordDensityAnalysis, type KeywordLLM, type KeywordOpportunity, type KeywordRecommendation, type KeywordResearchOptions, type KeywordResearchResult, type KeywordStats, type KeywordTopic, type KeywordTrend, type LLMCitationResult, type LLMJudgeOptions, type LLMProvider, LOCATION_CODES, type LSIKeyword, type LinkInfo$1 as LinkInfo, type Mention, type MentionType, type MetaData, type MetaFixOptions, type NGram, type NLPAnalysisResult, OG_IMAGE_SPECS, type OptimizeOptions, type PRConfig, type PRDescription, PRIORITY_WEIGHTS, type PRResult, type PageAudit, type PageData, type PageMeta, type PaidKeywordResult, type ParsedResponse, type PlanTier, type ProviderStats, type QuickWin, type RankCheckOptions, type RankCheckResult, type RankHistory, RankTracker, type RankingResult, type RankingTierLimits, type ReadabilityResult, type ReadmeConfig, type ReadmeResult, type ReportBranding, type ReportConfig, type ReportData, type ReportPageData, type RouteInfo, type SEOAnalysisResult, type SEOFixCommit, type SEOFixSummary, type SEOIssue, type SEORecommendation, type SEOScore, SEO_SCOPES, SITE_PROFILE_QUESTIONS, type ScheduledAuditConfig, type ScheduledAuditResult, type SchemaData, Schemas, type SearchIntent, type Sentiment, type SerpApiConfig, SerpClient, type SerpClientConfig, type SerpFeature, type SiteCrawlResult, type SiteProfile, type SiteSummary, type SnippetRecommendation, type SnippetType, type SocialMetaConfig, type SocialMetaFix, type SummarizerOptions, type TFIDFResult, TIER_LIMITS, type ToolFeasibilityScore, type ToolFunction, type ToolResult, type TopicClusterResult, type TopicModel, type TrackedKeyword, type TrackerConfig, type TrackingOptions, type TrendDirection, type UncertaintyAssessment, type WizardQuestion, type WizardResponse, type WizardResult, type WizardSession, type WorkflowConfig, addTrackingResult, analyzeAnchorText, analyzeCanonicalAdvanced, analyzeClientRendering, analyzeContentFreshness, analyzeDOMStructure, analyzeFeaturedSnippetPotential, analyzeHeadings, analyzeHeadline, analyzeHreflang, analyzeImages$1 as analyzeImages, analyzeKeywordDensity, analyzeKeywords, analyzeLinks, analyzeLocalSEO, analyzeMobile, analyzeModernImages, analyzeOnPage, analyzePagination, analyzePerformance, analyzeReadability, analyzeRedirectChain, analyzeRedirects, analyzeResponsiveImages, analyzeSERPPreview, analyzeSecurity, analyzeSecurityHeaders$1 as analyzeSecurityHeaders, analyzeSocialMeta, analyzeStructuredData$1 as analyzeStructuredData, analyzeTrackerBloat, analyzeUrl, analyzeUrlSafety, index$1 as analyzers, applyFixes, assessReplacement, buildDeterministicFixes, buildFrameworkGuidance, buildGSCApiRequest, buildGSCRequest, calculateAIVisibilityScore, calculateBM25, calculateTFIDF$1 as calculateKeywordTFIDF, calculateNextRun, calculateTFIDF, checkAIBotBlocking, checkAMP, checkAdsTxt, checkAppleTouchIcon, checkBalance, checkCertificate, checkDMARC, checkGitHubCLI, checkInternalRedirects, checkJSRenderingRatio, checkLLMCitations, checkLlmsTxt, checkMobileResources, checkPlaintextEmails, checkRedirects, checkRobots, checkRobotsTxt, checkSPF, checkSitemap, classifyIntent, classifyIntents, clusterKeywordsByEmbedding, compareCompetitorVisibility, comparePeriods, completeWizard, crawlSite, crawlUrl, createAuditPR, createFallbackSummary, createGEOHistory, createPullRequest, createSEOCommit, createSEOCommits, describeConcerns, detectDuplicates, detectFramework, detectGitHubPages, detectMentions, detectSoft404, detectTechnologies, detectVisibilityChanges, discoverCompetitorKeywords, discoverPagesFromLinks, discoverRoutesFromRepo, enhanceSummaryWithCompetitors, enhanceToolIdea, enrichKeywordsWithEstimates, ensureGitRepo, evaluateAndEnhanceToolIdeas, evaluateToolFeasibility, executeAgent, extractContentHash, extractEntityPhrases, extractImages, extractKeyPhrases, extractLinks, extractMeta, extractNgrams, extractSchema, extractSeedKeywords, extractTopics, fetchCoreWebVitals, findAlmostPage1Keywords, findAppRoot, findCTROpportunities, findHtmlEntry, findLSIKeywords, findPageFiles, findTopPerformers, formatAlertMessage, formatCIResult, formatCompetitorReport, formatConventionalCommit, formatFeaturedSnippetReport, formatHeadlineReport, formatIntentReport, formatKeywordDensityReport, formatKeywordReport, formatPRBody, formatPRTitle, formatReadabilityReport, formatReport, formatSEOCommitMessage, formatTopicReport, formatWizardProgress, index as frameworks, generateAICitableContent, generateAllFixes, generateAngularSEOService, generateAspNetCoreSEO, generateAstroBaseHead, generateAstroMeta, generateBlogPost, generateBranchName, generateChangelog, generateCommitSummary, generateComparisonTable, generateCompleteSocialMetaSetup, generateDjangoSEOHelper, generateDuplicateIssues, generateEleventySEO, generateFAQSchema, generateFixes, generateGA4EnvTemplate, generateGA4ReactComponent, generateGA4Script, generateGA4ViteScript, generateGEOReport, generateGSCVerificationTag, generateGitHubActionSetup, generateGoSEO, generateHTMLReport, generateHTMLSocialMeta, generateHTMXSEO, generateHeadlineVariations, generateHugoSEO, generateJekyllSEO, generateJsonReport, generateKeyFacts, generateLaravelSEOHelper, generateMarkdownReport, generateNextAppMetadata, generateNextJsAppRouterMetadata, generateNextJsPagesRouterHead, generateNextPagesHead, generateNuxtSEOHead, generatePDFReport, generatePRDescription, generatePhoenixSEO, generateRailsSEOHelper, generateReactHelmetSocialMeta, generateReactSEOHead, generateRecommendationQueries, generateRemixMeta, generateRemixSEO, generateSecretsDoc, generateSocialMetaFix, generateSpringBootSEO, generateSvelteKitMeta, generateSvelteKitSEOHead, generateUncertaintyQuestions, generateVueSEOHead, generateWizardQuestions, generateWorkflow, getAIVisibilitySummary, getAutocompleteSuggestions, getDateRange, getExpandedSuggestions, getFrameworkSpecificFix, getFrameworkSpecificFixExtended, getGSCSetupInstructions, getGitUser, getKeywordData, getKeywordSuggestions, getMaxKdThreshold, getNextQuestion, getRelatedKeywords, getVisibilityTrend, groupKeywordsByTopic, identifyQuickWins, injectGA4, injectGSCVerification, interpolatePrompt, isGitRepo, keywordLLM, listFiles, loadAgent, loadAgentByName, mergePages, openAIKeywordLLM, optimizeForAI, optimizeReadme, parseGEOResponse, parseGSCResponse, parseJsonReply, parseSitemap, prioritizeKeywords, processWizardResponse, readFile, routesToUrls, runAIKeywordResearch, runAIReadinessChecks, runAdditionalChecks, runAuditWithFixes, runCIKeywordResearch, runCrawlabilityChecks, runDirectAnalysis, runFullAudit, runKeywordResearch, runNLPAnalysis, runScheduledAudit, scoreContentSEO, scoreMention, searchCompetitors, searchFormatConverters, searchHackerNews, sendAlert, sendAlerts, sendDiscordAlert, sendSlackAlert, servedDir, shouldRunAudit, shouldSendAlert, startWizardSession, suggestSchemaTypes, summarizeSite, tokenize, tools, trackLLMVisibility, transformGSCData, urlSafetyDatabase, wizardResponsesToContext, writeFile, writeGitHubActionFiles };
package/dist/index.d.ts CHANGED
@@ -3031,20 +3031,6 @@ declare function buildDeterministicFixes(input: DeterministicFixInput): Determin
3031
3031
  */
3032
3032
  declare function findAppRoot(files: string[]): string;
3033
3033
 
3034
- interface ReplacementConcern {
3035
- /** Machine-readable, for logs and the PR body. */
3036
- code: 'shrunk' | 'lost_closing_tag' | 'lost_script' | 'lost_stylesheet' | 'lost_verification' | 'lost_import' | 'invalid_json' | 'invalid_xml' | 'invalid_json_ld' | 'fewer_sitemap_urls' | 'lastmod_moved_backwards';
3037
- detail: string;
3038
- }
3039
- /**
3040
- * Everything wrong with replacing `original` with `replacement` at `path`. Empty means safe
3041
- * to commit. `original` is null for a file being created, where there is nothing to lose and
3042
- * only the content's own validity matters.
3043
- */
3044
- declare function assessReplacement(path: string, original: string | null, replacement: string): ReplacementConcern[];
3045
- /** One line naming why a file was dropped, for a log line or a PR body. */
3046
- declare function describeConcerns(concerns: ReplacementConcern[]): string;
3047
-
3048
3034
  /**
3049
3035
  * Framework Detection
3050
3036
  *
@@ -3056,7 +3042,7 @@ declare function describeConcerns(concerns: ReplacementConcern[]): string;
3056
3042
  *
3057
3043
  * Supports 25+ frameworks across JavaScript, Ruby, Python, PHP, Java, C#, Go, and Elixir.
3058
3044
  */
3059
- type Framework = 'react' | 'nextjs' | 'vue' | 'nuxt' | 'angular' | 'svelte' | 'sveltekit' | 'astro' | 'remix' | 'gatsby' | 'solidjs' | 'qwik' | 'fresh' | 'rails' | 'django' | 'flask' | 'fastapi' | 'laravel' | 'symfony' | 'wordpress' | 'spring' | 'aspnet' | 'go' | 'phoenix' | 'htmx' | 'hotwire' | 'hugo' | 'jekyll' | 'eleventy' | 'pelican' | 'sphinx' | 'unknown';
3045
+ type Framework = 'react' | 'nextjs' | 'vue' | 'nuxt' | 'angular' | 'svelte' | 'sveltekit' | 'astro' | 'remix' | 'gatsby' | 'solidjs' | 'qwik' | 'fresh' | 'rails' | 'django' | 'flask' | 'fastapi' | 'laravel' | 'symfony' | 'wordpress' | 'spring' | 'aspnet' | 'go' | 'phoenix' | 'htmx' | 'hotwire' | 'hugo' | 'jekyll' | 'eleventy' | 'pelican' | 'sphinx' | 'docusaurus' | 'unknown';
3060
3046
  type Language = 'javascript' | 'typescript' | 'ruby' | 'python' | 'php' | 'java' | 'csharp' | 'go' | 'elixir' | 'rust' | 'unknown';
3061
3047
  interface FrameworkInfo {
3062
3048
  framework: Framework;
@@ -3108,6 +3094,30 @@ declare function detectFramework$1(options: {
3108
3094
  */
3109
3095
  declare function getFrameworkDisplayName(framework: Framework): string;
3110
3096
 
3097
+ /**
3098
+ * Where to write a file that must be served at the site root.
3099
+ *
3100
+ * @param framework what detection identified, or 'unknown'
3101
+ * @param files every path in the repository
3102
+ * @param appRoot the app's directory, with a trailing slash, or ''
3103
+ * @returns a path prefix relative to appRoot: 'public/', 'static/', or '' for the app root
3104
+ */
3105
+ declare function servedDir(framework: Framework | undefined, files: string[], appRoot?: string): string;
3106
+
3107
+ interface ReplacementConcern {
3108
+ /** Machine-readable, for logs and the PR body. */
3109
+ code: 'shrunk' | 'lost_closing_tag' | 'lost_script' | 'lost_stylesheet' | 'lost_verification' | 'lost_import' | 'invalid_json' | 'invalid_xml' | 'invalid_json_ld' | 'fewer_sitemap_urls' | 'lastmod_moved_backwards';
3110
+ detail: string;
3111
+ }
3112
+ /**
3113
+ * Everything wrong with replacing `original` with `replacement` at `path`. Empty means safe
3114
+ * to commit. `original` is null for a file being created, where there is nothing to lose and
3115
+ * only the content's own validity matters.
3116
+ */
3117
+ declare function assessReplacement(path: string, original: string | null, replacement: string): ReplacementConcern[];
3118
+ /** One line naming why a file was dropped, for a log line or a PR body. */
3119
+ declare function describeConcerns(concerns: ReplacementConcern[]): string;
3120
+
3111
3121
  /**
3112
3122
  * Framework-Specific SEO Suggestion Engine
3113
3123
  *
@@ -3131,6 +3141,13 @@ interface FrameworkCheck {
3131
3141
  };
3132
3142
  app_router_only?: boolean;
3133
3143
  pages_router_only?: boolean;
3144
+ /**
3145
+ * Only meaningful on a page search engines are allowed to index. A check that
3146
+ * says "crawlers will not see your content" is not a finding on a page that
3147
+ * asks not to be crawled - every SaaS has a noindex /login, and reporting a
3148
+ * critical SEO error on it is noise that drags the score down.
3149
+ */
3150
+ indexable_only?: boolean;
3134
3151
  }
3135
3152
  interface FrameworkRecipe {
3136
3153
  framework: Framework;
@@ -3158,7 +3175,9 @@ declare function loadRecipe(framework: Framework): Promise<FrameworkRecipe | nul
3158
3175
  /**
3159
3176
  * Get framework-specific issues based on HTML analysis
3160
3177
  */
3161
- declare function getFrameworkIssues(frameworkInfo: FrameworkInfo, html: string, url: string): Promise<AuditIssue[]>;
3178
+ declare function getFrameworkIssues(frameworkInfo: FrameworkInfo, html: string, url: string, options?: {
3179
+ indexable?: boolean;
3180
+ }): Promise<AuditIssue[]>;
3162
3181
  /**
3163
3182
  * Get framework-specific suggestions for fixing issues
3164
3183
  */
@@ -5689,4 +5708,4 @@ declare namespace index {
5689
5708
  export { type index_DetectionResult as DetectionResult, type index_Evidence as Evidence, type index_Framework as Framework, type index_FrameworkCheck as FrameworkCheck, type index_FrameworkInfo as FrameworkInfo, type index_FrameworkRecipe as FrameworkRecipe, index_SCORE_FLOOR as SCORE_FLOOR, index_SIGNALS as SIGNALS, index_SUPPORTED_FRAMEWORKS as SUPPORTED_FRAMEWORKS, type index_Signal as Signal, index_WEIGHT as WEIGHT, detectFramework$1 as detectFramework, index_detectFromEvidence as detectFromEvidence, index_detectFromHeaders as detectFromHeaders, index_detectFromHtml as detectFromHtml, index_detectFromPackageJson as detectFromPackageJson, index_embedRecipe as embedRecipe, index_getFrameworkChecks as getFrameworkChecks, index_getFrameworkDisplayName as getFrameworkDisplayName, index_getFrameworkIssues as getFrameworkIssues, index_getFrameworkSuggestions as getFrameworkSuggestions, index_getFrameworkSummary as getFrameworkSummary, index_getMigrationPaths as getMigrationPaths, index_getPerformanceTips as getPerformanceTips, index_getSeoChallenges as getSeoChallenges, index_getTotalFrameworkChecks as getTotalFrameworkChecks, index_loadRecipe as loadRecipe };
5690
5709
  }
5691
5710
 
5692
- export { type AIContentConfig, type AIKeywordResearchOptions, type AgentDefinition, type AgentTool, type AlertChannel, type AlertConfig, type AlertMessage, type AlertResult, type AlertSeverity, type AlertType, type AuditAlertPayload, type AuditIssue, type AuditIssueData, type AuditOptions, type AuditPRResult, type AuditReport, type AuditReportData, type AuditRunnerOptions, type AuditRunnerResult, type AuditSchedule, type BlogPostConfig, type BrandConfig, type CIAction, type CIKeywordOptions, type CIKeywordResult, COMMIT_TYPES, type ChangelogConfig, type ChangelogResult, type CitationCheckOptions, type CommitConfig, type ComparisonConfig, type ComparisonRow, type CompetitiveInsight, type CompetitiveSearchOptions, type CompetitiveSearchResult, type CompetitorComparison, type CompetitorKeywordResult, type CompetitorOverlap, type CompetitorTool, type CompetitorUrl, type ContentElements, type ContentGeneratorOptions, type ContentRecommendation, type ConventionalCommit, type CrawlResult, type CrawlStats, type CrawledPage, type CreateAuditPROptions, type CreatePROptions, type DataForSEOCredentials, type DeterministicFixInput, type DeterministicFixes, type DiscoveredPage, type EnhancedToolIdea, type ExecOptions, type ExecuteOptions, type ExecutionResult, type FAQItem, type FAQSchemaOptions, type FeaturedSnippetAnalysis, type Fix, type FixFile, type FixGeneratorOptions, type FixResult, type FrameworkInfo$1 as FrameworkInfo, type FreeKeywordResult, type FreeToolIdea, type GA4Config, type GEOAlert, type GEOAlertType, type GEOHistory, type GEOHistoryOptions, type GEOQuery, type GEOReport, type GEOResult, type GEOTrend, type GSCConfig, type GSCCredentials, type GSCPerformanceData, type GSCQueryData, type GSCQueryResult, type GeneratedCode, type GeneratedContent, type GeneratedFix, type HeadingStructure, type HeadlineAnalysis, type HealthScore, ISSUE_DEFINITIONS, type ImageInfo$2 as ImageInfo, type InjectionResult, type IntentAnalysis, type IssueCategory, type IssueDefinition, type IssueSeverity, type KeyFact, type KeyFactsOptions, type KeywordAction, type KeywordCluster, type KeywordData, type KeywordDensityAnalysis, type KeywordLLM, type KeywordOpportunity, type KeywordRecommendation, type KeywordResearchOptions, type KeywordResearchResult, type KeywordStats, type KeywordTopic, type KeywordTrend, type LLMCitationResult, type LLMJudgeOptions, type LLMProvider, LOCATION_CODES, type LSIKeyword, type LinkInfo$1 as LinkInfo, type Mention, type MentionType, type MetaData, type MetaFixOptions, type NGram, type NLPAnalysisResult, OG_IMAGE_SPECS, type OptimizeOptions, type PRConfig, type PRDescription, PRIORITY_WEIGHTS, type PRResult, type PageAudit, type PageData, type PageMeta, type PaidKeywordResult, type ParsedResponse, type PlanTier, type ProviderStats, type QuickWin, type RankCheckOptions, type RankCheckResult, type RankHistory, RankTracker, type RankingResult, type RankingTierLimits, type ReadabilityResult, type ReadmeConfig, type ReadmeResult, type ReportBranding, type ReportConfig, type ReportData, type ReportPageData, type RouteInfo, type SEOAnalysisResult, type SEOFixCommit, type SEOFixSummary, type SEOIssue, type SEORecommendation, type SEOScore, SEO_SCOPES, SITE_PROFILE_QUESTIONS, type ScheduledAuditConfig, type ScheduledAuditResult, type SchemaData, Schemas, type SearchIntent, type Sentiment, type SerpApiConfig, SerpClient, type SerpClientConfig, type SerpFeature, type SiteCrawlResult, type SiteProfile, type SiteSummary, type SnippetRecommendation, type SnippetType, type SocialMetaConfig, type SocialMetaFix, type SummarizerOptions, type TFIDFResult, TIER_LIMITS, type ToolFeasibilityScore, type ToolFunction, type ToolResult, type TopicClusterResult, type TopicModel, type TrackedKeyword, type TrackerConfig, type TrackingOptions, type TrendDirection, type UncertaintyAssessment, type WizardQuestion, type WizardResponse, type WizardResult, type WizardSession, type WorkflowConfig, addTrackingResult, analyzeAnchorText, analyzeCanonicalAdvanced, analyzeClientRendering, analyzeContentFreshness, analyzeDOMStructure, analyzeFeaturedSnippetPotential, analyzeHeadings, analyzeHeadline, analyzeHreflang, analyzeImages$1 as analyzeImages, analyzeKeywordDensity, analyzeKeywords, analyzeLinks, analyzeLocalSEO, analyzeMobile, analyzeModernImages, analyzeOnPage, analyzePagination, analyzePerformance, analyzeReadability, analyzeRedirectChain, analyzeRedirects, analyzeResponsiveImages, analyzeSERPPreview, analyzeSecurity, analyzeSecurityHeaders$1 as analyzeSecurityHeaders, analyzeSocialMeta, analyzeStructuredData$1 as analyzeStructuredData, analyzeTrackerBloat, analyzeUrl, analyzeUrlSafety, index$1 as analyzers, applyFixes, assessReplacement, buildDeterministicFixes, buildFrameworkGuidance, buildGSCApiRequest, buildGSCRequest, calculateAIVisibilityScore, calculateBM25, calculateTFIDF$1 as calculateKeywordTFIDF, calculateNextRun, calculateTFIDF, checkAIBotBlocking, checkAMP, checkAdsTxt, checkAppleTouchIcon, checkBalance, checkCertificate, checkDMARC, checkGitHubCLI, checkInternalRedirects, checkJSRenderingRatio, checkLLMCitations, checkLlmsTxt, checkMobileResources, checkPlaintextEmails, checkRedirects, checkRobots, checkRobotsTxt, checkSPF, checkSitemap, classifyIntent, classifyIntents, clusterKeywordsByEmbedding, compareCompetitorVisibility, comparePeriods, completeWizard, crawlSite, crawlUrl, createAuditPR, createFallbackSummary, createGEOHistory, createPullRequest, createSEOCommit, createSEOCommits, describeConcerns, detectDuplicates, detectFramework, detectGitHubPages, detectMentions, detectSoft404, detectTechnologies, detectVisibilityChanges, discoverCompetitorKeywords, discoverPagesFromLinks, discoverRoutesFromRepo, enhanceSummaryWithCompetitors, enhanceToolIdea, enrichKeywordsWithEstimates, ensureGitRepo, evaluateAndEnhanceToolIdeas, evaluateToolFeasibility, executeAgent, extractContentHash, extractEntityPhrases, extractImages, extractKeyPhrases, extractLinks, extractMeta, extractNgrams, extractSchema, extractSeedKeywords, extractTopics, fetchCoreWebVitals, findAlmostPage1Keywords, findAppRoot, findCTROpportunities, findHtmlEntry, findLSIKeywords, findPageFiles, findTopPerformers, formatAlertMessage, formatCIResult, formatCompetitorReport, formatConventionalCommit, formatFeaturedSnippetReport, formatHeadlineReport, formatIntentReport, formatKeywordDensityReport, formatKeywordReport, formatPRBody, formatPRTitle, formatReadabilityReport, formatReport, formatSEOCommitMessage, formatTopicReport, formatWizardProgress, index as frameworks, generateAICitableContent, generateAllFixes, generateAngularSEOService, generateAspNetCoreSEO, generateAstroBaseHead, generateAstroMeta, generateBlogPost, generateBranchName, generateChangelog, generateCommitSummary, generateComparisonTable, generateCompleteSocialMetaSetup, generateDjangoSEOHelper, generateDuplicateIssues, generateEleventySEO, generateFAQSchema, generateFixes, generateGA4EnvTemplate, generateGA4ReactComponent, generateGA4Script, generateGA4ViteScript, generateGEOReport, generateGSCVerificationTag, generateGitHubActionSetup, generateGoSEO, generateHTMLReport, generateHTMLSocialMeta, generateHTMXSEO, generateHeadlineVariations, generateHugoSEO, generateJekyllSEO, generateJsonReport, generateKeyFacts, generateLaravelSEOHelper, generateMarkdownReport, generateNextAppMetadata, generateNextJsAppRouterMetadata, generateNextJsPagesRouterHead, generateNextPagesHead, generateNuxtSEOHead, generatePDFReport, generatePRDescription, generatePhoenixSEO, generateRailsSEOHelper, generateReactHelmetSocialMeta, generateReactSEOHead, generateRecommendationQueries, generateRemixMeta, generateRemixSEO, generateSecretsDoc, generateSocialMetaFix, generateSpringBootSEO, generateSvelteKitMeta, generateSvelteKitSEOHead, generateUncertaintyQuestions, generateVueSEOHead, generateWizardQuestions, generateWorkflow, getAIVisibilitySummary, getAutocompleteSuggestions, getDateRange, getExpandedSuggestions, getFrameworkSpecificFix, getFrameworkSpecificFixExtended, getGSCSetupInstructions, getGitUser, getKeywordData, getKeywordSuggestions, getMaxKdThreshold, getNextQuestion, getRelatedKeywords, getVisibilityTrend, groupKeywordsByTopic, identifyQuickWins, injectGA4, injectGSCVerification, interpolatePrompt, isGitRepo, keywordLLM, listFiles, loadAgent, loadAgentByName, mergePages, openAIKeywordLLM, optimizeForAI, optimizeReadme, parseGEOResponse, parseGSCResponse, parseJsonReply, parseSitemap, prioritizeKeywords, processWizardResponse, readFile, routesToUrls, runAIKeywordResearch, runAIReadinessChecks, runAdditionalChecks, runAuditWithFixes, runCIKeywordResearch, runCrawlabilityChecks, runDirectAnalysis, runFullAudit, runKeywordResearch, runNLPAnalysis, runScheduledAudit, scoreContentSEO, scoreMention, searchCompetitors, searchFormatConverters, searchHackerNews, sendAlert, sendAlerts, sendDiscordAlert, sendSlackAlert, shouldRunAudit, shouldSendAlert, startWizardSession, suggestSchemaTypes, summarizeSite, tokenize, tools, trackLLMVisibility, transformGSCData, urlSafetyDatabase, wizardResponsesToContext, writeFile, writeGitHubActionFiles };
5711
+ export { type AIContentConfig, type AIKeywordResearchOptions, type AgentDefinition, type AgentTool, type AlertChannel, type AlertConfig, type AlertMessage, type AlertResult, type AlertSeverity, type AlertType, type AuditAlertPayload, type AuditIssue, type AuditIssueData, type AuditOptions, type AuditPRResult, type AuditReport, type AuditReportData, type AuditRunnerOptions, type AuditRunnerResult, type AuditSchedule, type BlogPostConfig, type BrandConfig, type CIAction, type CIKeywordOptions, type CIKeywordResult, COMMIT_TYPES, type ChangelogConfig, type ChangelogResult, type CitationCheckOptions, type CommitConfig, type ComparisonConfig, type ComparisonRow, type CompetitiveInsight, type CompetitiveSearchOptions, type CompetitiveSearchResult, type CompetitorComparison, type CompetitorKeywordResult, type CompetitorOverlap, type CompetitorTool, type CompetitorUrl, type ContentElements, type ContentGeneratorOptions, type ContentRecommendation, type ConventionalCommit, type CrawlResult, type CrawlStats, type CrawledPage, type CreateAuditPROptions, type CreatePROptions, type DataForSEOCredentials, type DeterministicFixInput, type DeterministicFixes, type DiscoveredPage, type EnhancedToolIdea, type ExecOptions, type ExecuteOptions, type ExecutionResult, type FAQItem, type FAQSchemaOptions, type FeaturedSnippetAnalysis, type Fix, type FixFile, type FixGeneratorOptions, type FixResult, type FrameworkInfo$1 as FrameworkInfo, type FreeKeywordResult, type FreeToolIdea, type GA4Config, type GEOAlert, type GEOAlertType, type GEOHistory, type GEOHistoryOptions, type GEOQuery, type GEOReport, type GEOResult, type GEOTrend, type GSCConfig, type GSCCredentials, type GSCPerformanceData, type GSCQueryData, type GSCQueryResult, type GeneratedCode, type GeneratedContent, type GeneratedFix, type HeadingStructure, type HeadlineAnalysis, type HealthScore, ISSUE_DEFINITIONS, type ImageInfo$2 as ImageInfo, type InjectionResult, type IntentAnalysis, type IssueCategory, type IssueDefinition, type IssueSeverity, type KeyFact, type KeyFactsOptions, type KeywordAction, type KeywordCluster, type KeywordData, type KeywordDensityAnalysis, type KeywordLLM, type KeywordOpportunity, type KeywordRecommendation, type KeywordResearchOptions, type KeywordResearchResult, type KeywordStats, type KeywordTopic, type KeywordTrend, type LLMCitationResult, type LLMJudgeOptions, type LLMProvider, LOCATION_CODES, type LSIKeyword, type LinkInfo$1 as LinkInfo, type Mention, type MentionType, type MetaData, type MetaFixOptions, type NGram, type NLPAnalysisResult, OG_IMAGE_SPECS, type OptimizeOptions, type PRConfig, type PRDescription, PRIORITY_WEIGHTS, type PRResult, type PageAudit, type PageData, type PageMeta, type PaidKeywordResult, type ParsedResponse, type PlanTier, type ProviderStats, type QuickWin, type RankCheckOptions, type RankCheckResult, type RankHistory, RankTracker, type RankingResult, type RankingTierLimits, type ReadabilityResult, type ReadmeConfig, type ReadmeResult, type ReportBranding, type ReportConfig, type ReportData, type ReportPageData, type RouteInfo, type SEOAnalysisResult, type SEOFixCommit, type SEOFixSummary, type SEOIssue, type SEORecommendation, type SEOScore, SEO_SCOPES, SITE_PROFILE_QUESTIONS, type ScheduledAuditConfig, type ScheduledAuditResult, type SchemaData, Schemas, type SearchIntent, type Sentiment, type SerpApiConfig, SerpClient, type SerpClientConfig, type SerpFeature, type SiteCrawlResult, type SiteProfile, type SiteSummary, type SnippetRecommendation, type SnippetType, type SocialMetaConfig, type SocialMetaFix, type SummarizerOptions, type TFIDFResult, TIER_LIMITS, type ToolFeasibilityScore, type ToolFunction, type ToolResult, type TopicClusterResult, type TopicModel, type TrackedKeyword, type TrackerConfig, type TrackingOptions, type TrendDirection, type UncertaintyAssessment, type WizardQuestion, type WizardResponse, type WizardResult, type WizardSession, type WorkflowConfig, addTrackingResult, analyzeAnchorText, analyzeCanonicalAdvanced, analyzeClientRendering, analyzeContentFreshness, analyzeDOMStructure, analyzeFeaturedSnippetPotential, analyzeHeadings, analyzeHeadline, analyzeHreflang, analyzeImages$1 as analyzeImages, analyzeKeywordDensity, analyzeKeywords, analyzeLinks, analyzeLocalSEO, analyzeMobile, analyzeModernImages, analyzeOnPage, analyzePagination, analyzePerformance, analyzeReadability, analyzeRedirectChain, analyzeRedirects, analyzeResponsiveImages, analyzeSERPPreview, analyzeSecurity, analyzeSecurityHeaders$1 as analyzeSecurityHeaders, analyzeSocialMeta, analyzeStructuredData$1 as analyzeStructuredData, analyzeTrackerBloat, analyzeUrl, analyzeUrlSafety, index$1 as analyzers, applyFixes, assessReplacement, buildDeterministicFixes, buildFrameworkGuidance, buildGSCApiRequest, buildGSCRequest, calculateAIVisibilityScore, calculateBM25, calculateTFIDF$1 as calculateKeywordTFIDF, calculateNextRun, calculateTFIDF, checkAIBotBlocking, checkAMP, checkAdsTxt, checkAppleTouchIcon, checkBalance, checkCertificate, checkDMARC, checkGitHubCLI, checkInternalRedirects, checkJSRenderingRatio, checkLLMCitations, checkLlmsTxt, checkMobileResources, checkPlaintextEmails, checkRedirects, checkRobots, checkRobotsTxt, checkSPF, checkSitemap, classifyIntent, classifyIntents, clusterKeywordsByEmbedding, compareCompetitorVisibility, comparePeriods, completeWizard, crawlSite, crawlUrl, createAuditPR, createFallbackSummary, createGEOHistory, createPullRequest, createSEOCommit, createSEOCommits, describeConcerns, detectDuplicates, detectFramework, detectGitHubPages, detectMentions, detectSoft404, detectTechnologies, detectVisibilityChanges, discoverCompetitorKeywords, discoverPagesFromLinks, discoverRoutesFromRepo, enhanceSummaryWithCompetitors, enhanceToolIdea, enrichKeywordsWithEstimates, ensureGitRepo, evaluateAndEnhanceToolIdeas, evaluateToolFeasibility, executeAgent, extractContentHash, extractEntityPhrases, extractImages, extractKeyPhrases, extractLinks, extractMeta, extractNgrams, extractSchema, extractSeedKeywords, extractTopics, fetchCoreWebVitals, findAlmostPage1Keywords, findAppRoot, findCTROpportunities, findHtmlEntry, findLSIKeywords, findPageFiles, findTopPerformers, formatAlertMessage, formatCIResult, formatCompetitorReport, formatConventionalCommit, formatFeaturedSnippetReport, formatHeadlineReport, formatIntentReport, formatKeywordDensityReport, formatKeywordReport, formatPRBody, formatPRTitle, formatReadabilityReport, formatReport, formatSEOCommitMessage, formatTopicReport, formatWizardProgress, index as frameworks, generateAICitableContent, generateAllFixes, generateAngularSEOService, generateAspNetCoreSEO, generateAstroBaseHead, generateAstroMeta, generateBlogPost, generateBranchName, generateChangelog, generateCommitSummary, generateComparisonTable, generateCompleteSocialMetaSetup, generateDjangoSEOHelper, generateDuplicateIssues, generateEleventySEO, generateFAQSchema, generateFixes, generateGA4EnvTemplate, generateGA4ReactComponent, generateGA4Script, generateGA4ViteScript, generateGEOReport, generateGSCVerificationTag, generateGitHubActionSetup, generateGoSEO, generateHTMLReport, generateHTMLSocialMeta, generateHTMXSEO, generateHeadlineVariations, generateHugoSEO, generateJekyllSEO, generateJsonReport, generateKeyFacts, generateLaravelSEOHelper, generateMarkdownReport, generateNextAppMetadata, generateNextJsAppRouterMetadata, generateNextJsPagesRouterHead, generateNextPagesHead, generateNuxtSEOHead, generatePDFReport, generatePRDescription, generatePhoenixSEO, generateRailsSEOHelper, generateReactHelmetSocialMeta, generateReactSEOHead, generateRecommendationQueries, generateRemixMeta, generateRemixSEO, generateSecretsDoc, generateSocialMetaFix, generateSpringBootSEO, generateSvelteKitMeta, generateSvelteKitSEOHead, generateUncertaintyQuestions, generateVueSEOHead, generateWizardQuestions, generateWorkflow, getAIVisibilitySummary, getAutocompleteSuggestions, getDateRange, getExpandedSuggestions, getFrameworkSpecificFix, getFrameworkSpecificFixExtended, getGSCSetupInstructions, getGitUser, getKeywordData, getKeywordSuggestions, getMaxKdThreshold, getNextQuestion, getRelatedKeywords, getVisibilityTrend, groupKeywordsByTopic, identifyQuickWins, injectGA4, injectGSCVerification, interpolatePrompt, isGitRepo, keywordLLM, listFiles, loadAgent, loadAgentByName, mergePages, openAIKeywordLLM, optimizeForAI, optimizeReadme, parseGEOResponse, parseGSCResponse, parseJsonReply, parseSitemap, prioritizeKeywords, processWizardResponse, readFile, routesToUrls, runAIKeywordResearch, runAIReadinessChecks, runAdditionalChecks, runAuditWithFixes, runCIKeywordResearch, runCrawlabilityChecks, runDirectAnalysis, runFullAudit, runKeywordResearch, runNLPAnalysis, runScheduledAudit, scoreContentSEO, scoreMention, searchCompetitors, searchFormatConverters, searchHackerNews, sendAlert, sendAlerts, sendDiscordAlert, sendSlackAlert, servedDir, shouldRunAudit, shouldSendAlert, startWizardSession, suggestSchemaTypes, summarizeSite, tokenize, tools, trackLLMVisibility, transformGSCData, urlSafetyDatabase, wizardResponsesToContext, writeFile, writeGitHubActionFiles };
package/dist/index.js CHANGED
@@ -3562,6 +3562,7 @@ __export(index_exports, {
3562
3562
  sendAlerts: () => sendAlerts,
3563
3563
  sendDiscordAlert: () => sendDiscordAlert,
3564
3564
  sendSlackAlert: () => sendSlackAlert,
3565
+ servedDir: () => servedDir,
3565
3566
  shouldRunAudit: () => shouldRunAudit,
3566
3567
  shouldSendAlert: () => shouldSendAlert,
3567
3568
  startWizardSession: () => startWizardSession,
@@ -3623,6 +3624,7 @@ var RECIPES = {
3623
3624
  eleventy: null,
3624
3625
  pelican: null,
3625
3626
  sphinx: null,
3627
+ docusaurus: null,
3626
3628
  // Unknown
3627
3629
  unknown: null
3628
3630
  };
@@ -3658,12 +3660,13 @@ async function loadRecipe(framework) {
3658
3660
  }
3659
3661
  return null;
3660
3662
  }
3661
- async function getFrameworkIssues(frameworkInfo, html, url) {
3663
+ async function getFrameworkIssues(frameworkInfo, html, url, options = {}) {
3662
3664
  const recipe = await loadRecipe(frameworkInfo.framework);
3663
3665
  if (!recipe) return [];
3664
3666
  const issues = [];
3667
+ const indexable = options.indexable ?? true;
3665
3668
  for (const check of recipe.checks) {
3666
- const matched = evaluateCheck(check, html, frameworkInfo);
3669
+ const matched = evaluateCheck(check, html, frameworkInfo, indexable);
3667
3670
  if (matched) {
3668
3671
  issues.push({
3669
3672
  code: check.code,
@@ -3684,7 +3687,10 @@ async function getFrameworkIssues(frameworkInfo, html, url) {
3684
3687
  }
3685
3688
  return issues;
3686
3689
  }
3687
- function evaluateCheck(check, html, frameworkInfo) {
3690
+ function evaluateCheck(check, html, frameworkInfo, indexable = true) {
3691
+ if (check.indexable_only && !indexable) {
3692
+ return false;
3693
+ }
3688
3694
  if (check.app_router_only && html.includes("__next")) {
3689
3695
  return false;
3690
3696
  }
@@ -3798,6 +3804,7 @@ seo_challenges:
3798
3804
 
3799
3805
  checks:
3800
3806
  - code: ANGULAR_NO_UNIVERSAL
3807
+ indexable_only: true
3801
3808
  severity: error
3802
3809
  title: "Angular app without Universal (SSR)"
3803
3810
  description: "Client-side Angular apps send empty HTML to crawlers."
@@ -3816,6 +3823,7 @@ checks:
3816
3823
  missing_content: true
3817
3824
 
3818
3825
  - code: ANGULAR_NO_TITLE_SERVICE
3826
+ indexable_only: true
3819
3827
  severity: error
3820
3828
  title: "Not using Title/Meta services"
3821
3829
  description: "Angular has built-in services for managing title and meta tags."
@@ -3864,6 +3872,7 @@ checks:
3864
3872
  \`\`\`
3865
3873
 
3866
3874
  - code: ANGULAR_PRERENDER_MISSING
3875
+ indexable_only: true
3867
3876
  severity: warning
3868
3877
  title: "Static routes not prerendered"
3869
3878
  description: "Static pages should be prerendered at build time."
@@ -4222,6 +4231,7 @@ checks:
4222
4231
  \`\`\`
4223
4232
 
4224
4233
  - code: DJANGO_NO_META_FRAMEWORK
4234
+ indexable_only: true
4225
4235
  severity: warning
4226
4236
  title: "No SEO meta framework detected"
4227
4237
  description: "Use django-meta for consistent meta tag management."
@@ -4369,6 +4379,7 @@ seo_strengths:
4369
4379
 
4370
4380
  checks:
4371
4381
  - code: LARAVEL_NO_META_PACKAGE
4382
+ indexable_only: true
4372
4383
  severity: warning
4373
4384
  title: "No SEO package detected"
4374
4385
  description: "Laravel benefits from an SEO package like artesaos/seotools."
@@ -4420,6 +4431,7 @@ checks:
4420
4431
  \`\`\`
4421
4432
 
4422
4433
  - code: LARAVEL_INERTIA_NO_SSR
4434
+ indexable_only: true
4423
4435
  severity: error
4424
4436
  title: "Inertia.js without SSR"
4425
4437
  description: "Inertia apps are client-rendered by default, losing SEO benefits."
@@ -4514,6 +4526,7 @@ seo_strengths:
4514
4526
  checks:
4515
4527
  # App Router specific
4516
4528
  - code: NEXTJS_NO_METADATA_API
4529
+ indexable_only: true
4517
4530
  severity: warning
4518
4531
  title: "Not using Next.js Metadata API"
4519
4532
  description: "Next.js 13+ has a built-in Metadata API that's better than react-helmet."
@@ -4786,6 +4799,7 @@ seo_strengths:
4786
4799
 
4787
4800
  checks:
4788
4801
  - code: NUXT_NO_USESEOMETA
4802
+ indexable_only: true
4789
4803
  severity: warning
4790
4804
  title: "Not using useSeoMeta composable"
4791
4805
  description: "Nuxt 3 has built-in SEO meta composables that are cleaner than useHead."
@@ -4895,6 +4909,7 @@ checks:
4895
4909
  \`\`\`
4896
4910
 
4897
4911
  - code: NUXT_SPA_MODE
4912
+ indexable_only: true
4898
4913
  severity: error
4899
4914
  title: "Nuxt running in SPA mode"
4900
4915
  description: "SPA mode disables SSR, losing SEO benefits."
@@ -4920,6 +4935,7 @@ checks:
4920
4935
  missing_content: true
4921
4936
 
4922
4937
  - code: NUXT_NO_PRERENDER
4938
+ indexable_only: true
4923
4939
  severity: notice
4924
4940
  title: "Static pages not pre-rendered"
4925
4941
  description: "Static pages could be pre-rendered for better performance."
@@ -4963,6 +4979,7 @@ seo_strengths:
4963
4979
 
4964
4980
  checks:
4965
4981
  - code: RAILS_NO_META_TAGS_GEM
4982
+ indexable_only: true
4966
4983
  severity: warning
4967
4984
  title: "Not using meta-tags gem"
4968
4985
  description: "The meta-tags gem provides a clean API for SEO tags."
@@ -5035,6 +5052,7 @@ checks:
5035
5052
  \`\`\`
5036
5053
 
5037
5054
  - code: RAILS_TURBO_BREAKS_CRAWLERS
5055
+ indexable_only: true
5038
5056
  severity: notice
5039
5057
  title: "Turbo may affect JavaScript-dependent crawlers"
5040
5058
  description: "Turbo Drive updates pages via JavaScript, which some crawlers may miss."
@@ -5157,6 +5175,7 @@ seo_challenges:
5157
5175
  checks:
5158
5176
  # Critical - these will severely hurt SEO
5159
5177
  - code: REACT_CSR_DETECTED
5178
+ indexable_only: true
5160
5179
  severity: error
5161
5180
  title: "Client-side rendered React app detected"
5162
5181
  description: "React apps without SSR send empty HTML to crawlers. Search engines may not index your content."
@@ -5172,6 +5191,7 @@ checks:
5172
5191
  missing_content: true # Body has minimal text content
5173
5192
 
5174
5193
  - code: REACT_NO_SSR_META
5194
+ indexable_only: true
5175
5195
  severity: error
5176
5196
  title: "Meta tags not server-rendered"
5177
5197
  description: "Title and meta description are set client-side and won't be seen by crawlers."
@@ -5238,6 +5258,7 @@ checks:
5238
5258
  \`\`\`
5239
5259
 
5240
5260
  - code: REACT_ROUTER_NO_SSR
5261
+ indexable_only: true
5241
5262
  severity: warning
5242
5263
  title: "React Router without server-side support"
5243
5264
  description: "Client-side routing breaks when crawlers access URLs directly."
@@ -5256,6 +5277,7 @@ checks:
5256
5277
  \`\`\`
5257
5278
 
5258
5279
  - code: REACT_NO_PRERENDER
5280
+ indexable_only: true
5259
5281
  severity: warning
5260
5282
  title: "No prerendering configured"
5261
5283
  description: "Static pages could be prerendered for better SEO and performance."
@@ -5412,6 +5434,7 @@ checks:
5412
5434
  - 'name="description"'
5413
5435
 
5414
5436
  - code: SVELTEKIT_NO_PRERENDER
5437
+ indexable_only: true
5415
5438
  severity: notice
5416
5439
  title: "Static pages not prerendered"
5417
5440
  description: "Static pages should be prerendered for best performance."
@@ -5517,6 +5540,7 @@ seo_challenges:
5517
5540
 
5518
5541
  checks:
5519
5542
  - code: VUE_CSR_DETECTED
5543
+ indexable_only: true
5520
5544
  severity: error
5521
5545
  title: "Client-side rendered Vue app detected"
5522
5546
  description: "Vue apps without SSR send empty HTML to crawlers."
@@ -5533,6 +5557,7 @@ checks:
5533
5557
  missing_content: true
5534
5558
 
5535
5559
  - code: VUE_NO_META_MANAGEMENT
5560
+ indexable_only: true
5536
5561
  severity: error
5537
5562
  title: "No meta tag management library"
5538
5563
  description: "Title and meta description are not dynamically managed."
@@ -7836,6 +7861,11 @@ var SIGNALS = [
7836
7861
  // Sphinx: what nearly every Python project's documentation is built with, and the shape
7837
7862
  // flask.palletsprojects.com turned out to be. Structural markers only - not the word
7838
7863
  // "sphinx", which appears in plenty of prose.
7864
+ // Validated against docusaurus.io, jestjs.io and redux.js.org - all three carry the mount
7865
+ // point and the skip-to-content id. Deliberately not the word "docusaurus", which appears on
7866
+ // deno.com purely as a link in a logo list; that near-miss is why this needed a real marker.
7867
+ { framework: "docusaurus", language: "typescript", weight: WEIGHT.DECISIVE, name: "docusaurus-root", html: /id=["']__docusaurus/ },
7868
+ { framework: "docusaurus", language: "typescript", weight: WEIGHT.STRONG, name: "docusaurus-skip-link", html: /__docusaurus_skipToContent/ },
7839
7869
  { framework: "sphinx", language: "python", weight: WEIGHT.DECISIVE, name: "sphinx-doc-options", html: /_static\/documentation_options\.js/ },
7840
7870
  { framework: "sphinx", language: "python", weight: WEIGHT.STRONG, name: "sphinx-theme-markup", html: /class=["'][^"']*sphinxsidebar|sphinx-highlight|_static\/searchtools\.js/ },
7841
7871
  { framework: "sphinx", language: "python", weight: WEIGHT.STRONG, name: "docutils-generator", html: /name=["']?generator["']?[^>]*content=["']?Docutils/i }
@@ -8249,6 +8279,7 @@ function getFrameworkDisplayName(framework) {
8249
8279
  eleventy: "Eleventy (11ty)",
8250
8280
  pelican: "Pelican",
8251
8281
  sphinx: "Sphinx",
8282
+ docusaurus: "Docusaurus",
8252
8283
  unknown: "Unknown"
8253
8284
  };
8254
8285
  return names[framework];
@@ -8494,6 +8525,23 @@ function metaHasNoindex(content) {
8494
8525
  if (!content) return false;
8495
8526
  return content.toLowerCase().split(",").some((d) => ["noindex", "none"].includes(d.trim()));
8496
8527
  }
8528
+ function readRobotsDirectives(html, headers) {
8529
+ const $ = cheerio.load(html);
8530
+ const metaValues = [];
8531
+ $("meta[name]").each((_, el) => {
8532
+ const name = ($(el).attr("name") || "").toLowerCase();
8533
+ if (name === "robots" || name === "googlebot") {
8534
+ metaValues.push($(el).attr("content") || "");
8535
+ }
8536
+ });
8537
+ const metaNoindex = metaValues.some(metaHasNoindex);
8538
+ const xRobotsTag = headers ? Object.entries(headers).find(([k]) => k.toLowerCase() === "x-robots-tag")?.[1] : void 0;
8539
+ const headerNoindex = xRobotsTagHasNoindex(xRobotsTag);
8540
+ return { metaValues, metaNoindex, xRobotsTag, headerNoindex, hasNoindex: metaNoindex || headerNoindex };
8541
+ }
8542
+ function pageHasNoindex(html, headers) {
8543
+ return readRobotsDirectives(html, headers).hasNoindex;
8544
+ }
8497
8545
  function isSiteRoot(url) {
8498
8546
  try {
8499
8547
  const u = new URL(url);
@@ -8509,17 +8557,7 @@ function analyzeOnPage(html, url, headers, options = {}) {
8509
8557
  const description = $('meta[name="description"]').attr("content")?.trim();
8510
8558
  const canonical = $('link[rel="canonical"]').attr("href");
8511
8559
  const metaRobots = $('meta[name="robots"]').attr("content");
8512
- const robotsMetaValues = [];
8513
- $("meta[name]").each((_, el) => {
8514
- const name = ($(el).attr("name") || "").toLowerCase();
8515
- if (name === "robots" || name === "googlebot") {
8516
- robotsMetaValues.push($(el).attr("content") || "");
8517
- }
8518
- });
8519
- const metaNoindex = robotsMetaValues.some(metaHasNoindex);
8520
- const headerLookup = headers ? Object.entries(headers).find(([k]) => k.toLowerCase() === "x-robots-tag")?.[1] : void 0;
8521
- const headerNoindex = xRobotsTagHasNoindex(headerLookup);
8522
- const hasNoindex = metaNoindex || headerNoindex;
8560
+ const { metaValues: robotsMetaValues, metaNoindex, xRobotsTag: headerLookup, headerNoindex, hasNoindex } = readRobotsDirectives(html, headers);
8523
8561
  const h1s = [];
8524
8562
  $("h1").each((_, el) => {
8525
8563
  h1s.push($(el).text().trim());
@@ -15940,6 +15978,17 @@ var NAMED_COLORS = {
15940
15978
  transparent: [255, 255, 255]
15941
15979
  // Treat as white for calculation
15942
15980
  };
15981
+ function inheritedBackground($el) {
15982
+ for (const ancestor of $el.parents().toArray()) {
15983
+ const style = ancestor.attribs?.style;
15984
+ if (!style) continue;
15985
+ const match = style.match(/(?:^|;)\s*background(?:-color)?\s*:\s*([^;]+)/i);
15986
+ if (!match) continue;
15987
+ const value = match[1].trim();
15988
+ if (!/^(transparent|none)$/i.test(value)) return value;
15989
+ }
15990
+ return null;
15991
+ }
15943
15992
  function analyzeColorContrast(html, url) {
15944
15993
  const issues = [];
15945
15994
  const $ = cheerio32.load(html);
@@ -15960,12 +16009,16 @@ function analyzeColorContrast(html, url) {
15960
16009
  const colorMatch = style.match(/(?:^|;)\s*color\s*:\s*([^;]+)/i);
15961
16010
  const bgMatch = style.match(/(?:^|;)\s*background(?:-color)?\s*:\s*([^;]+)/i);
15962
16011
  const foreground = colorMatch ? colorMatch[1].trim() : null;
15963
- const background = bgMatch ? bgMatch[1].trim() : null;
16012
+ const background = bgMatch ? bgMatch[1].trim() : inheritedBackground($el);
15964
16013
  if (!foreground) {
15965
16014
  cannotDetermine++;
15966
16015
  return;
15967
16016
  }
15968
- const bgColor = background || "white";
16017
+ if (!background) {
16018
+ cannotDetermine++;
16019
+ return;
16020
+ }
16021
+ const bgColor = background;
15969
16022
  const fgRgb = parseColor(foreground);
15970
16023
  const bgRgb = parseColor(bgColor);
15971
16024
  if (!fgRgb || !bgRgb) {
@@ -17661,7 +17714,9 @@ async function runFullAudit(options) {
17661
17714
  asyncChecks.push(safeAsyncIssues("Framework Detection", async () => {
17662
17715
  const frameworkInfo = detectFramework({ html, headers });
17663
17716
  if (frameworkInfo.framework === "unknown") return [];
17664
- return getFrameworkIssues(frameworkInfo, html, url);
17717
+ return getFrameworkIssues(frameworkInfo, html, url, {
17718
+ indexable: !pageHasNoindex(html, headers)
17719
+ });
17665
17720
  }));
17666
17721
  if (runExtendedChecks) {
17667
17722
  asyncChecks.push(safeAsync("Hreflang", () => analyzeHreflang(html, url, { validateUrls: checkHreflangUrls })));
@@ -32936,6 +32991,48 @@ function findAppRoot(files) {
32936
32991
  return `${nonRoot[0]}/`;
32937
32992
  }
32938
32993
 
32994
+ // src/fixer/served-dir.ts
32995
+ var SERVED_DIR = {
32996
+ // Bundlers and meta-frameworks that copy a public/ directory verbatim.
32997
+ react: "public/",
32998
+ vue: "public/",
32999
+ svelte: "public/",
33000
+ angular: "public/",
33001
+ solidjs: "public/",
33002
+ qwik: "public/",
33003
+ nextjs: "public/",
33004
+ nuxt: "public/",
33005
+ astro: "public/",
33006
+ sveltekit: "static/",
33007
+ // SvelteKit's is static/, not public/
33008
+ remix: "public/",
33009
+ fresh: "static/",
33010
+ // Gatsby, Hugo, Eleventy and Docusaurus all name theirs static/.
33011
+ gatsby: "static/",
33012
+ hugo: "static/",
33013
+ eleventy: "public/",
33014
+ docusaurus: "static/",
33015
+ // Server frameworks with a public document root.
33016
+ rails: "public/",
33017
+ laravel: "public/",
33018
+ symfony: "public/",
33019
+ phoenix: "priv/static/",
33020
+ spring: "src/main/resources/static/",
33021
+ aspnet: "wwwroot/",
33022
+ // Jekyll and Pelican copy their source tree, so the app root is the served root.
33023
+ jekyll: "",
33024
+ pelican: ""
33025
+ // Python web frameworks have no single convention; the tree decides below.
33026
+ };
33027
+ function servedDir(framework, files, appRoot = "") {
33028
+ const known = framework ? SERVED_DIR[framework] : void 0;
33029
+ if (known !== void 0) return known;
33030
+ for (const candidate of ["public/", "static/", "assets/"]) {
33031
+ if (files.some((f) => f.startsWith(`${appRoot}${candidate}`))) return candidate;
33032
+ }
33033
+ return "";
33034
+ }
33035
+
32939
33036
  // src/fixer/safety.ts
32940
33037
  var HTML_EXT = /\.html?$/i;
32941
33038
  var SOURCE_EXT = /\.(ts|tsx|js|jsx|mjs|cjs|vue|svelte|astro)$/i;
@@ -35915,6 +36012,7 @@ var SUPPORTED_FRAMEWORKS = [
35915
36012
  sendAlerts,
35916
36013
  sendDiscordAlert,
35917
36014
  sendSlackAlert,
36015
+ servedDir,
35918
36016
  shouldRunAudit,
35919
36017
  shouldSendAlert,
35920
36018
  startWizardSession,
package/dist/index.mjs CHANGED
@@ -95,6 +95,7 @@ var RECIPES = {
95
95
  eleventy: null,
96
96
  pelican: null,
97
97
  sphinx: null,
98
+ docusaurus: null,
98
99
  // Unknown
99
100
  unknown: null
100
101
  };
@@ -130,12 +131,13 @@ async function loadRecipe(framework) {
130
131
  }
131
132
  return null;
132
133
  }
133
- async function getFrameworkIssues(frameworkInfo, html, url) {
134
+ async function getFrameworkIssues(frameworkInfo, html, url, options = {}) {
134
135
  const recipe = await loadRecipe(frameworkInfo.framework);
135
136
  if (!recipe) return [];
136
137
  const issues = [];
138
+ const indexable = options.indexable ?? true;
137
139
  for (const check of recipe.checks) {
138
- const matched = evaluateCheck(check, html, frameworkInfo);
140
+ const matched = evaluateCheck(check, html, frameworkInfo, indexable);
139
141
  if (matched) {
140
142
  issues.push({
141
143
  code: check.code,
@@ -156,7 +158,10 @@ async function getFrameworkIssues(frameworkInfo, html, url) {
156
158
  }
157
159
  return issues;
158
160
  }
159
- function evaluateCheck(check, html, frameworkInfo) {
161
+ function evaluateCheck(check, html, frameworkInfo, indexable = true) {
162
+ if (check.indexable_only && !indexable) {
163
+ return false;
164
+ }
160
165
  if (check.app_router_only && html.includes("__next")) {
161
166
  return false;
162
167
  }
@@ -270,6 +275,7 @@ seo_challenges:
270
275
 
271
276
  checks:
272
277
  - code: ANGULAR_NO_UNIVERSAL
278
+ indexable_only: true
273
279
  severity: error
274
280
  title: "Angular app without Universal (SSR)"
275
281
  description: "Client-side Angular apps send empty HTML to crawlers."
@@ -288,6 +294,7 @@ checks:
288
294
  missing_content: true
289
295
 
290
296
  - code: ANGULAR_NO_TITLE_SERVICE
297
+ indexable_only: true
291
298
  severity: error
292
299
  title: "Not using Title/Meta services"
293
300
  description: "Angular has built-in services for managing title and meta tags."
@@ -336,6 +343,7 @@ checks:
336
343
  \`\`\`
337
344
 
338
345
  - code: ANGULAR_PRERENDER_MISSING
346
+ indexable_only: true
339
347
  severity: warning
340
348
  title: "Static routes not prerendered"
341
349
  description: "Static pages should be prerendered at build time."
@@ -694,6 +702,7 @@ checks:
694
702
  \`\`\`
695
703
 
696
704
  - code: DJANGO_NO_META_FRAMEWORK
705
+ indexable_only: true
697
706
  severity: warning
698
707
  title: "No SEO meta framework detected"
699
708
  description: "Use django-meta for consistent meta tag management."
@@ -841,6 +850,7 @@ seo_strengths:
841
850
 
842
851
  checks:
843
852
  - code: LARAVEL_NO_META_PACKAGE
853
+ indexable_only: true
844
854
  severity: warning
845
855
  title: "No SEO package detected"
846
856
  description: "Laravel benefits from an SEO package like artesaos/seotools."
@@ -892,6 +902,7 @@ checks:
892
902
  \`\`\`
893
903
 
894
904
  - code: LARAVEL_INERTIA_NO_SSR
905
+ indexable_only: true
895
906
  severity: error
896
907
  title: "Inertia.js without SSR"
897
908
  description: "Inertia apps are client-rendered by default, losing SEO benefits."
@@ -986,6 +997,7 @@ seo_strengths:
986
997
  checks:
987
998
  # App Router specific
988
999
  - code: NEXTJS_NO_METADATA_API
1000
+ indexable_only: true
989
1001
  severity: warning
990
1002
  title: "Not using Next.js Metadata API"
991
1003
  description: "Next.js 13+ has a built-in Metadata API that's better than react-helmet."
@@ -1258,6 +1270,7 @@ seo_strengths:
1258
1270
 
1259
1271
  checks:
1260
1272
  - code: NUXT_NO_USESEOMETA
1273
+ indexable_only: true
1261
1274
  severity: warning
1262
1275
  title: "Not using useSeoMeta composable"
1263
1276
  description: "Nuxt 3 has built-in SEO meta composables that are cleaner than useHead."
@@ -1367,6 +1380,7 @@ checks:
1367
1380
  \`\`\`
1368
1381
 
1369
1382
  - code: NUXT_SPA_MODE
1383
+ indexable_only: true
1370
1384
  severity: error
1371
1385
  title: "Nuxt running in SPA mode"
1372
1386
  description: "SPA mode disables SSR, losing SEO benefits."
@@ -1392,6 +1406,7 @@ checks:
1392
1406
  missing_content: true
1393
1407
 
1394
1408
  - code: NUXT_NO_PRERENDER
1409
+ indexable_only: true
1395
1410
  severity: notice
1396
1411
  title: "Static pages not pre-rendered"
1397
1412
  description: "Static pages could be pre-rendered for better performance."
@@ -1435,6 +1450,7 @@ seo_strengths:
1435
1450
 
1436
1451
  checks:
1437
1452
  - code: RAILS_NO_META_TAGS_GEM
1453
+ indexable_only: true
1438
1454
  severity: warning
1439
1455
  title: "Not using meta-tags gem"
1440
1456
  description: "The meta-tags gem provides a clean API for SEO tags."
@@ -1507,6 +1523,7 @@ checks:
1507
1523
  \`\`\`
1508
1524
 
1509
1525
  - code: RAILS_TURBO_BREAKS_CRAWLERS
1526
+ indexable_only: true
1510
1527
  severity: notice
1511
1528
  title: "Turbo may affect JavaScript-dependent crawlers"
1512
1529
  description: "Turbo Drive updates pages via JavaScript, which some crawlers may miss."
@@ -1629,6 +1646,7 @@ seo_challenges:
1629
1646
  checks:
1630
1647
  # Critical - these will severely hurt SEO
1631
1648
  - code: REACT_CSR_DETECTED
1649
+ indexable_only: true
1632
1650
  severity: error
1633
1651
  title: "Client-side rendered React app detected"
1634
1652
  description: "React apps without SSR send empty HTML to crawlers. Search engines may not index your content."
@@ -1644,6 +1662,7 @@ checks:
1644
1662
  missing_content: true # Body has minimal text content
1645
1663
 
1646
1664
  - code: REACT_NO_SSR_META
1665
+ indexable_only: true
1647
1666
  severity: error
1648
1667
  title: "Meta tags not server-rendered"
1649
1668
  description: "Title and meta description are set client-side and won't be seen by crawlers."
@@ -1710,6 +1729,7 @@ checks:
1710
1729
  \`\`\`
1711
1730
 
1712
1731
  - code: REACT_ROUTER_NO_SSR
1732
+ indexable_only: true
1713
1733
  severity: warning
1714
1734
  title: "React Router without server-side support"
1715
1735
  description: "Client-side routing breaks when crawlers access URLs directly."
@@ -1728,6 +1748,7 @@ checks:
1728
1748
  \`\`\`
1729
1749
 
1730
1750
  - code: REACT_NO_PRERENDER
1751
+ indexable_only: true
1731
1752
  severity: warning
1732
1753
  title: "No prerendering configured"
1733
1754
  description: "Static pages could be prerendered for better SEO and performance."
@@ -1884,6 +1905,7 @@ checks:
1884
1905
  - 'name="description"'
1885
1906
 
1886
1907
  - code: SVELTEKIT_NO_PRERENDER
1908
+ indexable_only: true
1887
1909
  severity: notice
1888
1910
  title: "Static pages not prerendered"
1889
1911
  description: "Static pages should be prerendered for best performance."
@@ -1989,6 +2011,7 @@ seo_challenges:
1989
2011
 
1990
2012
  checks:
1991
2013
  - code: VUE_CSR_DETECTED
2014
+ indexable_only: true
1992
2015
  severity: error
1993
2016
  title: "Client-side rendered Vue app detected"
1994
2017
  description: "Vue apps without SSR send empty HTML to crawlers."
@@ -2005,6 +2028,7 @@ checks:
2005
2028
  missing_content: true
2006
2029
 
2007
2030
  - code: VUE_NO_META_MANAGEMENT
2031
+ indexable_only: true
2008
2032
  severity: error
2009
2033
  title: "No meta tag management library"
2010
2034
  description: "Title and meta description are not dynamically managed."
@@ -4305,6 +4329,11 @@ var SIGNALS = [
4305
4329
  // Sphinx: what nearly every Python project's documentation is built with, and the shape
4306
4330
  // flask.palletsprojects.com turned out to be. Structural markers only - not the word
4307
4331
  // "sphinx", which appears in plenty of prose.
4332
+ // Validated against docusaurus.io, jestjs.io and redux.js.org - all three carry the mount
4333
+ // point and the skip-to-content id. Deliberately not the word "docusaurus", which appears on
4334
+ // deno.com purely as a link in a logo list; that near-miss is why this needed a real marker.
4335
+ { framework: "docusaurus", language: "typescript", weight: WEIGHT.DECISIVE, name: "docusaurus-root", html: /id=["']__docusaurus/ },
4336
+ { framework: "docusaurus", language: "typescript", weight: WEIGHT.STRONG, name: "docusaurus-skip-link", html: /__docusaurus_skipToContent/ },
4308
4337
  { framework: "sphinx", language: "python", weight: WEIGHT.DECISIVE, name: "sphinx-doc-options", html: /_static\/documentation_options\.js/ },
4309
4338
  { framework: "sphinx", language: "python", weight: WEIGHT.STRONG, name: "sphinx-theme-markup", html: /class=["'][^"']*sphinxsidebar|sphinx-highlight|_static\/searchtools\.js/ },
4310
4339
  { framework: "sphinx", language: "python", weight: WEIGHT.STRONG, name: "docutils-generator", html: /name=["']?generator["']?[^>]*content=["']?Docutils/i }
@@ -4718,6 +4747,7 @@ function getFrameworkDisplayName(framework) {
4718
4747
  eleventy: "Eleventy (11ty)",
4719
4748
  pelican: "Pelican",
4720
4749
  sphinx: "Sphinx",
4750
+ docusaurus: "Docusaurus",
4721
4751
  unknown: "Unknown"
4722
4752
  };
4723
4753
  return names[framework];
@@ -4962,6 +4992,23 @@ function metaHasNoindex(content) {
4962
4992
  if (!content) return false;
4963
4993
  return content.toLowerCase().split(",").some((d) => ["noindex", "none"].includes(d.trim()));
4964
4994
  }
4995
+ function readRobotsDirectives(html, headers) {
4996
+ const $ = cheerio.load(html);
4997
+ const metaValues = [];
4998
+ $("meta[name]").each((_, el) => {
4999
+ const name = ($(el).attr("name") || "").toLowerCase();
5000
+ if (name === "robots" || name === "googlebot") {
5001
+ metaValues.push($(el).attr("content") || "");
5002
+ }
5003
+ });
5004
+ const metaNoindex = metaValues.some(metaHasNoindex);
5005
+ const xRobotsTag = headers ? Object.entries(headers).find(([k]) => k.toLowerCase() === "x-robots-tag")?.[1] : void 0;
5006
+ const headerNoindex = xRobotsTagHasNoindex(xRobotsTag);
5007
+ return { metaValues, metaNoindex, xRobotsTag, headerNoindex, hasNoindex: metaNoindex || headerNoindex };
5008
+ }
5009
+ function pageHasNoindex(html, headers) {
5010
+ return readRobotsDirectives(html, headers).hasNoindex;
5011
+ }
4965
5012
  function isSiteRoot(url) {
4966
5013
  try {
4967
5014
  const u = new URL(url);
@@ -4977,17 +5024,7 @@ function analyzeOnPage(html, url, headers, options = {}) {
4977
5024
  const description = $('meta[name="description"]').attr("content")?.trim();
4978
5025
  const canonical = $('link[rel="canonical"]').attr("href");
4979
5026
  const metaRobots = $('meta[name="robots"]').attr("content");
4980
- const robotsMetaValues = [];
4981
- $("meta[name]").each((_, el) => {
4982
- const name = ($(el).attr("name") || "").toLowerCase();
4983
- if (name === "robots" || name === "googlebot") {
4984
- robotsMetaValues.push($(el).attr("content") || "");
4985
- }
4986
- });
4987
- const metaNoindex = robotsMetaValues.some(metaHasNoindex);
4988
- const headerLookup = headers ? Object.entries(headers).find(([k]) => k.toLowerCase() === "x-robots-tag")?.[1] : void 0;
4989
- const headerNoindex = xRobotsTagHasNoindex(headerLookup);
4990
- const hasNoindex = metaNoindex || headerNoindex;
5027
+ const { metaValues: robotsMetaValues, metaNoindex, xRobotsTag: headerLookup, headerNoindex, hasNoindex } = readRobotsDirectives(html, headers);
4991
5028
  const h1s = [];
4992
5029
  $("h1").each((_, el) => {
4993
5030
  h1s.push($(el).text().trim());
@@ -12387,6 +12424,17 @@ var NAMED_COLORS = {
12387
12424
  transparent: [255, 255, 255]
12388
12425
  // Treat as white for calculation
12389
12426
  };
12427
+ function inheritedBackground($el) {
12428
+ for (const ancestor of $el.parents().toArray()) {
12429
+ const style = ancestor.attribs?.style;
12430
+ if (!style) continue;
12431
+ const match = style.match(/(?:^|;)\s*background(?:-color)?\s*:\s*([^;]+)/i);
12432
+ if (!match) continue;
12433
+ const value = match[1].trim();
12434
+ if (!/^(transparent|none)$/i.test(value)) return value;
12435
+ }
12436
+ return null;
12437
+ }
12390
12438
  function analyzeColorContrast(html, url) {
12391
12439
  const issues = [];
12392
12440
  const $ = cheerio32.load(html);
@@ -12407,12 +12455,16 @@ function analyzeColorContrast(html, url) {
12407
12455
  const colorMatch = style.match(/(?:^|;)\s*color\s*:\s*([^;]+)/i);
12408
12456
  const bgMatch = style.match(/(?:^|;)\s*background(?:-color)?\s*:\s*([^;]+)/i);
12409
12457
  const foreground = colorMatch ? colorMatch[1].trim() : null;
12410
- const background = bgMatch ? bgMatch[1].trim() : null;
12458
+ const background = bgMatch ? bgMatch[1].trim() : inheritedBackground($el);
12411
12459
  if (!foreground) {
12412
12460
  cannotDetermine++;
12413
12461
  return;
12414
12462
  }
12415
- const bgColor = background || "white";
12463
+ if (!background) {
12464
+ cannotDetermine++;
12465
+ return;
12466
+ }
12467
+ const bgColor = background;
12416
12468
  const fgRgb = parseColor(foreground);
12417
12469
  const bgRgb = parseColor(bgColor);
12418
12470
  if (!fgRgb || !bgRgb) {
@@ -14105,7 +14157,9 @@ async function runFullAudit(options) {
14105
14157
  asyncChecks.push(safeAsyncIssues("Framework Detection", async () => {
14106
14158
  const frameworkInfo = detectFramework({ html, headers });
14107
14159
  if (frameworkInfo.framework === "unknown") return [];
14108
- return getFrameworkIssues(frameworkInfo, html, url);
14160
+ return getFrameworkIssues(frameworkInfo, html, url, {
14161
+ indexable: !pageHasNoindex(html, headers)
14162
+ });
14109
14163
  }));
14110
14164
  if (runExtendedChecks) {
14111
14165
  asyncChecks.push(safeAsync("Hreflang", () => analyzeHreflang(html, url, { validateUrls: checkHreflangUrls })));
@@ -29365,6 +29419,48 @@ function findAppRoot(files) {
29365
29419
  return `${nonRoot[0]}/`;
29366
29420
  }
29367
29421
 
29422
+ // src/fixer/served-dir.ts
29423
+ var SERVED_DIR = {
29424
+ // Bundlers and meta-frameworks that copy a public/ directory verbatim.
29425
+ react: "public/",
29426
+ vue: "public/",
29427
+ svelte: "public/",
29428
+ angular: "public/",
29429
+ solidjs: "public/",
29430
+ qwik: "public/",
29431
+ nextjs: "public/",
29432
+ nuxt: "public/",
29433
+ astro: "public/",
29434
+ sveltekit: "static/",
29435
+ // SvelteKit's is static/, not public/
29436
+ remix: "public/",
29437
+ fresh: "static/",
29438
+ // Gatsby, Hugo, Eleventy and Docusaurus all name theirs static/.
29439
+ gatsby: "static/",
29440
+ hugo: "static/",
29441
+ eleventy: "public/",
29442
+ docusaurus: "static/",
29443
+ // Server frameworks with a public document root.
29444
+ rails: "public/",
29445
+ laravel: "public/",
29446
+ symfony: "public/",
29447
+ phoenix: "priv/static/",
29448
+ spring: "src/main/resources/static/",
29449
+ aspnet: "wwwroot/",
29450
+ // Jekyll and Pelican copy their source tree, so the app root is the served root.
29451
+ jekyll: "",
29452
+ pelican: ""
29453
+ // Python web frameworks have no single convention; the tree decides below.
29454
+ };
29455
+ function servedDir(framework, files, appRoot = "") {
29456
+ const known = framework ? SERVED_DIR[framework] : void 0;
29457
+ if (known !== void 0) return known;
29458
+ for (const candidate of ["public/", "static/", "assets/"]) {
29459
+ if (files.some((f) => f.startsWith(`${appRoot}${candidate}`))) return candidate;
29460
+ }
29461
+ return "";
29462
+ }
29463
+
29368
29464
  // src/fixer/safety.ts
29369
29465
  var HTML_EXT = /\.html?$/i;
29370
29466
  var SOURCE_EXT = /\.(ts|tsx|js|jsx|mjs|cjs|vue|svelte|astro)$/i;
@@ -32336,6 +32432,7 @@ export {
32336
32432
  sendAlerts,
32337
32433
  sendDiscordAlert,
32338
32434
  sendSlackAlert,
32435
+ servedDir,
32339
32436
  shouldRunAudit,
32340
32437
  shouldSendAlert,
32341
32438
  startWizardSession,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rankcli/agent-runtime",
3
- "version": "0.0.31",
3
+ "version": "0.0.33",
4
4
  "description": "RankCLI agent runtime - executes SEO audits and fixes with AI",
5
5
  "homepage": "https://rankcli.dev",
6
6
  "main": "dist/index.js",