recker 1.0.43 → 1.0.44-next.084c2ef
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/README.md +47 -0
- 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 +85617 -99560
- 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 +733 -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 +270 -0
- package/dist/browser/seo/rules/content.d.ts +2 -0
- package/dist/browser/seo/rules/content.js +522 -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 +312 -0
- package/dist/browser/seo/rules/i18n.d.ts +2 -0
- package/dist/browser/seo/rules/i18n.js +288 -0
- package/dist/browser/seo/rules/images.d.ts +2 -0
- package/dist/browser/seo/rules/images.js +255 -0
- package/dist/browser/seo/rules/index.d.ts +52 -0
- package/dist/browser/seo/rules/index.js +163 -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 +498 -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/manifest.d.ts +2 -0
- package/dist/browser/seo/rules/manifest.js +621 -0
- package/dist/browser/seo/rules/meta.d.ts +2 -0
- package/dist/browser/seo/rules/meta.js +805 -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 +738 -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 +223 -0
- package/dist/browser/seo/rules/technical-advanced.d.ts +10 -0
- package/dist/browser/seo/rules/technical-advanced.js +289 -0
- package/dist/browser/seo/rules/technical.d.ts +3 -0
- package/dist/browser/seo/rules/technical.js +509 -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 +518 -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 +478 -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 -1
- 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 +96 -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 +53 -32
- package/dist/seo/rules/content.js +317 -31
- package/dist/seo/rules/crawl.js +55 -40
- package/dist/seo/rules/cwv.js +21 -15
- package/dist/seo/rules/ecommerce.js +82 -22
- package/dist/seo/rules/i18n.js +75 -36
- package/dist/seo/rules/images.js +109 -30
- package/dist/seo/rules/index.js +7 -1
- package/dist/seo/rules/internal-linking.js +58 -39
- package/dist/seo/rules/links.js +79 -52
- package/dist/seo/rules/local.js +49 -25
- package/dist/seo/rules/manifest.d.ts +2 -0
- package/dist/seo/rules/manifest.js +621 -0
- package/dist/seo/rules/meta.js +339 -81
- package/dist/seo/rules/mobile.js +112 -2
- package/dist/seo/rules/performance.js +434 -66
- 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 +87 -19
- package/dist/seo/rules/technical-advanced.js +30 -24
- package/dist/seo/rules/technical.d.ts +1 -0
- package/dist/seo/rules/technical.js +272 -42
- package/dist/seo/rules/types.d.ts +74 -2
- package/dist/seo/seo-spider.d.ts +35 -0
- package/dist/seo/seo-spider.js +152 -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 +12 -6
- package/dist/browser/utils/optional-require.d.ts +0 -19
- package/dist/browser/utils/optional-require.js +0 -105
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { createResult } from './types.js';
|
|
2
|
+
export const analyticsRules = [
|
|
3
|
+
{
|
|
4
|
+
id: 'analytics-installed',
|
|
5
|
+
name: 'Analytics Installed',
|
|
6
|
+
category: 'technical',
|
|
7
|
+
severity: 'warning',
|
|
8
|
+
description: 'Page should have analytics tracking installed',
|
|
9
|
+
check: (ctx) => {
|
|
10
|
+
if (ctx.analyticsDetected === undefined) {
|
|
11
|
+
return createResult({ id: 'analytics-installed', name: 'Analytics Installed', category: 'technical', severity: 'warning' }, 'info', 'Not applicable (analytics detection data unavailable)', { recommendation: 'This rule checks for analytics tracking to measure user behavior' });
|
|
12
|
+
}
|
|
13
|
+
if (!ctx.analyticsDetected || ctx.analyticsProviders?.length === 0) {
|
|
14
|
+
return createResult({ id: 'analytics-installed', name: 'Analytics Installed', category: 'technical', severity: 'warning' }, 'warn', 'No analytics tracking detected', {
|
|
15
|
+
recommendation: 'Install analytics to understand user behavior and optimize conversions.',
|
|
16
|
+
evidence: {
|
|
17
|
+
found: 'No analytics scripts detected',
|
|
18
|
+
expected: 'Google Analytics, GTM, or other analytics provider',
|
|
19
|
+
impact: 'Without analytics, you cannot measure traffic sources, user behavior, conversion rates, or content performance. The average conversion rate is only 2.35% - optimization requires data.',
|
|
20
|
+
example: '<!-- Google Analytics 4 -->\n<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX"></script>',
|
|
21
|
+
learnMore: 'https://marketingplatform.google.com/about/analytics/',
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
return createResult({ id: 'analytics-installed', name: 'Analytics Installed', category: 'technical', severity: 'warning' }, 'pass', `Analytics detected: ${ctx.analyticsProviders?.join(', ')}`, { value: ctx.analyticsProviders?.join(', ') });
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: 'google-analytics-4',
|
|
30
|
+
name: 'Google Analytics 4',
|
|
31
|
+
category: 'technical',
|
|
32
|
+
severity: 'info',
|
|
33
|
+
description: 'Check for modern GA4 vs deprecated Universal Analytics',
|
|
34
|
+
check: (ctx) => {
|
|
35
|
+
if (!ctx.analyticsProviders || ctx.analyticsProviders.length === 0) {
|
|
36
|
+
return createResult({ id: 'google-analytics-4', name: 'Google Analytics 4', category: 'technical', severity: 'info' }, 'info', 'Not applicable (no analytics providers detected)', { recommendation: 'This rule checks for modern Google Analytics 4 implementation' });
|
|
37
|
+
}
|
|
38
|
+
const hasUA = ctx.analyticsProviders.some(p => p === 'Universal Analytics (UA)');
|
|
39
|
+
const hasGA4 = ctx.analyticsProviders.some(p => p === 'Google Analytics 4 (GA4)');
|
|
40
|
+
if (hasUA && !hasGA4) {
|
|
41
|
+
return createResult({ id: 'google-analytics-4', name: 'Google Analytics 4', category: 'technical', severity: 'info' }, 'warn', 'Using deprecated Universal Analytics (UA) without GA4', {
|
|
42
|
+
recommendation: 'Migrate to Google Analytics 4 - Universal Analytics stopped processing data July 2023.',
|
|
43
|
+
evidence: {
|
|
44
|
+
found: 'Universal Analytics (analytics.js or ga.js)',
|
|
45
|
+
expected: 'Google Analytics 4 (gtag.js with G-XXXXXX)',
|
|
46
|
+
impact: 'Universal Analytics is deprecated and no longer collects data. GA4 offers better cross-platform tracking, machine learning insights, and privacy-focused measurement.',
|
|
47
|
+
example: '<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX"></script>',
|
|
48
|
+
learnMore: 'https://support.google.com/analytics/answer/11583528',
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
if (hasGA4) {
|
|
53
|
+
return createResult({ id: 'google-analytics-4', name: 'Google Analytics 4', category: 'technical', severity: 'info' }, 'pass', 'Using modern Google Analytics 4');
|
|
54
|
+
}
|
|
55
|
+
return createResult({ id: 'google-analytics-4', name: 'Google Analytics 4', category: 'technical', severity: 'info' }, 'pass', 'Using other analytics provider (not Google Analytics)');
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: 'google-tag-manager',
|
|
60
|
+
name: 'Google Tag Manager',
|
|
61
|
+
category: 'technical',
|
|
62
|
+
severity: 'info',
|
|
63
|
+
description: 'Google Tag Manager enables flexible tag management without code changes',
|
|
64
|
+
check: (ctx) => {
|
|
65
|
+
if (!ctx.analyticsProviders) {
|
|
66
|
+
return createResult({ id: 'google-tag-manager', name: 'Google Tag Manager', category: 'technical', severity: 'info' }, 'info', 'Not applicable (analytics provider data unavailable)', { recommendation: 'This rule checks for Google Tag Manager for easier tag management' });
|
|
67
|
+
}
|
|
68
|
+
const hasGTM = ctx.analyticsProviders.some(p => p === 'Google Tag Manager');
|
|
69
|
+
if (hasGTM) {
|
|
70
|
+
return createResult({ id: 'google-tag-manager', name: 'Google Tag Manager', category: 'technical', severity: 'info' }, 'pass', 'Google Tag Manager detected', {
|
|
71
|
+
evidence: {
|
|
72
|
+
found: 'GTM container script',
|
|
73
|
+
impact: 'GTM allows marketing teams to manage tags without developer intervention, improving agility.',
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
if (ctx.analyticsDetected && ctx.analyticsProviders.length > 0) {
|
|
78
|
+
return createResult({ id: 'google-tag-manager', name: 'Google Tag Manager', category: 'technical', severity: 'info' }, 'info', 'Consider using Google Tag Manager for easier tag management', {
|
|
79
|
+
recommendation: 'GTM centralizes all tracking scripts and enables non-developers to manage tags.',
|
|
80
|
+
evidence: {
|
|
81
|
+
found: `Direct analytics scripts: ${ctx.analyticsProviders.join(', ')}`,
|
|
82
|
+
expected: 'Google Tag Manager container managing all tags',
|
|
83
|
+
impact: 'GTM provides version control, preview mode, and easy updates without code changes. Best practice for sites with multiple tracking tools.',
|
|
84
|
+
learnMore: 'https://tagmanager.google.com/',
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return createResult({ id: 'google-tag-manager', name: 'Google Tag Manager', category: 'technical', severity: 'info' }, 'pass', 'No analytics detected or GTM not needed');
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: 'multiple-analytics-warning',
|
|
93
|
+
name: 'Multiple Analytics Scripts',
|
|
94
|
+
category: 'performance',
|
|
95
|
+
severity: 'warning',
|
|
96
|
+
description: 'Too many analytics scripts can impact page performance',
|
|
97
|
+
check: (ctx) => {
|
|
98
|
+
if (!ctx.analyticsProviders || ctx.analyticsProviders.length < 4) {
|
|
99
|
+
return createResult({ id: 'multiple-analytics-warning', name: 'Multiple Analytics Scripts', category: 'performance', severity: 'warning' }, 'info', 'Not applicable (acceptable number of analytics scripts)', { recommendation: 'This rule warns when too many analytics scripts impact performance' });
|
|
100
|
+
}
|
|
101
|
+
return createResult({ id: 'multiple-analytics-warning', name: 'Multiple Analytics Scripts', category: 'performance', severity: 'warning' }, 'warn', `${ctx.analyticsProviders.length} analytics scripts detected`, {
|
|
102
|
+
value: ctx.analyticsProviders.length,
|
|
103
|
+
recommendation: 'Consolidate analytics tools to reduce page load impact.',
|
|
104
|
+
evidence: {
|
|
105
|
+
found: ctx.analyticsProviders.join(', '),
|
|
106
|
+
expected: '1-3 analytics tools',
|
|
107
|
+
impact: 'Each analytics script adds HTTP requests, JavaScript execution time, and can slow page rendering. Too many tools can impact Core Web Vitals and user experience.',
|
|
108
|
+
learnMore: 'https://web.dev/articles/optimize-third-party-javascript',
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
id: 'heatmap-tool',
|
|
115
|
+
name: 'Heatmap/Session Recording',
|
|
116
|
+
category: 'technical',
|
|
117
|
+
severity: 'info',
|
|
118
|
+
description: 'Heatmap tools like Hotjar help visualize user behavior',
|
|
119
|
+
check: (ctx) => {
|
|
120
|
+
if (!ctx.analyticsProviders) {
|
|
121
|
+
return createResult({ id: 'heatmap-tool', name: 'Heatmap Tool', category: 'technical', severity: 'info' }, 'info', 'Not applicable (analytics provider data unavailable)', { recommendation: 'This rule checks for heatmap and session recording tools' });
|
|
122
|
+
}
|
|
123
|
+
const heatmapTools = ['Hotjar', 'Microsoft Clarity', 'FullStory', 'Lucky Orange', 'Crazy Egg'];
|
|
124
|
+
const foundHeatmap = ctx.analyticsProviders.filter(p => heatmapTools.includes(p));
|
|
125
|
+
if (foundHeatmap.length > 0) {
|
|
126
|
+
return createResult({ id: 'heatmap-tool', name: 'Heatmap Tool', category: 'technical', severity: 'info' }, 'pass', `Heatmap/session recording detected: ${foundHeatmap.join(', ')}`, {
|
|
127
|
+
evidence: {
|
|
128
|
+
found: foundHeatmap.join(', '),
|
|
129
|
+
impact: 'Heatmaps and session recordings help identify UX issues, understand user journeys, and optimize conversion paths.',
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
return createResult({ id: 'heatmap-tool', name: 'Heatmap Tool', category: 'technical', severity: 'info' }, 'pass', 'No heatmap tool detected (optional)');
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
id: 'cta-presence',
|
|
138
|
+
name: 'Call-to-Action Elements',
|
|
139
|
+
category: 'content',
|
|
140
|
+
severity: 'info',
|
|
141
|
+
description: 'Page should have clear call-to-action elements',
|
|
142
|
+
check: (ctx) => {
|
|
143
|
+
if (ctx.ctaButtonsCount === undefined) {
|
|
144
|
+
return createResult({ id: 'cta-presence', name: 'Call-to-Action Elements', category: 'content', severity: 'info' }, 'info', 'Not applicable (CTA button data unavailable)', { recommendation: 'This rule checks for clear call-to-action elements to guide conversions' });
|
|
145
|
+
}
|
|
146
|
+
if (ctx.ctaButtonsCount === 0) {
|
|
147
|
+
return createResult({ id: 'cta-presence', name: 'Call-to-Action Elements', category: 'content', severity: 'info' }, 'info', 'No clear call-to-action buttons detected', {
|
|
148
|
+
recommendation: 'Add clear CTAs to guide users toward conversion.',
|
|
149
|
+
evidence: {
|
|
150
|
+
found: 'No buttons with action-oriented text',
|
|
151
|
+
expected: 'Buttons like "Get Started", "Sign Up", "Buy Now", "Contact Us"',
|
|
152
|
+
impact: 'Clear CTAs guide users through the conversion funnel. Pages without obvious next steps lose potential conversions.',
|
|
153
|
+
example: '<button class="cta">Get Started Free</button>\n<a href="/signup" class="btn-primary">Start Your Trial</a>',
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
return createResult({ id: 'cta-presence', name: 'Call-to-Action Elements', category: 'content', severity: 'info' }, 'pass', `${ctx.ctaButtonsCount} call-to-action element(s) found`, { value: ctx.ctaButtonsCount });
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
id: 'form-presence',
|
|
162
|
+
name: 'Form Elements',
|
|
163
|
+
category: 'content',
|
|
164
|
+
severity: 'info',
|
|
165
|
+
description: 'Detect forms for lead generation or conversion',
|
|
166
|
+
check: (ctx) => {
|
|
167
|
+
if (ctx.formCount === undefined) {
|
|
168
|
+
return createResult({ id: 'form-presence', name: 'Form Elements', category: 'content', severity: 'info' }, 'info', 'Not applicable (form count data unavailable)', { recommendation: 'This rule checks for forms for lead generation and conversions' });
|
|
169
|
+
}
|
|
170
|
+
if (ctx.formCount > 0) {
|
|
171
|
+
return createResult({ id: 'form-presence', name: 'Form Elements', category: 'content', severity: 'info' }, 'pass', `${ctx.formCount} form(s) detected for lead capture/conversion`, {
|
|
172
|
+
value: ctx.formCount,
|
|
173
|
+
evidence: {
|
|
174
|
+
found: `${ctx.formCount} form elements`,
|
|
175
|
+
impact: 'Forms are essential for lead generation, contact, and conversions.',
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
return createResult({ id: 'form-presence', name: 'Form Elements', category: 'content', severity: 'info' }, 'pass', 'No forms detected (may not be needed for this page type)');
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
id: 'contact-methods',
|
|
184
|
+
name: 'Contact Methods',
|
|
185
|
+
category: 'content',
|
|
186
|
+
severity: 'info',
|
|
187
|
+
description: 'Page should provide contact methods for user trust',
|
|
188
|
+
check: (ctx) => {
|
|
189
|
+
const methods = [];
|
|
190
|
+
if (ctx.emailsFound && ctx.emailsFound.length > 0)
|
|
191
|
+
methods.push('email');
|
|
192
|
+
if (ctx.hasPhoneOnPage)
|
|
193
|
+
methods.push('phone');
|
|
194
|
+
if (ctx.hasWhatsAppLink)
|
|
195
|
+
methods.push('WhatsApp');
|
|
196
|
+
if (methods.length === 0 && ctx.hasContactPageLink) {
|
|
197
|
+
return createResult({ id: 'contact-methods', name: 'Contact Methods', category: 'content', severity: 'info' }, 'pass', 'Contact page link found', { evidence: { found: 'Link to contact page' } });
|
|
198
|
+
}
|
|
199
|
+
if (methods.length > 0) {
|
|
200
|
+
return createResult({ id: 'contact-methods', name: 'Contact Methods', category: 'content', severity: 'info' }, 'pass', `Contact methods found: ${methods.join(', ')}`, { value: methods.join(', ') });
|
|
201
|
+
}
|
|
202
|
+
return createResult({ id: 'contact-methods', name: 'Contact Methods', category: 'content', severity: 'info' }, 'pass', 'No direct contact methods detected (may not be needed)');
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
id: 'rss-feed-present',
|
|
207
|
+
name: 'RSS Feed',
|
|
208
|
+
category: 'technical',
|
|
209
|
+
severity: 'info',
|
|
210
|
+
description: 'RSS feeds help users and search engines discover new content',
|
|
211
|
+
check: (ctx) => {
|
|
212
|
+
if (ctx.hasRssFeed === undefined) {
|
|
213
|
+
return createResult({ id: 'rss-feed-present', name: 'RSS Feed', category: 'technical', severity: 'info' }, 'info', 'Not applicable (RSS feed data unavailable)', { recommendation: 'This rule checks for RSS feeds for content distribution' });
|
|
214
|
+
}
|
|
215
|
+
if (ctx.hasRssFeed) {
|
|
216
|
+
return createResult({ id: 'rss-feed-present', name: 'RSS Feed', category: 'technical', severity: 'info' }, 'pass', 'RSS feed detected', {
|
|
217
|
+
value: ctx.rssFeedUrl,
|
|
218
|
+
evidence: {
|
|
219
|
+
found: ctx.rssFeedUrl || 'RSS link tag present',
|
|
220
|
+
impact: 'RSS feeds notify search engines and feed readers when content is updated, improving content discovery.',
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
return createResult({ id: 'rss-feed-present', name: 'RSS Feed', category: 'technical', severity: 'info' }, 'info', 'No RSS feed detected', {
|
|
225
|
+
recommendation: 'Add an RSS feed for blogs or news sites to improve content distribution.',
|
|
226
|
+
evidence: {
|
|
227
|
+
expected: '<link rel="alternate" type="application/rss+xml" href="/feed.xml">',
|
|
228
|
+
impact: 'RSS feeds help users subscribe to content and notify search engines of updates. Particularly important for blogs and news sites.',
|
|
229
|
+
learnMore: 'https://developers.google.com/search/docs/crawling-indexing/sitemaps/build-sitemap#rss-atom',
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
id: 'atom-feed-present',
|
|
236
|
+
name: 'Atom Feed',
|
|
237
|
+
category: 'technical',
|
|
238
|
+
severity: 'info',
|
|
239
|
+
description: 'Atom feeds are an alternative to RSS for content syndication',
|
|
240
|
+
check: (ctx) => {
|
|
241
|
+
if (ctx.hasAtomFeed === undefined) {
|
|
242
|
+
return createResult({ id: 'atom-feed-present', name: 'Atom Feed', category: 'technical', severity: 'info' }, 'info', 'Not applicable (Atom feed data unavailable)', { recommendation: 'This rule checks for Atom feeds as alternative to RSS' });
|
|
243
|
+
}
|
|
244
|
+
if (ctx.hasAtomFeed) {
|
|
245
|
+
return createResult({ id: 'atom-feed-present', name: 'Atom Feed', category: 'technical', severity: 'info' }, 'pass', 'Atom feed detected', {
|
|
246
|
+
value: ctx.atomFeedUrl,
|
|
247
|
+
evidence: {
|
|
248
|
+
found: ctx.atomFeedUrl || 'Atom link tag present',
|
|
249
|
+
},
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
return createResult({ id: 'atom-feed-present', name: 'Atom Feed', category: 'technical', severity: 'info' }, 'pass', 'No Atom feed detected (RSS is more common)');
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
id: 'noindex-canonical-conflict',
|
|
257
|
+
name: 'Noindex + Canonical Conflict',
|
|
258
|
+
category: 'technical',
|
|
259
|
+
severity: 'error',
|
|
260
|
+
description: 'Having both noindex and canonical sends conflicting signals',
|
|
261
|
+
check: (ctx) => {
|
|
262
|
+
if (!ctx.metaRobots || !ctx.hasCanonical) {
|
|
263
|
+
return createResult({ id: 'noindex-canonical-conflict', name: 'Noindex + Canonical Conflict', category: 'technical', severity: 'error' }, 'info', 'Not applicable (robots meta tag or canonical data unavailable)', { recommendation: 'This rule detects conflicting noindex and canonical directives' });
|
|
264
|
+
}
|
|
265
|
+
const hasNoindex = ctx.metaRobots.includes('noindex');
|
|
266
|
+
if (hasNoindex && ctx.hasCanonical) {
|
|
267
|
+
return createResult({ id: 'noindex-canonical-conflict', name: 'Noindex + Canonical Conflict', category: 'technical', severity: 'error' }, 'fail', 'Page has both noindex and canonical - conflicting signals', {
|
|
268
|
+
recommendation: 'Remove either noindex or canonical. They serve opposite purposes.',
|
|
269
|
+
evidence: {
|
|
270
|
+
found: `noindex meta tag + canonical: ${ctx.canonicalUrl}`,
|
|
271
|
+
expected: 'Either noindex (to block indexing) OR canonical (to consolidate indexing)',
|
|
272
|
+
impact: 'noindex tells search engines not to index this page, while canonical tells them to consolidate signals to a URL. Google may ignore the canonical if noindex is present, wasting crawl budget.',
|
|
273
|
+
issue: 'Conflicting indexing directives',
|
|
274
|
+
learnMore: 'https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls',
|
|
275
|
+
},
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
return createResult({ id: 'noindex-canonical-conflict', name: 'Noindex + Canonical Conflict', category: 'technical', severity: 'error' }, 'pass', 'No conflicting noindex and canonical directives');
|
|
279
|
+
},
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
id: 'noindex-sitemap-hint',
|
|
283
|
+
name: 'Noindex Pages in Sitemap',
|
|
284
|
+
category: 'crawlability',
|
|
285
|
+
severity: 'warning',
|
|
286
|
+
description: 'Pages with noindex should not be in sitemap',
|
|
287
|
+
check: (ctx) => {
|
|
288
|
+
if (!ctx.metaRobots) {
|
|
289
|
+
return createResult({ id: 'noindex-sitemap-hint', name: 'Noindex in Sitemap', category: 'crawlability', severity: 'warning' }, 'info', 'Not applicable (robots meta tag data unavailable)', { recommendation: 'This rule checks if noindex pages are incorrectly included in sitemap' });
|
|
290
|
+
}
|
|
291
|
+
const hasNoindex = ctx.metaRobots.includes('noindex');
|
|
292
|
+
if (hasNoindex && ctx.pageInSitemap) {
|
|
293
|
+
return createResult({ id: 'noindex-sitemap-hint', name: 'Noindex in Sitemap', category: 'crawlability', severity: 'warning' }, 'warn', 'Page has noindex but appears to be in sitemap', {
|
|
294
|
+
recommendation: 'Remove noindex pages from sitemap to avoid wasting crawl budget.',
|
|
295
|
+
evidence: {
|
|
296
|
+
found: 'noindex directive on page that may be in sitemap',
|
|
297
|
+
expected: 'Only indexable pages in sitemap',
|
|
298
|
+
impact: 'Including noindex pages in sitemap wastes crawl budget and sends conflicting signals to search engines.',
|
|
299
|
+
learnMore: 'https://developers.google.com/search/docs/crawling-indexing/sitemaps/build-sitemap#best-practices',
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
return createResult({ id: 'noindex-sitemap-hint', name: 'Noindex in Sitemap', category: 'crawlability', severity: 'warning' }, 'pass', 'Page is indexable or not in sitemap');
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
];
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { createResult } from './types.js';
|
|
2
|
+
export const bestPracticesRules = [
|
|
3
|
+
{
|
|
4
|
+
id: 'bp-doctype',
|
|
5
|
+
name: 'HTML Doctype',
|
|
6
|
+
category: 'technical',
|
|
7
|
+
severity: 'error',
|
|
8
|
+
description: 'Page must have the HTML doctype',
|
|
9
|
+
check: (ctx) => {
|
|
10
|
+
if (ctx.hasDoctype === undefined) {
|
|
11
|
+
return createResult({ id: 'bp-doctype', name: 'HTML Doctype', category: 'technical', severity: 'error' }, 'info', 'Unable to check doctype (data unavailable)', { recommendation: 'Ensure HTML content is properly fetched' });
|
|
12
|
+
}
|
|
13
|
+
if (!ctx.hasDoctype) {
|
|
14
|
+
return createResult({ id: 'bp-doctype', name: 'HTML Doctype', category: 'technical', severity: 'error' }, 'fail', 'Page is missing HTML doctype', {
|
|
15
|
+
recommendation: 'Add <!DOCTYPE html> at the start of the document',
|
|
16
|
+
evidence: {
|
|
17
|
+
expected: '<!DOCTYPE html>',
|
|
18
|
+
impact: 'Missing doctype triggers quirks mode in browsers',
|
|
19
|
+
learnMore: 'https://developer.mozilla.org/en-US/docs/Glossary/Doctype',
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
return createResult({ id: 'bp-doctype', name: 'HTML Doctype', category: 'technical', severity: 'error' }, 'pass', 'Page has HTML doctype');
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: 'bp-charset',
|
|
28
|
+
name: 'Character Encoding',
|
|
29
|
+
category: 'technical',
|
|
30
|
+
severity: 'error',
|
|
31
|
+
description: 'Page should properly define charset',
|
|
32
|
+
check: (ctx) => {
|
|
33
|
+
if (ctx.hasCharset === undefined) {
|
|
34
|
+
return createResult({ id: 'bp-charset', name: 'Character Encoding', category: 'technical', severity: 'error' }, 'info', 'Unable to check charset (data unavailable)', { recommendation: 'Ensure HTML content is properly fetched' });
|
|
35
|
+
}
|
|
36
|
+
if (!ctx.hasCharset) {
|
|
37
|
+
return createResult({ id: 'bp-charset', name: 'Character Encoding', category: 'technical', severity: 'error' }, 'fail', 'Page is missing character encoding declaration', {
|
|
38
|
+
recommendation: 'Add <meta charset="UTF-8"> in the <head>',
|
|
39
|
+
evidence: {
|
|
40
|
+
expected: '<meta charset="UTF-8">',
|
|
41
|
+
impact: 'Characters may render incorrectly without charset declaration',
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
if (ctx.charset && ctx.charset.toLowerCase() !== 'utf-8') {
|
|
46
|
+
return createResult({ id: 'bp-charset', name: 'Character Encoding', category: 'technical', severity: 'error' }, 'warn', `Non-UTF-8 charset: ${ctx.charset}`, {
|
|
47
|
+
value: ctx.charset,
|
|
48
|
+
recommendation: 'Use UTF-8 encoding for maximum compatibility',
|
|
49
|
+
evidence: {
|
|
50
|
+
found: ctx.charset,
|
|
51
|
+
expected: 'UTF-8',
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return createResult({ id: 'bp-charset', name: 'Character Encoding', category: 'technical', severity: 'error' }, 'pass', `Charset: ${ctx.charset || 'UTF-8'}`);
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: 'bp-http-status',
|
|
60
|
+
name: 'HTTP Status Code',
|
|
61
|
+
category: 'technical',
|
|
62
|
+
severity: 'error',
|
|
63
|
+
description: 'Page should have successful HTTP status code',
|
|
64
|
+
check: (ctx) => {
|
|
65
|
+
if (ctx.httpStatusCode === undefined) {
|
|
66
|
+
return createResult({ id: 'bp-http-status', name: 'HTTP Status Code', category: 'technical', severity: 'error' }, 'info', 'Unable to check HTTP status (data unavailable)', { recommendation: 'Ensure the page request completed successfully' });
|
|
67
|
+
}
|
|
68
|
+
const status = ctx.httpStatusCode;
|
|
69
|
+
if (status >= 400) {
|
|
70
|
+
return createResult({ id: 'bp-http-status', name: 'HTTP Status Code', category: 'technical', severity: 'error' }, 'fail', `HTTP ${status} error response`, {
|
|
71
|
+
value: status,
|
|
72
|
+
recommendation: 'Fix server configuration to return successful status codes',
|
|
73
|
+
evidence: {
|
|
74
|
+
found: status,
|
|
75
|
+
expected: '200-299',
|
|
76
|
+
impact: 'Error pages will not be indexed by search engines',
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
if (status >= 300) {
|
|
81
|
+
return createResult({ id: 'bp-http-status', name: 'HTTP Status Code', category: 'technical', severity: 'error' }, 'info', `HTTP ${status} redirect response`, {
|
|
82
|
+
value: status,
|
|
83
|
+
recommendation: 'Redirects add latency; consider updating links to final URLs',
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return createResult({ id: 'bp-http-status', name: 'HTTP Status Code', category: 'technical', severity: 'error' }, 'pass', `HTTP ${status} OK`);
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: 'bp-indexable',
|
|
91
|
+
name: 'Page Indexable',
|
|
92
|
+
category: 'technical',
|
|
93
|
+
severity: 'error',
|
|
94
|
+
description: 'Page should not be blocked from indexing',
|
|
95
|
+
check: (ctx) => {
|
|
96
|
+
if (ctx.metaRobots === undefined) {
|
|
97
|
+
return createResult({ id: 'bp-indexable', name: 'Page Indexable', category: 'technical', severity: 'error' }, 'info', 'Unable to check indexability (no robots meta)', { recommendation: 'Check if the page has robots meta tags' });
|
|
98
|
+
}
|
|
99
|
+
const robots = Array.isArray(ctx.metaRobots) ? ctx.metaRobots : [ctx.metaRobots];
|
|
100
|
+
const blocked = robots.some(r => r.toLowerCase().includes('noindex') || r.toLowerCase().includes('none'));
|
|
101
|
+
if (blocked) {
|
|
102
|
+
return createResult({ id: 'bp-indexable', name: 'Page Indexable', category: 'technical', severity: 'error' }, 'fail', 'Page is blocked from indexing', {
|
|
103
|
+
recommendation: 'Remove noindex directive if this page should be searchable',
|
|
104
|
+
evidence: {
|
|
105
|
+
found: robots.join(', '),
|
|
106
|
+
expected: 'No noindex directive',
|
|
107
|
+
impact: 'Page will not appear in search results',
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
return createResult({ id: 'bp-indexable', name: 'Page Indexable', category: 'technical', severity: 'error' }, 'pass', 'Page is indexable');
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
id: 'bp-links-crawlable',
|
|
116
|
+
name: 'Crawlable Links',
|
|
117
|
+
category: 'links',
|
|
118
|
+
severity: 'warning',
|
|
119
|
+
description: 'Links should be crawlable by search engines',
|
|
120
|
+
check: (ctx) => {
|
|
121
|
+
if (ctx.uncrawlableLinksCount === undefined) {
|
|
122
|
+
return createResult({ id: 'bp-links-crawlable', name: 'Crawlable Links', category: 'links', severity: 'warning' }, 'info', 'Unable to check link crawlability (data unavailable)', { recommendation: 'Ensure link analysis completed' });
|
|
123
|
+
}
|
|
124
|
+
const count = ctx.uncrawlableLinksCount;
|
|
125
|
+
if (count > 0) {
|
|
126
|
+
return createResult({ id: 'bp-links-crawlable', name: 'Crawlable Links', category: 'links', severity: 'warning' }, 'warn', `${count} link(s) are not crawlable`, {
|
|
127
|
+
value: count,
|
|
128
|
+
recommendation: 'Use <a href> tags instead of JavaScript navigation for important links',
|
|
129
|
+
evidence: {
|
|
130
|
+
found: count,
|
|
131
|
+
expected: 0,
|
|
132
|
+
impact: 'Search engines cannot discover linked content',
|
|
133
|
+
learnMore: 'https://developers.google.com/search/docs/crawling-indexing/links-crawlable',
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
return createResult({ id: 'bp-links-crawlable', name: 'Crawlable Links', category: 'links', severity: 'warning' }, 'pass', 'All links are crawlable');
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
id: 'bp-robots-txt',
|
|
142
|
+
name: 'Valid robots.txt',
|
|
143
|
+
category: 'technical',
|
|
144
|
+
severity: 'info',
|
|
145
|
+
description: 'robots.txt should be valid and accessible',
|
|
146
|
+
check: (ctx) => {
|
|
147
|
+
if (ctx.robotsTxtValid === undefined) {
|
|
148
|
+
return createResult({ id: 'bp-robots-txt', name: 'Valid robots.txt', category: 'technical', severity: 'info' }, 'info', 'Unable to check robots.txt (data unavailable)', { recommendation: 'Check if robots.txt is accessible at /robots.txt' });
|
|
149
|
+
}
|
|
150
|
+
if (!ctx.robotsTxtValid) {
|
|
151
|
+
return createResult({ id: 'bp-robots-txt', name: 'Valid robots.txt', category: 'technical', severity: 'info' }, 'warn', 'robots.txt is invalid or inaccessible', {
|
|
152
|
+
recommendation: 'Ensure robots.txt is valid and returns 200 status',
|
|
153
|
+
evidence: {
|
|
154
|
+
found: ctx.robotsTxtError || 'Invalid',
|
|
155
|
+
expected: 'Valid robots.txt',
|
|
156
|
+
impact: 'Invalid robots.txt may cause crawling issues',
|
|
157
|
+
learnMore: 'https://developers.google.com/search/docs/crawling-indexing/robots/intro',
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
return createResult({ id: 'bp-robots-txt', name: 'Valid robots.txt', category: 'technical', severity: 'info' }, 'pass', 'robots.txt is valid');
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
id: 'bp-structured-data',
|
|
166
|
+
name: 'Structured Data',
|
|
167
|
+
category: 'structured-data',
|
|
168
|
+
severity: 'info',
|
|
169
|
+
description: 'Page should have valid structured data',
|
|
170
|
+
check: (ctx) => {
|
|
171
|
+
if (ctx.structuredDataErrors === undefined) {
|
|
172
|
+
return createResult({ id: 'bp-structured-data', name: 'Structured Data', category: 'structured-data', severity: 'info' }, 'info', 'Unable to check structured data (data unavailable)', { recommendation: 'Ensure JSON-LD structured data analysis completed' });
|
|
173
|
+
}
|
|
174
|
+
const errors = ctx.structuredDataErrors;
|
|
175
|
+
if (errors > 0) {
|
|
176
|
+
return createResult({ id: 'bp-structured-data', name: 'Structured Data', category: 'structured-data', severity: 'info' }, 'warn', `${errors} structured data error(s) found`, {
|
|
177
|
+
value: errors,
|
|
178
|
+
recommendation: 'Validate structured data using Google Rich Results Test',
|
|
179
|
+
evidence: {
|
|
180
|
+
found: errors,
|
|
181
|
+
expected: 0,
|
|
182
|
+
impact: 'Invalid structured data may not generate rich results',
|
|
183
|
+
learnMore: 'https://search.google.com/test/rich-results',
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
if (ctx.jsonLdCount && ctx.jsonLdCount > 0) {
|
|
188
|
+
return createResult({ id: 'bp-structured-data', name: 'Structured Data', category: 'structured-data', severity: 'info' }, 'pass', `${ctx.jsonLdCount} structured data block(s) found`);
|
|
189
|
+
}
|
|
190
|
+
return createResult({ id: 'bp-structured-data', name: 'Structured Data', category: 'structured-data', severity: 'info' }, 'info', 'No structured data found', {
|
|
191
|
+
recommendation: 'Add schema.org structured data for rich search results',
|
|
192
|
+
});
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
];
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { SeoRule } from './types.js';
|
|
2
|
+
export declare const canonicalRules: SeoRule[];
|
|
3
|
+
declare module './types.js' {
|
|
4
|
+
interface RuleContext {
|
|
5
|
+
canonicalCount?: number;
|
|
6
|
+
canonicalUrls?: string[];
|
|
7
|
+
canonicalStatus?: number;
|
|
8
|
+
canonicalFinalUrl?: string;
|
|
9
|
+
canonicalChainLength?: number;
|
|
10
|
+
canonicalChain?: string[];
|
|
11
|
+
}
|
|
12
|
+
}
|