recker 1.0.42 → 1.0.43-next.7a08602
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/bin/recker-linux-x64 +0 -0
- package/dist/bin/recker-macos-x64 +0 -0
- package/dist/bin/recker-win-x64.exe +0 -0
- package/dist/bin/rek.cjs +93958 -0
- package/dist/browser/ai/adaptive-timeout.d.ts +50 -0
- package/dist/browser/ai/adaptive-timeout.js +208 -0
- package/dist/browser/ai/client.d.ts +22 -0
- package/dist/browser/ai/client.js +294 -0
- package/dist/browser/ai/index.d.ts +14 -0
- package/dist/browser/ai/index.js +11 -0
- package/dist/browser/ai/providers/anthropic.d.ts +63 -0
- package/dist/browser/ai/providers/anthropic.js +370 -0
- package/dist/browser/ai/providers/base.d.ts +48 -0
- package/dist/browser/ai/providers/base.js +150 -0
- package/dist/browser/ai/providers/google.d.ts +59 -0
- package/dist/browser/ai/providers/google.js +305 -0
- package/dist/browser/ai/providers/ollama.d.ts +44 -0
- package/dist/browser/ai/providers/ollama.js +240 -0
- package/dist/browser/ai/providers/openai.d.ts +64 -0
- package/dist/browser/ai/providers/openai.js +298 -0
- package/dist/browser/ai/rate-limiter.d.ts +43 -0
- package/dist/browser/ai/rate-limiter.js +215 -0
- package/dist/browser/ai/vector/index.d.ts +2 -0
- package/dist/browser/ai/vector/index.js +2 -0
- package/dist/browser/ai/vector/similarity.d.ts +2 -0
- package/dist/browser/ai/vector/similarity.js +27 -0
- package/dist/browser/ai/vector/store.d.ts +27 -0
- package/dist/browser/ai/vector/store.js +82 -0
- package/dist/browser/browser/cache.d.ts +2 -40
- package/dist/browser/browser/cache.js +2 -199
- package/dist/browser/browser/index.d.ts +8 -0
- package/dist/browser/browser/index.js +8 -0
- package/dist/browser/browser/recker.d.ts +8 -1
- package/dist/browser/browser/recker.js +8 -2
- package/dist/browser/cache/indexed-db.d.ts +10 -0
- package/dist/browser/cache/indexed-db.js +88 -0
- package/dist/browser/cache/service-worker-cache.d.ts +18 -0
- package/dist/browser/cache/service-worker-cache.js +103 -0
- package/dist/browser/cache.d.ts +2 -40
- package/dist/browser/cache.js +2 -199
- package/dist/browser/constants/user-agents.d.ts +7 -0
- package/dist/browser/constants/user-agents.js +7 -0
- package/dist/browser/core/client.d.ts +2 -0
- package/dist/browser/core/client.js +19 -1
- package/dist/browser/index.d.ts +8 -0
- package/dist/browser/index.js +8 -0
- package/dist/browser/plugins/har-recorder.d.ts +40 -0
- package/dist/browser/plugins/har-recorder.js +120 -0
- package/dist/browser/plugins/network-simulation.d.ts +7 -0
- package/dist/browser/plugins/network-simulation.js +13 -0
- package/dist/browser/presets/android.d.ts +2 -0
- package/dist/browser/presets/android.js +16 -0
- package/dist/browser/presets/anthropic.d.ts +8 -0
- package/dist/browser/presets/anthropic.js +27 -0
- package/dist/browser/presets/aws.d.ts +19 -0
- package/dist/browser/presets/aws.js +68 -0
- package/dist/browser/presets/azure-openai.d.ts +10 -0
- package/dist/browser/presets/azure-openai.js +35 -0
- package/dist/browser/presets/azure.d.ts +41 -0
- package/dist/browser/presets/azure.js +104 -0
- package/dist/browser/presets/chaturbate.d.ts +2 -0
- package/dist/browser/presets/chaturbate.js +17 -0
- package/dist/browser/presets/cloudflare.d.ts +12 -0
- package/dist/browser/presets/cloudflare.js +39 -0
- package/dist/browser/presets/cohere.d.ts +7 -0
- package/dist/browser/presets/cohere.js +22 -0
- package/dist/browser/presets/deepseek.d.ts +7 -0
- package/dist/browser/presets/deepseek.js +22 -0
- package/dist/browser/presets/digitalocean.d.ts +5 -0
- package/dist/browser/presets/digitalocean.js +16 -0
- package/dist/browser/presets/discord.d.ts +6 -0
- package/dist/browser/presets/discord.js +17 -0
- package/dist/browser/presets/elevenlabs.d.ts +6 -0
- package/dist/browser/presets/elevenlabs.js +20 -0
- package/dist/browser/presets/enhancers.d.ts +20 -0
- package/dist/browser/presets/enhancers.js +85 -0
- package/dist/browser/presets/fireworks.d.ts +7 -0
- package/dist/browser/presets/fireworks.js +22 -0
- package/dist/browser/presets/gcp.d.ts +34 -0
- package/dist/browser/presets/gcp.js +91 -0
- package/dist/browser/presets/gemini.d.ts +7 -0
- package/dist/browser/presets/gemini.js +23 -0
- package/dist/browser/presets/github.d.ts +6 -0
- package/dist/browser/presets/github.js +17 -0
- package/dist/browser/presets/gitlab.d.ts +6 -0
- package/dist/browser/presets/gitlab.js +16 -0
- package/dist/browser/presets/groq.d.ts +7 -0
- package/dist/browser/presets/groq.js +22 -0
- package/dist/browser/presets/hubspot.d.ts +9 -0
- package/dist/browser/presets/hubspot.js +28 -0
- package/dist/browser/presets/huggingface.d.ts +7 -0
- package/dist/browser/presets/huggingface.js +23 -0
- package/dist/browser/presets/index.d.ts +47 -0
- package/dist/browser/presets/index.js +47 -0
- package/dist/browser/presets/ios.d.ts +2 -0
- package/dist/browser/presets/ios.js +13 -0
- package/dist/browser/presets/linear.d.ts +5 -0
- package/dist/browser/presets/linear.js +16 -0
- package/dist/browser/presets/mailgun.d.ts +7 -0
- package/dist/browser/presets/mailgun.js +20 -0
- package/dist/browser/presets/meta.d.ts +10 -0
- package/dist/browser/presets/meta.js +33 -0
- package/dist/browser/presets/mistral.d.ts +7 -0
- package/dist/browser/presets/mistral.js +22 -0
- package/dist/browser/presets/notion.d.ts +6 -0
- package/dist/browser/presets/notion.js +17 -0
- package/dist/browser/presets/openai.d.ts +9 -0
- package/dist/browser/presets/openai.js +30 -0
- package/dist/browser/presets/oracle.d.ts +19 -0
- package/dist/browser/presets/oracle.js +117 -0
- package/dist/browser/presets/perplexity.d.ts +7 -0
- package/dist/browser/presets/perplexity.js +22 -0
- package/dist/browser/presets/pinecone.d.ts +8 -0
- package/dist/browser/presets/pinecone.js +42 -0
- package/dist/browser/presets/registry.d.ts +23 -0
- package/dist/browser/presets/registry.js +519 -0
- package/dist/browser/presets/replicate.d.ts +7 -0
- package/dist/browser/presets/replicate.js +23 -0
- package/dist/browser/presets/sendgrid.d.ts +6 -0
- package/dist/browser/presets/sendgrid.js +20 -0
- package/dist/browser/presets/sentry.d.ts +11 -0
- package/dist/browser/presets/sentry.js +48 -0
- package/dist/browser/presets/sinch.d.ts +9 -0
- package/dist/browser/presets/sinch.js +39 -0
- package/dist/browser/presets/slack.d.ts +5 -0
- package/dist/browser/presets/slack.js +16 -0
- package/dist/browser/presets/square.d.ts +10 -0
- package/dist/browser/presets/square.js +33 -0
- package/dist/browser/presets/stripe.d.ts +7 -0
- package/dist/browser/presets/stripe.js +23 -0
- package/dist/browser/presets/supabase.d.ts +6 -0
- package/dist/browser/presets/supabase.js +18 -0
- package/dist/browser/presets/tiktok.d.ts +10 -0
- package/dist/browser/presets/tiktok.js +38 -0
- package/dist/browser/presets/together.d.ts +7 -0
- package/dist/browser/presets/together.js +22 -0
- package/dist/browser/presets/twilio.d.ts +6 -0
- package/dist/browser/presets/twilio.js +17 -0
- package/dist/browser/presets/vercel.d.ts +6 -0
- package/dist/browser/presets/vercel.js +23 -0
- package/dist/browser/presets/vultr.d.ts +5 -0
- package/dist/browser/presets/vultr.js +16 -0
- package/dist/browser/presets/xai.d.ts +8 -0
- package/dist/browser/presets/xai.js +23 -0
- package/dist/browser/presets/youtube.d.ts +5 -0
- package/dist/browser/presets/youtube.js +20 -0
- package/dist/browser/recker.d.ts +8 -1
- package/dist/browser/recker.js +8 -2
- package/dist/browser/scrape/document.d.ts +5 -4
- package/dist/browser/scrape/document.js +89 -76
- package/dist/browser/scrape/element.d.ts +10 -8
- package/dist/browser/scrape/element.js +295 -81
- package/dist/browser/scrape/extractors.d.ts +11 -11
- package/dist/browser/scrape/extractors.js +145 -113
- package/dist/browser/scrape/parser/back.d.ts +1 -0
- package/dist/browser/scrape/parser/back.js +3 -0
- package/dist/browser/scrape/parser/index.d.ts +20 -0
- package/dist/browser/scrape/parser/index.js +19 -0
- package/dist/browser/scrape/parser/matcher.d.ts +30 -0
- package/dist/browser/scrape/parser/matcher.js +99 -0
- package/dist/browser/scrape/parser/nodes/comment.d.ts +12 -0
- package/dist/browser/scrape/parser/nodes/comment.js +21 -0
- package/dist/browser/scrape/parser/nodes/html.d.ts +110 -0
- package/dist/browser/scrape/parser/nodes/html.js +978 -0
- package/dist/browser/scrape/parser/nodes/node.d.ts +18 -0
- package/dist/browser/scrape/parser/nodes/node.js +31 -0
- package/dist/browser/scrape/parser/nodes/text.d.ts +14 -0
- package/dist/browser/scrape/parser/nodes/text.js +30 -0
- package/dist/browser/scrape/parser/nodes/type.d.ts +6 -0
- package/dist/browser/scrape/parser/nodes/type.js +7 -0
- package/dist/browser/scrape/parser/parse.d.ts +1 -0
- package/dist/browser/scrape/parser/parse.js +1 -0
- package/dist/browser/scrape/parser/valid.d.ts +2 -0
- package/dist/browser/scrape/parser/valid.js +5 -0
- package/dist/browser/scrape/parser/void-tag.d.ts +7 -0
- package/dist/browser/scrape/parser/void-tag.js +43 -0
- package/dist/browser/scrape/types.d.ts +7 -0
- package/dist/browser/seo/analyzer.d.ts +59 -0
- package/dist/browser/seo/analyzer.js +1399 -0
- package/dist/browser/seo/keywords.d.ts +16 -0
- package/dist/browser/seo/keywords.js +55 -0
- package/dist/browser/seo/rules/accessibility.d.ts +2 -0
- package/dist/browser/seo/rules/accessibility.js +722 -0
- package/dist/browser/seo/rules/ai-search.d.ts +2 -0
- package/dist/browser/seo/rules/ai-search.js +436 -0
- package/dist/browser/seo/rules/analytics.d.ts +2 -0
- package/dist/browser/seo/rules/analytics.js +306 -0
- package/dist/browser/seo/rules/best-practices.d.ts +2 -0
- package/dist/browser/seo/rules/best-practices.js +195 -0
- package/dist/browser/seo/rules/canonical.d.ts +12 -0
- package/dist/browser/seo/rules/canonical.js +258 -0
- package/dist/browser/seo/rules/content.d.ts +2 -0
- package/dist/browser/seo/rules/content.js +424 -0
- package/dist/browser/seo/rules/crawl.d.ts +2 -0
- package/dist/browser/seo/rules/crawl.js +435 -0
- package/dist/browser/seo/rules/cwv.d.ts +2 -0
- package/dist/browser/seo/rules/cwv.js +248 -0
- package/dist/browser/seo/rules/ecommerce.d.ts +2 -0
- package/dist/browser/seo/rules/ecommerce.js +283 -0
- package/dist/browser/seo/rules/i18n.d.ts +2 -0
- package/dist/browser/seo/rules/i18n.js +277 -0
- package/dist/browser/seo/rules/images.d.ts +2 -0
- package/dist/browser/seo/rules/images.js +235 -0
- package/dist/browser/seo/rules/index.d.ts +52 -0
- package/dist/browser/seo/rules/index.js +159 -0
- package/dist/browser/seo/rules/internal-linking.d.ts +2 -0
- package/dist/browser/seo/rules/internal-linking.js +394 -0
- package/dist/browser/seo/rules/links.d.ts +2 -0
- package/dist/browser/seo/rules/links.js +490 -0
- package/dist/browser/seo/rules/local.d.ts +2 -0
- package/dist/browser/seo/rules/local.js +289 -0
- package/dist/browser/seo/rules/meta.d.ts +2 -0
- package/dist/browser/seo/rules/meta.js +646 -0
- package/dist/browser/seo/rules/mobile.d.ts +2 -0
- package/dist/browser/seo/rules/mobile.js +161 -0
- package/dist/browser/seo/rules/performance.d.ts +2 -0
- package/dist/browser/seo/rules/performance.js +625 -0
- package/dist/browser/seo/rules/pwa.d.ts +2 -0
- package/dist/browser/seo/rules/pwa.js +299 -0
- package/dist/browser/seo/rules/readability.d.ts +2 -0
- package/dist/browser/seo/rules/readability.js +264 -0
- package/dist/browser/seo/rules/redirects.d.ts +16 -0
- package/dist/browser/seo/rules/redirects.js +199 -0
- package/dist/browser/seo/rules/resources.d.ts +2 -0
- package/dist/browser/seo/rules/resources.js +390 -0
- package/dist/browser/seo/rules/schema.d.ts +2 -0
- package/dist/browser/seo/rules/schema.js +379 -0
- package/dist/browser/seo/rules/security.d.ts +2 -0
- package/dist/browser/seo/rules/security.js +877 -0
- package/dist/browser/seo/rules/social.d.ts +2 -0
- package/dist/browser/seo/rules/social.js +603 -0
- package/dist/browser/seo/rules/structural.d.ts +2 -0
- package/dist/browser/seo/rules/structural.js +179 -0
- package/dist/browser/seo/rules/technical-advanced.d.ts +10 -0
- package/dist/browser/seo/rules/technical-advanced.js +288 -0
- package/dist/browser/seo/rules/technical.d.ts +2 -0
- package/dist/browser/seo/rules/technical.js +480 -0
- package/dist/browser/seo/rules/thresholds.d.ts +196 -0
- package/dist/browser/seo/rules/thresholds.js +118 -0
- package/dist/browser/seo/rules/types.d.ts +498 -0
- package/dist/browser/seo/rules/types.js +11 -0
- package/dist/browser/seo/types.d.ts +211 -0
- package/dist/browser/seo/types.js +1 -0
- package/dist/browser/transport/curl.d.ts +4 -0
- package/dist/browser/transport/curl.js +101 -0
- package/dist/browser/transport/undici.js +1 -2
- package/dist/browser/transport/worker.d.ts +18 -0
- package/dist/browser/transport/worker.js +278 -0
- package/dist/browser/types/index.d.ts +4 -1
- package/dist/browser/utils/binary-manager.d.ts +4 -0
- package/dist/browser/utils/binary-manager.js +72 -0
- package/dist/browser/utils/user-agent.js +2 -13
- package/dist/cache/indexed-db.d.ts +10 -0
- package/dist/cache/indexed-db.js +88 -0
- package/dist/cache/service-worker-cache.d.ts +18 -0
- package/dist/cache/service-worker-cache.js +103 -0
- package/dist/cli/commands/ai.d.ts +2 -0
- package/dist/cli/commands/ai.js +162 -0
- package/dist/cli/commands/bench.d.ts +2 -0
- package/dist/cli/commands/bench.js +51 -0
- package/dist/cli/commands/dns.d.ts +2 -0
- package/dist/cli/commands/dns.js +295 -0
- package/dist/cli/commands/har.d.ts +2 -0
- package/dist/cli/commands/har.js +171 -0
- package/dist/cli/commands/hls.d.ts +2 -0
- package/dist/cli/commands/hls.js +192 -0
- package/dist/cli/commands/network.d.ts +2 -0
- package/dist/cli/commands/network.js +288 -0
- package/dist/cli/commands/protocols.d.ts +2 -0
- package/dist/cli/commands/protocols.js +344 -0
- package/dist/cli/commands/scrape.d.ts +2 -0
- package/dist/cli/commands/scrape.js +176 -0
- package/dist/cli/commands/security.d.ts +2 -0
- package/dist/cli/commands/security.js +57 -0
- package/dist/cli/commands/seo.d.ts +2 -0
- package/dist/cli/commands/seo.js +125 -0
- package/dist/cli/commands/serve.d.ts +2 -0
- package/dist/cli/commands/serve.js +531 -0
- package/dist/cli/commands/spider.d.ts +3 -0
- package/dist/cli/commands/spider.js +456 -0
- package/dist/cli/commands/utils.d.ts +2 -0
- package/dist/cli/commands/utils.js +176 -0
- package/dist/cli/commands/vector.d.ts +2 -0
- package/dist/cli/commands/vector.js +158 -0
- package/dist/cli/handler.d.ts +2 -2
- package/dist/cli/handler.js +6 -6
- package/dist/cli/helpers.d.ts +7 -0
- package/dist/cli/helpers.js +128 -0
- package/dist/cli/index.js +96 -5228
- package/dist/cli/parser/help.d.ts +2 -0
- package/dist/cli/parser/help.js +52 -0
- package/dist/cli/parser/index.d.ts +3 -0
- package/dist/cli/parser/index.js +3 -0
- package/dist/cli/parser/parser.d.ts +4 -0
- package/dist/cli/parser/parser.js +146 -0
- package/dist/cli/parser/types.d.ts +41 -0
- package/dist/cli/parser/types.js +1 -0
- package/dist/cli/presets.d.ts +1 -1
- package/dist/cli/presets.js +1 -1
- package/dist/cli/router.d.ts +36 -0
- package/dist/cli/router.js +195 -0
- package/dist/cli/tui/ai-chat.js +1 -1
- package/dist/cli/tui/commands/context.d.ts +9 -0
- package/dist/cli/tui/commands/context.js +1 -0
- package/dist/cli/tui/commands/dns.d.ts +10 -0
- package/dist/cli/tui/commands/dns.js +461 -0
- package/dist/cli/tui/commands/hls.d.ts +2 -0
- package/dist/cli/tui/commands/hls.js +162 -0
- package/dist/cli/tui/commands/ip.d.ts +2 -0
- package/dist/cli/tui/commands/ip.js +45 -0
- package/dist/cli/tui/commands/network.d.ts +3 -0
- package/dist/cli/tui/commands/network.js +81 -0
- package/dist/cli/tui/commands/protocols.d.ts +6 -0
- package/dist/cli/tui/commands/protocols.js +531 -0
- package/dist/cli/tui/commands/security.d.ts +2 -0
- package/dist/cli/tui/commands/security.js +48 -0
- package/dist/cli/tui/commands/seo.d.ts +2 -0
- package/dist/cli/tui/commands/seo.js +74 -0
- package/dist/cli/tui/context.d.ts +12 -0
- package/dist/cli/tui/context.js +1 -0
- package/dist/cli/tui/shell.d.ts +11 -20
- package/dist/cli/tui/shell.js +216 -1873
- package/dist/constants/user-agents.d.ts +7 -0
- package/dist/constants/user-agents.js +7 -0
- package/dist/core/client.d.ts +2 -0
- package/dist/core/client.js +19 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/mcp/cli.js +2 -3
- package/dist/mcp/data/embeddings.json +1 -0
- package/dist/mcp/tools/network.js +298 -158
- package/dist/plugins/har-player.d.ts +23 -0
- package/dist/plugins/har-player.js +49 -0
- package/dist/plugins/har-recorder.d.ts +37 -3
- package/dist/plugins/har-recorder.js +116 -63
- package/dist/plugins/network-simulation.d.ts +7 -0
- package/dist/plugins/network-simulation.js +13 -0
- package/dist/presets/android.d.ts +2 -0
- package/dist/presets/android.js +16 -0
- package/dist/presets/chaturbate.d.ts +2 -0
- package/dist/presets/chaturbate.js +17 -0
- package/dist/presets/elevenlabs.d.ts +6 -0
- package/dist/presets/elevenlabs.js +20 -0
- package/dist/presets/enhancers.d.ts +20 -0
- package/dist/presets/enhancers.js +85 -0
- package/dist/presets/hubspot.d.ts +9 -0
- package/dist/presets/hubspot.js +28 -0
- package/dist/presets/index.d.ts +10 -0
- package/dist/presets/index.js +10 -0
- package/dist/presets/ios.d.ts +2 -0
- package/dist/presets/ios.js +13 -0
- package/dist/presets/pinecone.d.ts +8 -0
- package/dist/presets/pinecone.js +42 -0
- package/dist/presets/registry.js +60 -0
- package/dist/presets/sendgrid.d.ts +6 -0
- package/dist/presets/sendgrid.js +20 -0
- package/dist/presets/sentry.d.ts +11 -0
- package/dist/presets/sentry.js +48 -0
- package/dist/presets/square.d.ts +10 -0
- package/dist/presets/square.js +33 -0
- package/dist/recker.d.ts +3 -0
- package/dist/recker.js +4 -0
- package/dist/scrape/document.d.ts +5 -4
- package/dist/scrape/document.js +89 -76
- package/dist/scrape/element.d.ts +10 -8
- package/dist/scrape/element.js +295 -81
- package/dist/scrape/extractors.d.ts +11 -11
- package/dist/scrape/extractors.js +145 -113
- package/dist/scrape/index.d.ts +2 -0
- package/dist/scrape/index.js +1 -0
- package/dist/scrape/parser/back.d.ts +1 -0
- package/dist/scrape/parser/back.js +3 -0
- package/dist/scrape/parser/index.d.ts +20 -0
- package/dist/scrape/parser/index.js +19 -0
- package/dist/scrape/parser/matcher.d.ts +30 -0
- package/dist/scrape/parser/matcher.js +99 -0
- package/dist/scrape/parser/nodes/comment.d.ts +12 -0
- package/dist/scrape/parser/nodes/comment.js +21 -0
- package/dist/scrape/parser/nodes/html.d.ts +110 -0
- package/dist/scrape/parser/nodes/html.js +978 -0
- package/dist/scrape/parser/nodes/node.d.ts +18 -0
- package/dist/scrape/parser/nodes/node.js +31 -0
- package/dist/scrape/parser/nodes/text.d.ts +14 -0
- package/dist/scrape/parser/nodes/text.js +30 -0
- package/dist/scrape/parser/nodes/type.d.ts +6 -0
- package/dist/scrape/parser/nodes/type.js +7 -0
- package/dist/scrape/parser/parse.d.ts +1 -0
- package/dist/scrape/parser/parse.js +1 -0
- package/dist/scrape/parser/valid.d.ts +2 -0
- package/dist/scrape/parser/valid.js +5 -0
- package/dist/scrape/parser/void-tag.d.ts +7 -0
- package/dist/scrape/parser/void-tag.js +43 -0
- package/dist/scrape/spider.d.ts +19 -0
- package/dist/scrape/spider.js +28 -3
- package/dist/scrape/types.d.ts +7 -0
- package/dist/seo/analyzer.d.ts +15 -5
- package/dist/seo/analyzer.js +636 -175
- package/dist/seo/formatter.d.ts +16 -0
- package/dist/seo/formatter.js +228 -0
- package/dist/seo/index.d.ts +2 -0
- package/dist/seo/index.js +1 -0
- package/dist/seo/keywords.d.ts +16 -0
- package/dist/seo/keywords.js +55 -0
- package/dist/seo/rules/accessibility.js +85 -57
- package/dist/seo/rules/ai-search.js +44 -31
- package/dist/seo/rules/analytics.d.ts +2 -0
- package/dist/seo/rules/analytics.js +306 -0
- package/dist/seo/rules/best-practices.js +21 -14
- package/dist/seo/rules/canonical.js +31 -22
- package/dist/seo/rules/content.js +207 -19
- package/dist/seo/rules/crawl.js +55 -40
- package/dist/seo/rules/cwv.js +21 -15
- package/dist/seo/rules/ecommerce.js +51 -20
- package/dist/seo/rules/i18n.js +61 -33
- package/dist/seo/rules/images.js +87 -28
- package/dist/seo/rules/index.js +2 -0
- package/dist/seo/rules/internal-linking.js +58 -39
- package/dist/seo/rules/links.js +70 -51
- package/dist/seo/rules/local.js +49 -25
- package/dist/seo/rules/meta.js +161 -62
- package/dist/seo/rules/mobile.js +112 -2
- package/dist/seo/rules/performance.js +309 -54
- package/dist/seo/rules/pwa.js +36 -39
- package/dist/seo/rules/readability.js +31 -22
- package/dist/seo/rules/redirects.js +21 -15
- package/dist/seo/rules/resources.js +59 -42
- package/dist/seo/rules/schema.js +333 -8
- package/dist/seo/rules/security.js +142 -80
- package/dist/seo/rules/social.js +277 -47
- package/dist/seo/rules/structural.js +40 -16
- package/dist/seo/rules/technical-advanced.js +27 -22
- package/dist/seo/rules/technical.js +243 -42
- package/dist/seo/rules/types.d.ts +53 -1
- package/dist/seo/seo-spider.d.ts +22 -0
- package/dist/seo/seo-spider.js +77 -13
- package/dist/seo/types.d.ts +8 -1
- package/dist/seo/validators/llms-txt.js +19 -0
- package/dist/seo/validators/rss.d.ts +11 -0
- package/dist/seo/validators/rss.js +93 -0
- package/dist/seo/validators/sitemap.js +36 -26
- package/dist/transport/curl.d.ts +4 -0
- package/dist/transport/curl.js +101 -0
- package/dist/transport/udp.js +0 -1
- package/dist/transport/undici.js +1 -2
- package/dist/transport/worker.d.ts +18 -0
- package/dist/transport/worker.js +278 -0
- package/dist/types/index.d.ts +4 -1
- package/dist/utils/binary-manager.d.ts +4 -0
- package/dist/utils/binary-manager.js +72 -0
- package/dist/utils/optional-require.d.ts +7 -8
- package/dist/utils/optional-require.js +2 -21
- package/dist/utils/upload.d.ts +6 -0
- package/dist/utils/upload.js +11 -0
- package/dist/utils/user-agent.js +2 -13
- package/dist/version.js +1 -1
- package/package.json +14 -5
- package/dist/browser/utils/optional-require.d.ts +0 -19
- package/dist/browser/utils/optional-require.js +0 -105
package/dist/seo/rules/images.js
CHANGED
|
@@ -8,13 +8,24 @@ export const imageRules = [
|
|
|
8
8
|
severity: 'error',
|
|
9
9
|
description: 'All images must have alt text',
|
|
10
10
|
check: (ctx) => {
|
|
11
|
-
if (ctx.totalImages === undefined || ctx.totalImages === 0)
|
|
12
|
-
return
|
|
11
|
+
if (ctx.totalImages === undefined || ctx.totalImages === 0) {
|
|
12
|
+
return createResult({ id: 'images-alt-text', name: 'Image Alt Text', category: 'images', severity: 'error' }, 'info', 'Not applicable (no images detected)', { recommendation: 'This rule ensures all images have descriptive alt text for accessibility and SEO' });
|
|
13
|
+
}
|
|
13
14
|
const withoutAlt = ctx.imagesWithoutAlt ?? 0;
|
|
14
15
|
if (withoutAlt > 0) {
|
|
15
16
|
const percentage = Math.round((withoutAlt / ctx.totalImages) * 100);
|
|
16
17
|
const severity = withoutAlt > ctx.totalImages / 2 ? 'fail' : 'warn';
|
|
17
|
-
return createResult({ id: 'images-alt-text', name: 'Image Alt Text', category: 'images', severity: 'error' }, severity, `${withoutAlt} of ${ctx.totalImages} images missing alt text (${percentage}%)`, {
|
|
18
|
+
return createResult({ id: 'images-alt-text', name: 'Image Alt Text', category: 'images', severity: 'error' }, severity, `${withoutAlt} of ${ctx.totalImages} images missing alt text (${percentage}%)`, {
|
|
19
|
+
value: withoutAlt,
|
|
20
|
+
recommendation: 'Add descriptive alt text to all images. Describe what the image shows and its relevance to the content.',
|
|
21
|
+
evidence: {
|
|
22
|
+
found: `${withoutAlt} images without alt attribute`,
|
|
23
|
+
expected: 'All images should have meaningful alt text',
|
|
24
|
+
impact: 'Missing alt text hurts accessibility (screen readers) and prevents images from appearing in Google Image Search. Alt text is also used as anchor text when images are linked.',
|
|
25
|
+
example: '<img src="product.jpg" alt="Red leather wallet with zipper closure - front view">',
|
|
26
|
+
learnMore: 'https://developers.google.com/search/docs/appearance/google-images#use-descriptive-alt-text'
|
|
27
|
+
}
|
|
28
|
+
});
|
|
18
29
|
}
|
|
19
30
|
return createResult({ id: 'images-alt-text', name: 'Image Alt Text', category: 'images', severity: 'error' }, 'pass', 'All images have alt text');
|
|
20
31
|
},
|
|
@@ -26,11 +37,23 @@ export const imageRules = [
|
|
|
26
37
|
severity: 'warning',
|
|
27
38
|
description: 'Images should have width and height attributes to prevent CLS',
|
|
28
39
|
check: (ctx) => {
|
|
29
|
-
if (ctx.totalImages === undefined || ctx.totalImages === 0)
|
|
30
|
-
return
|
|
40
|
+
if (ctx.totalImages === undefined || ctx.totalImages === 0) {
|
|
41
|
+
return createResult({ id: 'images-dimensions', name: 'Image Dimensions', category: 'images', severity: 'warning' }, 'info', 'Not applicable (no images detected)', { recommendation: 'This rule checks that images have width and height attributes to prevent layout shifts' });
|
|
42
|
+
}
|
|
31
43
|
const missing = ctx.imagesMissingDimensions ?? 0;
|
|
32
44
|
if (missing > 0) {
|
|
33
|
-
|
|
45
|
+
const percentage = Math.round((missing / ctx.totalImages) * 100);
|
|
46
|
+
return createResult({ id: 'images-dimensions', name: 'Image Dimensions', category: 'images', severity: 'warning' }, 'warn', `${missing} of ${ctx.totalImages} images missing width/height (${percentage}%)`, {
|
|
47
|
+
value: missing,
|
|
48
|
+
recommendation: 'Add explicit width and height attributes to all images to reserve space and prevent layout shifts.',
|
|
49
|
+
evidence: {
|
|
50
|
+
found: `${missing} images without dimensions`,
|
|
51
|
+
expected: 'All images should have width and height attributes',
|
|
52
|
+
impact: 'Images without dimensions cause Cumulative Layout Shift (CLS), which negatively affects Core Web Vitals and user experience.',
|
|
53
|
+
example: '<img src="photo.jpg" width="800" height="600" alt="Description">',
|
|
54
|
+
learnMore: 'https://web.dev/optimize-cls/#images-without-dimensions'
|
|
55
|
+
}
|
|
56
|
+
});
|
|
34
57
|
}
|
|
35
58
|
return createResult({ id: 'images-dimensions', name: 'Image Dimensions', category: 'images', severity: 'warning' }, 'pass', 'All images have dimensions defined');
|
|
36
59
|
},
|
|
@@ -42,11 +65,21 @@ export const imageRules = [
|
|
|
42
65
|
severity: 'info',
|
|
43
66
|
description: 'Below-the-fold images should use lazy loading',
|
|
44
67
|
check: (ctx) => {
|
|
45
|
-
if (ctx.totalImages === undefined || ctx.totalImages <= 3)
|
|
46
|
-
return
|
|
68
|
+
if (ctx.totalImages === undefined || ctx.totalImages <= 3) {
|
|
69
|
+
return createResult({ id: 'images-lazy-loading', name: 'Lazy Loading', category: 'images', severity: 'info' }, 'info', 'Not applicable (too few images to require lazy loading)', { recommendation: 'This rule checks for lazy loading on pages with multiple images' });
|
|
70
|
+
}
|
|
47
71
|
const lazy = ctx.imagesWithLazyLoad ?? 0;
|
|
48
72
|
if (lazy === 0) {
|
|
49
|
-
return createResult({ id: 'images-lazy-loading', name: 'Lazy Loading', category: 'images', severity: 'info' }, 'info', 'No images use lazy loading', {
|
|
73
|
+
return createResult({ id: 'images-lazy-loading', name: 'Lazy Loading', category: 'images', severity: 'info' }, 'info', 'No images use lazy loading', {
|
|
74
|
+
recommendation: 'Add loading="lazy" to below-the-fold images to defer loading until they are near the viewport.',
|
|
75
|
+
evidence: {
|
|
76
|
+
found: 'No images with loading="lazy"',
|
|
77
|
+
expected: 'Below-the-fold images should use lazy loading',
|
|
78
|
+
impact: 'Lazy loading reduces initial page load time, saves bandwidth, and improves Core Web Vitals (LCP).',
|
|
79
|
+
example: '<img src="photo.jpg" loading="lazy" alt="Description">',
|
|
80
|
+
learnMore: 'https://web.dev/browser-level-image-lazy-loading/'
|
|
81
|
+
}
|
|
82
|
+
});
|
|
50
83
|
}
|
|
51
84
|
return createResult({ id: 'images-lazy-loading', name: 'Lazy Loading', category: 'images', severity: 'info' }, 'pass', `${lazy} images use lazy loading`);
|
|
52
85
|
},
|
|
@@ -58,11 +91,22 @@ export const imageRules = [
|
|
|
58
91
|
severity: 'info',
|
|
59
92
|
description: 'Images should use modern formats like WebP or AVIF',
|
|
60
93
|
check: (ctx) => {
|
|
61
|
-
if (ctx.totalImages === undefined || ctx.totalImages === 0)
|
|
62
|
-
return
|
|
94
|
+
if (ctx.totalImages === undefined || ctx.totalImages === 0) {
|
|
95
|
+
return createResult({ id: 'images-format-modern', name: 'Modern Image Formats', category: 'images', severity: 'info' }, 'info', 'Not applicable (no images detected)', { recommendation: 'This rule checks for modern image formats like WebP and AVIF for better performance' });
|
|
96
|
+
}
|
|
63
97
|
const modern = ctx.imagesUsingModernFormats ?? 0;
|
|
64
98
|
if (ctx.totalImages > 0 && modern === 0) {
|
|
65
|
-
return createResult({ id: 'images-format-modern', name: 'Modern Image Formats', category: 'images', severity: 'info' }, 'info', 'No images using modern formats (WebP/AVIF)', {
|
|
99
|
+
return createResult({ id: 'images-format-modern', name: 'Modern Image Formats', category: 'images', severity: 'info' }, 'info', 'No images using modern formats (WebP/AVIF)', {
|
|
100
|
+
value: modern,
|
|
101
|
+
recommendation: 'Convert images to WebP or AVIF format for 25-50% smaller file sizes with same quality.',
|
|
102
|
+
evidence: {
|
|
103
|
+
found: 'Only traditional formats (JPEG, PNG, GIF)',
|
|
104
|
+
expected: 'At least some images in WebP or AVIF format',
|
|
105
|
+
impact: 'Modern formats significantly reduce page weight and improve load times. WebP has 94% browser support.',
|
|
106
|
+
example: '<picture>\n <source srcset="image.avif" type="image/avif">\n <source srcset="image.webp" type="image/webp">\n <img src="image.jpg" alt="Description">\n</picture>',
|
|
107
|
+
learnMore: 'https://web.dev/uses-webp-images/'
|
|
108
|
+
}
|
|
109
|
+
});
|
|
66
110
|
}
|
|
67
111
|
return createResult({ id: 'images-format-modern', name: 'Modern Image Formats', category: 'images', severity: 'info' }, 'pass', `${modern} images using modern formats`, { value: modern });
|
|
68
112
|
},
|
|
@@ -78,7 +122,7 @@ export const imageRules = [
|
|
|
78
122
|
if (emptyAlt > 0) {
|
|
79
123
|
return createResult({ id: 'images-empty-alt', name: 'Empty Alt Text', category: 'images', severity: 'info' }, 'info', `${emptyAlt} image(s) with empty alt="" (decorative)`, { value: emptyAlt, recommendation: 'Ensure these images are truly decorative' });
|
|
80
124
|
}
|
|
81
|
-
return
|
|
125
|
+
return createResult({ id: 'images-empty-alt', name: 'Empty Alt Text', category: 'images', severity: 'info' }, 'pass', 'No images with empty alt attributes');
|
|
82
126
|
},
|
|
83
127
|
},
|
|
84
128
|
{
|
|
@@ -88,8 +132,9 @@ export const imageRules = [
|
|
|
88
132
|
severity: 'warning',
|
|
89
133
|
description: 'Alt text should be descriptive (ideal 80-120, max 150 chars)',
|
|
90
134
|
check: (ctx) => {
|
|
91
|
-
if (!ctx.altTextLengths || ctx.altTextLengths.length === 0)
|
|
92
|
-
return
|
|
135
|
+
if (!ctx.altTextLengths || ctx.altTextLengths.length === 0) {
|
|
136
|
+
return createResult({ id: 'images-alt-length', name: 'Alt Text Length', category: 'images', severity: 'warning' }, 'info', 'Not applicable (no alt text data available)', { recommendation: 'This rule checks alt text length to ensure descriptions are meaningful' });
|
|
137
|
+
}
|
|
93
138
|
const { minLength, idealLength, maxLength } = SEO_THRESHOLDS.images.alt;
|
|
94
139
|
let shortAlts = 0;
|
|
95
140
|
let longAlts = 0;
|
|
@@ -111,7 +156,7 @@ export const imageRules = [
|
|
|
111
156
|
if (nonIdealAlts > 0) {
|
|
112
157
|
return createResult({ id: 'images-alt-length', name: 'Alt Text Length', category: 'images', severity: 'info' }, 'info', `${nonIdealAlts} alt text(s) are not in the ideal length range (${idealLength.min}-${idealLength.max} chars)`, { value: nonIdealAlts, recommendation: `Aim for alt texts between ${idealLength.min} and ${idealLength.max} characters for best results.` });
|
|
113
158
|
}
|
|
114
|
-
return
|
|
159
|
+
return createResult({ id: 'images-alt-length', name: 'Alt Text Length', category: 'images', severity: 'warning' }, 'pass', 'All alt text lengths are within ideal range');
|
|
115
160
|
},
|
|
116
161
|
},
|
|
117
162
|
{
|
|
@@ -121,15 +166,26 @@ export const imageRules = [
|
|
|
121
166
|
severity: 'info',
|
|
122
167
|
description: 'Image filenames should be descriptive and use keywords, not generic names.',
|
|
123
168
|
check: (ctx) => {
|
|
124
|
-
if (!ctx.imageFilenames || ctx.imageFilenames.length === 0)
|
|
125
|
-
return
|
|
169
|
+
if (!ctx.imageFilenames || ctx.imageFilenames.length === 0) {
|
|
170
|
+
return createResult({ id: 'images-clean-filenames', name: 'Image Filenames', category: 'images', severity: 'info' }, 'info', 'Not applicable (no image filename data available)', { recommendation: 'This rule checks for descriptive image filenames with keywords' });
|
|
171
|
+
}
|
|
126
172
|
const genericFilenames = ctx.imageFilenames.filter(name => /^(img|image|photo|pic)\d*\.(jpg|jpeg|png|webp|avif|gif)$/i.test(name) ||
|
|
127
173
|
/^screenshot_\d*\.(jpg|jpeg|png)$/i.test(name) ||
|
|
128
174
|
/^untitled-\d*\.(jpg|jpeg|png)$/i.test(name));
|
|
129
175
|
if (genericFilenames.length > 0) {
|
|
130
|
-
return createResult({ id: 'images-clean-filenames', name: 'Image Filenames', category: 'images', severity: 'warning' }, 'warn', `${genericFilenames.length} image(s) have generic filenames
|
|
176
|
+
return createResult({ id: 'images-clean-filenames', name: 'Image Filenames', category: 'images', severity: 'warning' }, 'warn', `${genericFilenames.length} image(s) have generic filenames`, {
|
|
177
|
+
value: genericFilenames.length,
|
|
178
|
+
recommendation: 'Rename image files to be descriptive and include relevant keywords.',
|
|
179
|
+
evidence: {
|
|
180
|
+
found: genericFilenames.slice(0, 5),
|
|
181
|
+
expected: 'Descriptive filenames with keywords (e.g., red-leather-wallet-front.jpg)',
|
|
182
|
+
impact: 'Image filenames are used by search engines to understand image content and can appear in Google Image Search.',
|
|
183
|
+
example: 'Instead of "IMG_1234.jpg", use "blue-running-shoes-nike-air.jpg"',
|
|
184
|
+
learnMore: 'https://developers.google.com/search/docs/appearance/google-images#descriptive-titles-captions-filenames'
|
|
185
|
+
}
|
|
186
|
+
});
|
|
131
187
|
}
|
|
132
|
-
return
|
|
188
|
+
return createResult({ id: 'images-clean-filenames', name: 'Image Filenames', category: 'images', severity: 'info' }, 'pass', 'All image filenames are descriptive');
|
|
133
189
|
},
|
|
134
190
|
},
|
|
135
191
|
{
|
|
@@ -139,15 +195,17 @@ export const imageRules = [
|
|
|
139
195
|
severity: 'info',
|
|
140
196
|
description: 'Use decoding="async" for non-critical images to improve rendering performance.',
|
|
141
197
|
check: (ctx) => {
|
|
142
|
-
if (ctx.totalImages === undefined || ctx.totalImages === 0)
|
|
143
|
-
return
|
|
144
|
-
|
|
145
|
-
|
|
198
|
+
if (ctx.totalImages === undefined || ctx.totalImages === 0) {
|
|
199
|
+
return createResult({ id: 'images-decoding-async', name: 'Image Decoding Async', category: 'images', severity: 'info' }, 'info', 'Not applicable (no images detected)', { recommendation: 'This rule checks for async decoding on images for better rendering performance' });
|
|
200
|
+
}
|
|
201
|
+
if (ctx.imagesWithAsyncDecoding === undefined) {
|
|
202
|
+
return createResult({ id: 'images-decoding-async', name: 'Image Decoding Async', category: 'images', severity: 'info' }, 'info', 'Not applicable (async decoding data unavailable)', { recommendation: 'This rule checks for async decoding on images for better rendering performance' });
|
|
203
|
+
}
|
|
146
204
|
const nonAsync = ctx.totalImages - ctx.imagesWithAsyncDecoding;
|
|
147
205
|
if (nonAsync > 0 && ctx.totalImages > 3) {
|
|
148
206
|
return createResult({ id: 'images-decoding-async', name: 'Image Decoding Async', category: 'images', severity: 'info' }, 'info', `${nonAsync} image(s) do not use decoding="async"`, { value: nonAsync, recommendation: 'Consider adding decoding="async" to non-critical images for performance benefits.' });
|
|
149
207
|
}
|
|
150
|
-
return
|
|
208
|
+
return createResult({ id: 'images-decoding-async', name: 'Image Decoding Async', category: 'images', severity: 'info' }, 'pass', 'All images use async decoding or too few images to require it');
|
|
151
209
|
},
|
|
152
210
|
},
|
|
153
211
|
{
|
|
@@ -157,8 +215,9 @@ export const imageRules = [
|
|
|
157
215
|
severity: 'warning',
|
|
158
216
|
description: 'External images should be accessible',
|
|
159
217
|
check: (ctx) => {
|
|
160
|
-
if (ctx.brokenExternalImages === undefined)
|
|
161
|
-
return
|
|
218
|
+
if (ctx.brokenExternalImages === undefined) {
|
|
219
|
+
return createResult({ id: 'broken-external-images', name: 'Broken External Images', category: 'images', severity: 'warning' }, 'info', 'Not applicable (broken external images data unavailable)', { recommendation: 'This rule checks for broken external image references' });
|
|
220
|
+
}
|
|
162
221
|
if (ctx.brokenExternalImages > 0) {
|
|
163
222
|
return createResult({ id: 'broken-external-images', name: 'Broken External Images', category: 'images', severity: 'warning' }, 'warn', `${ctx.brokenExternalImages} broken external images`, {
|
|
164
223
|
value: ctx.brokenExternalImages,
|
|
@@ -170,7 +229,7 @@ export const imageRules = [
|
|
|
170
229
|
}
|
|
171
230
|
});
|
|
172
231
|
}
|
|
173
|
-
return
|
|
232
|
+
return createResult({ id: 'broken-external-images', name: 'Broken External Images', category: 'images', severity: 'warning' }, 'pass', 'No broken external images detected');
|
|
174
233
|
},
|
|
175
234
|
},
|
|
176
235
|
];
|
package/dist/seo/rules/index.js
CHANGED
|
@@ -24,6 +24,7 @@ import { resourceRules } from './resources.js';
|
|
|
24
24
|
import { technicalAdvancedRules } from './technical-advanced.js';
|
|
25
25
|
import { redirectRules } from './redirects.js';
|
|
26
26
|
import { canonicalRules } from './canonical.js';
|
|
27
|
+
import { analyticsRules } from './analytics.js';
|
|
27
28
|
export * from './types.js';
|
|
28
29
|
export * from './thresholds.js';
|
|
29
30
|
export const ALL_SEO_RULES = [
|
|
@@ -53,6 +54,7 @@ export const ALL_SEO_RULES = [
|
|
|
53
54
|
...technicalAdvancedRules,
|
|
54
55
|
...redirectRules,
|
|
55
56
|
...canonicalRules,
|
|
57
|
+
...analyticsRules,
|
|
56
58
|
];
|
|
57
59
|
export const SCORING_WEIGHTS = {
|
|
58
60
|
severity: {
|
|
@@ -7,8 +7,9 @@ export const internalLinkingRules = [
|
|
|
7
7
|
severity: 'warning',
|
|
8
8
|
description: 'Pages should have a healthy number of internal links',
|
|
9
9
|
check: (ctx) => {
|
|
10
|
-
if (ctx.internalLinks === undefined)
|
|
11
|
-
return
|
|
10
|
+
if (ctx.internalLinks === undefined) {
|
|
11
|
+
return createResult({ id: 'linking-internal-count', name: 'Internal Link Count', category: 'links', severity: 'warning' }, 'info', 'Not applicable (internal links data unavailable)', { recommendation: 'This rule checks internal link count to ensure proper site navigation and link equity distribution' });
|
|
12
|
+
}
|
|
12
13
|
const count = ctx.internalLinks;
|
|
13
14
|
if (count === 0) {
|
|
14
15
|
return createResult({ id: 'linking-internal-count', name: 'Internal Link Count', category: 'links', severity: 'warning' }, 'warn', 'No internal links found', {
|
|
@@ -39,10 +40,12 @@ export const internalLinkingRules = [
|
|
|
39
40
|
severity: 'info',
|
|
40
41
|
description: 'Pages should have more internal than external links',
|
|
41
42
|
check: (ctx) => {
|
|
42
|
-
if (ctx.internalLinks === undefined || ctx.externalLinks === undefined)
|
|
43
|
-
return
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
if (ctx.internalLinks === undefined || ctx.externalLinks === undefined) {
|
|
44
|
+
return createResult({ id: 'linking-internal-ratio', name: 'Internal/External Link Ratio', category: 'links', severity: 'info' }, 'info', 'Not applicable (link data unavailable)', { recommendation: 'This rule checks the balance between internal and external links' });
|
|
45
|
+
}
|
|
46
|
+
if (ctx.totalLinks === undefined || ctx.totalLinks === 0) {
|
|
47
|
+
return createResult({ id: 'linking-internal-ratio', name: 'Internal/External Link Ratio', category: 'links', severity: 'info' }, 'info', 'Not applicable (no links detected)', { recommendation: 'This rule requires at least some links to analyze the internal/external ratio' });
|
|
48
|
+
}
|
|
46
49
|
const internal = ctx.internalLinks;
|
|
47
50
|
const external = ctx.externalLinks;
|
|
48
51
|
const ratio = internal / (external || 1);
|
|
@@ -66,11 +69,13 @@ export const internalLinkingRules = [
|
|
|
66
69
|
severity: 'info',
|
|
67
70
|
description: 'Internal links should use diverse, descriptive anchor text',
|
|
68
71
|
check: (ctx) => {
|
|
69
|
-
if (!ctx.allLinks || ctx.allLinks.length === 0)
|
|
70
|
-
return
|
|
72
|
+
if (!ctx.allLinks || ctx.allLinks.length === 0) {
|
|
73
|
+
return createResult({ id: 'linking-anchor-diversity', name: 'Anchor Text Diversity', category: 'links', severity: 'info' }, 'info', 'Not applicable (no links detected)', { recommendation: 'This rule checks anchor text diversity to prevent over-optimization' });
|
|
74
|
+
}
|
|
71
75
|
const internalLinks = ctx.allLinks.filter(l => l.type === 'internal');
|
|
72
|
-
if (internalLinks.length < 3)
|
|
73
|
-
return
|
|
76
|
+
if (internalLinks.length < 3) {
|
|
77
|
+
return createResult({ id: 'linking-anchor-diversity', name: 'Anchor Text Diversity', category: 'links', severity: 'info' }, 'info', 'Not applicable (insufficient internal links)', { recommendation: 'This rule requires at least 3 internal links to analyze anchor text diversity' });
|
|
78
|
+
}
|
|
74
79
|
const anchorCounts = {};
|
|
75
80
|
for (const link of internalLinks) {
|
|
76
81
|
const anchor = (link.text || '').toLowerCase().trim();
|
|
@@ -100,11 +105,13 @@ export const internalLinkingRules = [
|
|
|
100
105
|
severity: 'info',
|
|
101
106
|
description: 'Pages should link to deep content, not just homepage',
|
|
102
107
|
check: (ctx) => {
|
|
103
|
-
if (!ctx.allLinks || ctx.allLinks.length === 0)
|
|
104
|
-
return
|
|
108
|
+
if (!ctx.allLinks || ctx.allLinks.length === 0) {
|
|
109
|
+
return createResult({ id: 'linking-deep-links', name: 'Deep Linking', category: 'links', severity: 'info' }, 'info', 'Not applicable (no links detected)', { recommendation: 'This rule checks for deep linking patterns to ensure inner pages are properly linked' });
|
|
110
|
+
}
|
|
105
111
|
const internalLinks = ctx.allLinks.filter(l => l.type === 'internal');
|
|
106
|
-
if (internalLinks.length === 0)
|
|
107
|
-
return
|
|
112
|
+
if (internalLinks.length === 0) {
|
|
113
|
+
return createResult({ id: 'linking-deep-links', name: 'Deep Linking', category: 'links', severity: 'info' }, 'info', 'Not applicable (no internal links detected)', { recommendation: 'This rule requires internal links to analyze deep linking patterns' });
|
|
114
|
+
}
|
|
108
115
|
let rootLinks = 0;
|
|
109
116
|
let deepLinks = 0;
|
|
110
117
|
for (const link of internalLinks) {
|
|
@@ -142,10 +149,12 @@ export const internalLinkingRules = [
|
|
|
142
149
|
severity: 'info',
|
|
143
150
|
description: 'Check for proper navigation link structure',
|
|
144
151
|
check: (ctx) => {
|
|
145
|
-
if (!ctx.hasNav)
|
|
146
|
-
return
|
|
147
|
-
|
|
148
|
-
|
|
152
|
+
if (!ctx.hasNav) {
|
|
153
|
+
return createResult({ id: 'linking-nav-links', name: 'Navigation Links', category: 'links', severity: 'info' }, 'info', 'Not applicable (no navigation element detected)', { recommendation: 'This rule checks navigation link structure when a <nav> element is present' });
|
|
154
|
+
}
|
|
155
|
+
if (ctx.navLinkCount === undefined) {
|
|
156
|
+
return createResult({ id: 'linking-nav-links', name: 'Navigation Links', category: 'links', severity: 'info' }, 'info', 'Not applicable (navigation link count unavailable)', { recommendation: 'This rule checks the number of links in navigation elements' });
|
|
157
|
+
}
|
|
149
158
|
if (ctx.navLinkCount === 0) {
|
|
150
159
|
return createResult({ id: 'linking-nav-links', name: 'Navigation Links', category: 'links', severity: 'info' }, 'warn', 'Navigation element has no links', {
|
|
151
160
|
recommendation: 'Add links to navigation for user experience and SEO',
|
|
@@ -175,10 +184,12 @@ export const internalLinkingRules = [
|
|
|
175
184
|
severity: 'info',
|
|
176
185
|
description: 'Footer should contain important site-wide links',
|
|
177
186
|
check: (ctx) => {
|
|
178
|
-
if (!ctx.hasFooter)
|
|
179
|
-
return
|
|
180
|
-
|
|
181
|
-
|
|
187
|
+
if (!ctx.hasFooter) {
|
|
188
|
+
return createResult({ id: 'linking-footer-links', name: 'Footer Links', category: 'links', severity: 'info' }, 'info', 'Not applicable (no footer element detected)', { recommendation: 'This rule checks footer link structure when a <footer> element is present' });
|
|
189
|
+
}
|
|
190
|
+
if (ctx.footerLinkCount === undefined) {
|
|
191
|
+
return createResult({ id: 'linking-footer-links', name: 'Footer Links', category: 'links', severity: 'info' }, 'info', 'Not applicable (footer link count unavailable)', { recommendation: 'This rule checks the number of links in footer elements' });
|
|
192
|
+
}
|
|
182
193
|
if (ctx.footerLinkCount === 0) {
|
|
183
194
|
return createResult({ id: 'linking-footer-links', name: 'Footer Links', category: 'links', severity: 'info' }, 'info', 'Footer has no links', {
|
|
184
195
|
recommendation: 'Add important links to footer',
|
|
@@ -206,10 +217,12 @@ export const internalLinkingRules = [
|
|
|
206
217
|
severity: 'info',
|
|
207
218
|
description: 'Check for in-content contextual links',
|
|
208
219
|
check: (ctx) => {
|
|
209
|
-
if (ctx.contextualLinkCount === undefined)
|
|
210
|
-
return
|
|
211
|
-
|
|
212
|
-
|
|
220
|
+
if (ctx.contextualLinkCount === undefined) {
|
|
221
|
+
return createResult({ id: 'linking-contextual', name: 'Contextual Links', category: 'links', severity: 'info' }, 'info', 'Not applicable (contextual link count unavailable)', { recommendation: 'This rule checks for in-content contextual links that pass more link equity' });
|
|
222
|
+
}
|
|
223
|
+
if (!ctx.wordCount || ctx.wordCount < 300) {
|
|
224
|
+
return createResult({ id: 'linking-contextual', name: 'Contextual Links', category: 'links', severity: 'info' }, 'info', 'Not applicable (insufficient content for contextual link analysis)', { recommendation: 'This rule requires at least 300 words to analyze contextual link patterns' });
|
|
225
|
+
}
|
|
213
226
|
const count = ctx.contextualLinkCount;
|
|
214
227
|
if (count === 0) {
|
|
215
228
|
return createResult({ id: 'linking-contextual', name: 'Contextual Links', category: 'links', severity: 'info' }, 'info', 'No contextual links in content', {
|
|
@@ -240,8 +253,9 @@ export const internalLinkingRules = [
|
|
|
240
253
|
severity: 'warning',
|
|
241
254
|
description: 'Pages should be linked from other pages on the site',
|
|
242
255
|
check: (ctx) => {
|
|
243
|
-
if (ctx.incomingInternalLinks === undefined)
|
|
244
|
-
return
|
|
256
|
+
if (ctx.incomingInternalLinks === undefined) {
|
|
257
|
+
return createResult({ id: 'linking-orphan-page', name: 'Orphan Page Detection', category: 'links', severity: 'warning' }, 'info', 'Not applicable (incoming internal links data unavailable)', { recommendation: 'This rule checks if pages receive incoming internal links to prevent orphan pages' });
|
|
258
|
+
}
|
|
245
259
|
if (ctx.incomingInternalLinks === 0) {
|
|
246
260
|
return createResult({ id: 'linking-orphan-page', name: 'Orphan Page Detection', category: 'links', severity: 'warning' }, 'warn', 'Page may be an orphan (no incoming internal links)', {
|
|
247
261
|
recommendation: 'Link to this page from other pages on your site',
|
|
@@ -262,8 +276,9 @@ export const internalLinkingRules = [
|
|
|
262
276
|
severity: 'info',
|
|
263
277
|
description: 'Avoid excessive self-referencing links',
|
|
264
278
|
check: (ctx) => {
|
|
265
|
-
if (ctx.selfReferencingLinks === undefined)
|
|
266
|
-
return
|
|
279
|
+
if (ctx.selfReferencingLinks === undefined) {
|
|
280
|
+
return createResult({ id: 'linking-self-referencing', name: 'Self-Referencing Links', category: 'links', severity: 'info' }, 'info', 'Not applicable (self-referencing links data unavailable)', { recommendation: 'This rule checks for excessive self-referencing links that waste crawl budget' });
|
|
281
|
+
}
|
|
267
282
|
if (ctx.selfReferencingLinks > 3) {
|
|
268
283
|
return createResult({ id: 'linking-self-referencing', name: 'Self-Referencing Links', category: 'links', severity: 'info' }, 'info', `${ctx.selfReferencingLinks} self-referencing links`, {
|
|
269
284
|
recommendation: 'Reduce links that point to the current page',
|
|
@@ -274,7 +289,7 @@ export const internalLinkingRules = [
|
|
|
274
289
|
},
|
|
275
290
|
});
|
|
276
291
|
}
|
|
277
|
-
return
|
|
292
|
+
return createResult({ id: 'linking-self-referencing', name: 'Self-Referencing Links', category: 'links', severity: 'info' }, 'info', 'Not applicable (acceptable self-referencing link count)', { recommendation: 'Self-referencing links are acceptable at current level (0-3 links)' });
|
|
278
293
|
},
|
|
279
294
|
},
|
|
280
295
|
{
|
|
@@ -284,8 +299,9 @@ export const internalLinkingRules = [
|
|
|
284
299
|
severity: 'error',
|
|
285
300
|
description: 'Internal links should not be broken',
|
|
286
301
|
check: (ctx) => {
|
|
287
|
-
if (ctx.brokenInternalLinks === undefined)
|
|
288
|
-
return
|
|
302
|
+
if (ctx.brokenInternalLinks === undefined) {
|
|
303
|
+
return createResult({ id: 'linking-broken-internal', name: 'Broken Internal Links', category: 'links', severity: 'error' }, 'info', 'Not applicable (broken internal links data unavailable)', { recommendation: 'This rule checks for broken internal links that harm user experience and waste crawl budget' });
|
|
304
|
+
}
|
|
289
305
|
if (ctx.brokenInternalLinks.length > 0) {
|
|
290
306
|
return createResult({ id: 'linking-broken-internal', name: 'Broken Internal Links', category: 'links', severity: 'error' }, 'fail', `${ctx.brokenInternalLinks.length} broken internal link(s)`, {
|
|
291
307
|
recommendation: 'Fix or remove broken internal links',
|
|
@@ -306,8 +322,9 @@ export const internalLinkingRules = [
|
|
|
306
322
|
severity: 'warning',
|
|
307
323
|
description: 'Internal links should not go through redirect chains',
|
|
308
324
|
check: (ctx) => {
|
|
309
|
-
if (ctx.redirectChainLinks === undefined)
|
|
310
|
-
return
|
|
325
|
+
if (ctx.redirectChainLinks === undefined) {
|
|
326
|
+
return createResult({ id: 'linking-redirect-chains', name: 'Redirect Chains', category: 'links', severity: 'warning' }, 'info', 'Not applicable (redirect chain data unavailable)', { recommendation: 'This rule checks for redirect chains that slow crawling and lose link equity' });
|
|
327
|
+
}
|
|
311
328
|
if (ctx.redirectChainLinks.length > 0) {
|
|
312
329
|
return createResult({ id: 'linking-redirect-chains', name: 'Redirect Chains', category: 'links', severity: 'warning' }, 'warn', `${ctx.redirectChainLinks.length} link(s) go through redirects`, {
|
|
313
330
|
recommendation: 'Update links to point to final destination URLs',
|
|
@@ -328,8 +345,9 @@ export const internalLinkingRules = [
|
|
|
328
345
|
severity: 'warning',
|
|
329
346
|
description: 'Internal links should not use nofollow',
|
|
330
347
|
check: (ctx) => {
|
|
331
|
-
if (!ctx.allLinks)
|
|
332
|
-
return
|
|
348
|
+
if (!ctx.allLinks) {
|
|
349
|
+
return createResult({ id: 'linking-nofollow-internal', name: 'Nofollow Internal Links', category: 'links', severity: 'warning' }, 'info', 'Not applicable (links data unavailable)', { recommendation: 'This rule checks for nofollow attributes on internal links which waste PageRank' });
|
|
350
|
+
}
|
|
333
351
|
const nofollowInternal = ctx.allLinks.filter(l => l.type === 'internal' && l.rel?.includes('nofollow'));
|
|
334
352
|
if (nofollowInternal.length > 0) {
|
|
335
353
|
return createResult({ id: 'linking-nofollow-internal', name: 'Nofollow Internal Links', category: 'links', severity: 'warning' }, 'warn', `${nofollowInternal.length} internal link(s) have nofollow`, {
|
|
@@ -351,8 +369,9 @@ export const internalLinkingRules = [
|
|
|
351
369
|
severity: 'info',
|
|
352
370
|
description: 'Important pages should be reachable in few clicks',
|
|
353
371
|
check: (ctx) => {
|
|
354
|
-
if (ctx.pageClickDepth === undefined)
|
|
355
|
-
return
|
|
372
|
+
if (ctx.pageClickDepth === undefined) {
|
|
373
|
+
return createResult({ id: 'linking-click-depth', name: 'Click Depth', category: 'links', severity: 'info' }, 'info', 'Not applicable (click depth data unavailable)', { recommendation: 'This rule checks how many clicks from homepage, affecting crawl priority and link equity' });
|
|
374
|
+
}
|
|
356
375
|
const depth = ctx.pageClickDepth;
|
|
357
376
|
if (depth > 4) {
|
|
358
377
|
return createResult({ id: 'linking-click-depth', name: 'Click Depth', category: 'links', severity: 'info' }, 'warn', `Page is ${depth} clicks from homepage`, {
|