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/cwv.js
CHANGED
|
@@ -7,8 +7,9 @@ export const cwvRules = [
|
|
|
7
7
|
severity: 'warning',
|
|
8
8
|
description: 'Hero images should be optimized for fast LCP',
|
|
9
9
|
check: (ctx) => {
|
|
10
|
-
if (!ctx.lcpCandidate)
|
|
11
|
-
return
|
|
10
|
+
if (!ctx.lcpCandidate) {
|
|
11
|
+
return createResult({ id: 'cwv-lcp-hero-image', name: 'LCP Hero Image', category: 'performance', severity: 'warning' }, 'info', 'No LCP candidate detected', { recommendation: 'Ensure the page has a hero image or prominent content element' });
|
|
12
|
+
}
|
|
12
13
|
const issues = [];
|
|
13
14
|
if (ctx.lcpCandidate.loading === 'lazy') {
|
|
14
15
|
issues.push('LCP image has loading="lazy" (should be eager or omitted)');
|
|
@@ -42,8 +43,9 @@ export const cwvRules = [
|
|
|
42
43
|
severity: 'warning',
|
|
43
44
|
description: 'Text LCP elements should render immediately without font blocking',
|
|
44
45
|
check: (ctx) => {
|
|
45
|
-
if (!ctx.webFonts || ctx.webFonts.length === 0)
|
|
46
|
-
return
|
|
46
|
+
if (!ctx.webFonts || ctx.webFonts.length === 0) {
|
|
47
|
+
return createResult({ id: 'cwv-lcp-text-visible', name: 'LCP Text Visibility', category: 'performance', severity: 'warning' }, 'info', 'No web fonts detected', { recommendation: 'No custom web fonts to analyze' });
|
|
48
|
+
}
|
|
47
49
|
const blockingFonts = ctx.webFonts.filter(f => !f.hasSwap && !f.hasOptional);
|
|
48
50
|
if (blockingFonts.length > 0) {
|
|
49
51
|
return createResult({ id: 'cwv-lcp-text-visible', name: 'LCP Text Visibility', category: 'performance', severity: 'warning' }, 'warn', `${blockingFonts.length} web font(s) may block text rendering`, {
|
|
@@ -67,8 +69,9 @@ export const cwvRules = [
|
|
|
67
69
|
severity: 'warning',
|
|
68
70
|
description: 'Minimize render-blocking CSS for faster LCP',
|
|
69
71
|
check: (ctx) => {
|
|
70
|
-
if (ctx.renderBlockingStylesheets === undefined)
|
|
71
|
-
return
|
|
72
|
+
if (ctx.renderBlockingStylesheets === undefined) {
|
|
73
|
+
return createResult({ id: 'cwv-render-blocking-css', name: 'Render-Blocking CSS', category: 'performance', severity: 'warning' }, 'info', 'Unable to check render-blocking CSS (data unavailable)', { recommendation: 'Ensure CSS resource analysis completed' });
|
|
74
|
+
}
|
|
72
75
|
if (ctx.renderBlockingStylesheets > 3) {
|
|
73
76
|
return createResult({ id: 'cwv-render-blocking-css', name: 'Render-Blocking CSS', category: 'performance', severity: 'warning' }, 'warn', `${ctx.renderBlockingStylesheets} render-blocking stylesheets`, {
|
|
74
77
|
recommendation: 'Reduce render-blocking CSS or use critical CSS inline',
|
|
@@ -90,8 +93,9 @@ export const cwvRules = [
|
|
|
90
93
|
severity: 'warning',
|
|
91
94
|
description: 'Scripts in head should use async or defer',
|
|
92
95
|
check: (ctx) => {
|
|
93
|
-
if (ctx.renderBlockingScripts === undefined)
|
|
94
|
-
return
|
|
96
|
+
if (ctx.renderBlockingScripts === undefined) {
|
|
97
|
+
return createResult({ id: 'cwv-render-blocking-js', name: 'Render-Blocking JavaScript', category: 'performance', severity: 'warning' }, 'info', 'Unable to check render-blocking scripts (data unavailable)', { recommendation: 'Ensure script analysis completed' });
|
|
98
|
+
}
|
|
95
99
|
if (ctx.renderBlockingScripts > 0) {
|
|
96
100
|
return createResult({ id: 'cwv-render-blocking-js', name: 'Render-Blocking JavaScript', category: 'performance', severity: 'warning' }, 'warn', `${ctx.renderBlockingScripts} render-blocking script(s) in <head>`, {
|
|
97
101
|
recommendation: 'Add async or defer to scripts, or move to end of body',
|
|
@@ -113,8 +117,9 @@ export const cwvRules = [
|
|
|
113
117
|
severity: 'warning',
|
|
114
118
|
description: 'Images should have explicit width and height to prevent layout shift',
|
|
115
119
|
check: (ctx) => {
|
|
116
|
-
if (ctx.imagesMissingDimensions === undefined)
|
|
117
|
-
return
|
|
120
|
+
if (ctx.imagesMissingDimensions === undefined) {
|
|
121
|
+
return createResult({ id: 'cwv-cls-image-dimensions', name: 'Image Dimensions', category: 'performance', severity: 'warning' }, 'info', 'Unable to check image dimensions (data unavailable)', { recommendation: 'Ensure image analysis completed' });
|
|
122
|
+
}
|
|
118
123
|
if (ctx.imagesMissingDimensions > 0) {
|
|
119
124
|
const total = ctx.totalImages || 0;
|
|
120
125
|
const percent = total > 0 ? Math.round((ctx.imagesMissingDimensions / total) * 100) : 0;
|
|
@@ -149,7 +154,7 @@ export const cwvRules = [
|
|
|
149
154
|
},
|
|
150
155
|
});
|
|
151
156
|
}
|
|
152
|
-
return
|
|
157
|
+
return createResult({ id: 'cwv-cls-aspect-ratio', name: 'Aspect Ratio CSS', category: 'performance', severity: 'info' }, 'pass', 'Aspect ratio handling is appropriate');
|
|
153
158
|
},
|
|
154
159
|
},
|
|
155
160
|
{
|
|
@@ -159,8 +164,9 @@ export const cwvRules = [
|
|
|
159
164
|
severity: 'info',
|
|
160
165
|
description: 'Web fonts should have size-matched fallbacks',
|
|
161
166
|
check: (ctx) => {
|
|
162
|
-
if (!ctx.webFonts || ctx.webFonts.length === 0)
|
|
163
|
-
return
|
|
167
|
+
if (!ctx.webFonts || ctx.webFonts.length === 0) {
|
|
168
|
+
return createResult({ id: 'cwv-cls-font-fallback', name: 'Font Fallback Metrics', category: 'performance', severity: 'info' }, 'info', 'No web fonts to check', { recommendation: 'No custom web fonts detected' });
|
|
169
|
+
}
|
|
164
170
|
const fontsWithoutMetrics = ctx.webFonts.filter(f => !f.hasSizeAdjust && !f.hasAscentOverride);
|
|
165
171
|
if (fontsWithoutMetrics.length > 0 && ctx.webFonts.length > 0) {
|
|
166
172
|
return createResult({ id: 'cwv-cls-font-fallback', name: 'Font Fallback Metrics', category: 'performance', severity: 'info' }, 'info', 'Web fonts may cause layout shift during swap', {
|
|
@@ -177,7 +183,7 @@ export const cwvRules = [
|
|
|
177
183
|
},
|
|
178
184
|
});
|
|
179
185
|
}
|
|
180
|
-
return
|
|
186
|
+
return createResult({ id: 'cwv-cls-font-fallback', name: 'Font Fallback Metrics', category: 'performance', severity: 'info' }, 'pass', 'Font fallback metrics configured');
|
|
181
187
|
},
|
|
182
188
|
},
|
|
183
189
|
{
|
|
@@ -236,7 +242,7 @@ export const cwvRules = [
|
|
|
236
242
|
},
|
|
237
243
|
});
|
|
238
244
|
}
|
|
239
|
-
return
|
|
245
|
+
return createResult({ id: 'cwv-critical-css', name: 'Critical CSS', category: 'performance', severity: 'info' }, 'info', 'No render-blocking stylesheets to optimize', { recommendation: 'Page has no render-blocking CSS' });
|
|
240
246
|
},
|
|
241
247
|
},
|
|
242
248
|
];
|
|
@@ -7,10 +7,16 @@ export const ecommerceRules = [
|
|
|
7
7
|
severity: 'warning',
|
|
8
8
|
description: 'Product pages should have Product schema for rich snippets',
|
|
9
9
|
check: (ctx) => {
|
|
10
|
-
if (!ctx.
|
|
11
|
-
return
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
if (!ctx.isProductPage) {
|
|
11
|
+
return createResult({ id: 'ecommerce-product-schema', name: 'Product Schema', category: 'structured-data', severity: 'warning' }, 'info', 'Not a product page', {
|
|
12
|
+
recommendation: 'Add Product schema if this is a product page',
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
if (!ctx.jsonLdTypes) {
|
|
16
|
+
return createResult({ id: 'ecommerce-product-schema', name: 'Product Schema', category: 'structured-data', severity: 'warning' }, 'warn', 'Product page has no JSON-LD structured data', {
|
|
17
|
+
recommendation: 'Add Product structured data for rich snippets',
|
|
18
|
+
});
|
|
19
|
+
}
|
|
14
20
|
const hasProduct = ctx.jsonLdTypes.includes('Product');
|
|
15
21
|
if (!hasProduct) {
|
|
16
22
|
return createResult({ id: 'ecommerce-product-schema', name: 'Product Schema', category: 'structured-data', severity: 'warning' }, 'warn', 'Product page missing Product schema', {
|
|
@@ -42,8 +48,11 @@ export const ecommerceRules = [
|
|
|
42
48
|
severity: 'warning',
|
|
43
49
|
description: 'Product schema should include price information',
|
|
44
50
|
check: (ctx) => {
|
|
45
|
-
if (!ctx.productSchema)
|
|
46
|
-
return
|
|
51
|
+
if (!ctx.productSchema) {
|
|
52
|
+
return createResult({ id: 'ecommerce-product-price', name: 'Product Price', category: 'structured-data', severity: 'warning' }, 'info', 'Not applicable (no Product schema)', {
|
|
53
|
+
recommendation: 'Add Product schema first, then include price information',
|
|
54
|
+
});
|
|
55
|
+
}
|
|
47
56
|
const hasPrice = ctx.productSchema.offers?.price !== undefined ||
|
|
48
57
|
ctx.productSchema.offers?.lowPrice !== undefined;
|
|
49
58
|
const hasCurrency = ctx.productSchema.offers?.priceCurrency !== undefined;
|
|
@@ -75,8 +84,11 @@ export const ecommerceRules = [
|
|
|
75
84
|
severity: 'info',
|
|
76
85
|
description: 'Product schema should include availability status',
|
|
77
86
|
check: (ctx) => {
|
|
78
|
-
if (!ctx.productSchema?.offers)
|
|
79
|
-
return
|
|
87
|
+
if (!ctx.productSchema?.offers) {
|
|
88
|
+
return createResult({ id: 'ecommerce-product-availability', name: 'Product Availability', category: 'structured-data', severity: 'info' }, 'info', 'Not applicable (no Product offers schema)', {
|
|
89
|
+
recommendation: 'Add Product schema with offers to include availability',
|
|
90
|
+
});
|
|
91
|
+
}
|
|
80
92
|
const availability = ctx.productSchema.offers.availability;
|
|
81
93
|
if (!availability) {
|
|
82
94
|
return createResult({ id: 'ecommerce-product-availability', name: 'Product Availability', category: 'structured-data', severity: 'info' }, 'info', 'Product schema missing availability', {
|
|
@@ -99,8 +111,11 @@ export const ecommerceRules = [
|
|
|
99
111
|
severity: 'warning',
|
|
100
112
|
description: 'Product schema should include high-quality images',
|
|
101
113
|
check: (ctx) => {
|
|
102
|
-
if (!ctx.productSchema)
|
|
103
|
-
return
|
|
114
|
+
if (!ctx.productSchema) {
|
|
115
|
+
return createResult({ id: 'ecommerce-product-image', name: 'Product Image', category: 'structured-data', severity: 'warning' }, 'info', 'Not applicable (no Product schema)', {
|
|
116
|
+
recommendation: 'Add Product schema first, then include images',
|
|
117
|
+
});
|
|
118
|
+
}
|
|
104
119
|
const hasImage = ctx.productSchema.image !== undefined;
|
|
105
120
|
if (!hasImage) {
|
|
106
121
|
return createResult({ id: 'ecommerce-product-image', name: 'Product Image', category: 'structured-data', severity: 'warning' }, 'warn', 'Product schema missing image', {
|
|
@@ -124,8 +139,11 @@ export const ecommerceRules = [
|
|
|
124
139
|
severity: 'info',
|
|
125
140
|
description: 'Product schema can include aggregate ratings for star snippets',
|
|
126
141
|
check: (ctx) => {
|
|
127
|
-
if (!ctx.productSchema)
|
|
128
|
-
return
|
|
142
|
+
if (!ctx.productSchema) {
|
|
143
|
+
return createResult({ id: 'ecommerce-product-reviews', name: 'Product Reviews', category: 'structured-data', severity: 'info' }, 'info', 'Not applicable (no Product schema)', {
|
|
144
|
+
recommendation: 'Add Product schema first, then include reviews/ratings',
|
|
145
|
+
});
|
|
146
|
+
}
|
|
129
147
|
const hasRating = ctx.productSchema.aggregateRating !== undefined;
|
|
130
148
|
const hasReviews = ctx.productSchema.review !== undefined;
|
|
131
149
|
if (!hasRating && !hasReviews) {
|
|
@@ -155,8 +173,11 @@ export const ecommerceRules = [
|
|
|
155
173
|
severity: 'info',
|
|
156
174
|
description: 'Product schema should include brand information',
|
|
157
175
|
check: (ctx) => {
|
|
158
|
-
if (!ctx.productSchema)
|
|
159
|
-
return
|
|
176
|
+
if (!ctx.productSchema) {
|
|
177
|
+
return createResult({ id: 'ecommerce-product-brand', name: 'Product Brand', category: 'structured-data', severity: 'info' }, 'info', 'Not applicable (no Product schema)', {
|
|
178
|
+
recommendation: 'Add Product schema first, then include brand',
|
|
179
|
+
});
|
|
180
|
+
}
|
|
160
181
|
const hasBrand = ctx.productSchema.brand !== undefined;
|
|
161
182
|
if (!hasBrand) {
|
|
162
183
|
return createResult({ id: 'ecommerce-product-brand', name: 'Product Brand', category: 'structured-data', severity: 'info' }, 'info', 'Product schema missing brand', {
|
|
@@ -180,8 +201,11 @@ export const ecommerceRules = [
|
|
|
180
201
|
severity: 'info',
|
|
181
202
|
description: 'Product schema should include unique identifiers (SKU, GTIN, MPN)',
|
|
182
203
|
check: (ctx) => {
|
|
183
|
-
if (!ctx.productSchema)
|
|
184
|
-
return
|
|
204
|
+
if (!ctx.productSchema) {
|
|
205
|
+
return createResult({ id: 'ecommerce-product-sku', name: 'Product Identifiers', category: 'structured-data', severity: 'info' }, 'info', 'Not applicable (no Product schema)', {
|
|
206
|
+
recommendation: 'Add Product schema first, then include identifiers',
|
|
207
|
+
});
|
|
208
|
+
}
|
|
185
209
|
const hasSku = ctx.productSchema.sku !== undefined;
|
|
186
210
|
const hasGtin = ctx.productSchema.gtin !== undefined ||
|
|
187
211
|
ctx.productSchema.gtin13 !== undefined ||
|
|
@@ -215,11 +239,13 @@ export const ecommerceRules = [
|
|
|
215
239
|
severity: 'info',
|
|
216
240
|
description: 'Time-sensitive offers should have valid date ranges',
|
|
217
241
|
check: (ctx) => {
|
|
218
|
-
if (!ctx.productSchema?.offers)
|
|
219
|
-
return
|
|
242
|
+
if (!ctx.productSchema?.offers) {
|
|
243
|
+
return createResult({ id: 'ecommerce-offer-valid-dates', name: 'Offer Valid Dates', category: 'structured-data', severity: 'info' }, 'info', 'Not applicable (no Product offers schema)', {
|
|
244
|
+
recommendation: 'Add Product schema with offers to enable date validation',
|
|
245
|
+
});
|
|
246
|
+
}
|
|
220
247
|
const offers = ctx.productSchema.offers;
|
|
221
248
|
const priceValidUntil = offers.priceValidUntil;
|
|
222
|
-
const validFrom = offers.validFrom;
|
|
223
249
|
const validThrough = offers.validThrough;
|
|
224
250
|
if (priceValidUntil) {
|
|
225
251
|
const endDate = new Date(priceValidUntil);
|
|
@@ -246,7 +272,12 @@ export const ecommerceRules = [
|
|
|
246
272
|
});
|
|
247
273
|
}
|
|
248
274
|
}
|
|
249
|
-
|
|
275
|
+
if (priceValidUntil || validThrough) {
|
|
276
|
+
return createResult({ id: 'ecommerce-offer-valid-dates', name: 'Offer Valid Dates', category: 'structured-data', severity: 'info' }, 'pass', 'Offer dates are valid');
|
|
277
|
+
}
|
|
278
|
+
return createResult({ id: 'ecommerce-offer-valid-dates', name: 'Offer Valid Dates', category: 'structured-data', severity: 'info' }, 'info', 'No offer date constraints specified', {
|
|
279
|
+
recommendation: 'Consider adding priceValidUntil for time-limited offers',
|
|
280
|
+
});
|
|
250
281
|
},
|
|
251
282
|
},
|
|
252
283
|
];
|
package/dist/seo/rules/i18n.js
CHANGED
|
@@ -19,7 +19,9 @@ export const i18nRules = [
|
|
|
19
19
|
},
|
|
20
20
|
});
|
|
21
21
|
}
|
|
22
|
-
return
|
|
22
|
+
return createResult({ id: 'i18n-hreflang-exists', name: 'Hreflang Tags', category: 'technical', severity: 'warning' }, 'info', 'No hreflang tags found', {
|
|
23
|
+
recommendation: 'Consider adding hreflang tags if you have multi-language versions of this page',
|
|
24
|
+
});
|
|
23
25
|
}
|
|
24
26
|
return createResult({ id: 'i18n-hreflang-exists', name: 'Hreflang Tags', category: 'technical', severity: 'warning' }, 'pass', `${ctx.hreflangTags.length} hreflang tag(s) found`, { value: ctx.hreflangTags.length });
|
|
25
27
|
},
|
|
@@ -31,10 +33,14 @@ export const i18nRules = [
|
|
|
31
33
|
severity: 'warning',
|
|
32
34
|
description: 'Hreflang tags should include a self-referencing tag for the current page',
|
|
33
35
|
check: (ctx) => {
|
|
34
|
-
if (!ctx.hreflangTags || ctx.hreflangTags.length === 0)
|
|
35
|
-
return
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
if (!ctx.hreflangTags || ctx.hreflangTags.length === 0) {
|
|
37
|
+
return createResult({ id: 'i18n-hreflang-self', name: 'Hreflang Self-Reference', category: 'technical', severity: 'warning' }, 'info', 'Not applicable (no hreflang tags)', {
|
|
38
|
+
recommendation: 'Add hreflang tags first, then ensure self-reference is included',
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
if (!ctx.url) {
|
|
42
|
+
return createResult({ id: 'i18n-hreflang-self', name: 'Hreflang Self-Reference', category: 'technical', severity: 'warning' }, 'info', 'Cannot verify (URL not available)');
|
|
43
|
+
}
|
|
38
44
|
const currentUrl = ctx.url.toLowerCase().replace(/\/$/, '');
|
|
39
45
|
const hasSelfRef = ctx.hreflangTags.some((tag) => {
|
|
40
46
|
const href = tag.href?.toLowerCase().replace(/\/$/, '');
|
|
@@ -50,7 +56,7 @@ export const i18nRules = [
|
|
|
50
56
|
},
|
|
51
57
|
});
|
|
52
58
|
}
|
|
53
|
-
return
|
|
59
|
+
return createResult({ id: 'i18n-hreflang-self', name: 'Hreflang Self-Reference', category: 'technical', severity: 'warning' }, 'pass', 'Self-referencing hreflang tag present');
|
|
54
60
|
},
|
|
55
61
|
},
|
|
56
62
|
{
|
|
@@ -60,8 +66,11 @@ export const i18nRules = [
|
|
|
60
66
|
severity: 'info',
|
|
61
67
|
description: 'Include x-default hreflang for users outside defined regions',
|
|
62
68
|
check: (ctx) => {
|
|
63
|
-
if (!ctx.hreflangTags || ctx.hreflangTags.length < 2)
|
|
64
|
-
return
|
|
69
|
+
if (!ctx.hreflangTags || ctx.hreflangTags.length < 2) {
|
|
70
|
+
return createResult({ id: 'i18n-hreflang-x-default', name: 'Hreflang X-Default', category: 'technical', severity: 'info' }, 'info', 'Not applicable (need 2+ hreflang tags for x-default)', {
|
|
71
|
+
recommendation: 'Add multiple hreflang tags to support international visitors',
|
|
72
|
+
});
|
|
73
|
+
}
|
|
65
74
|
const hasXDefault = ctx.hreflangTags.some((tag) => tag.lang === 'x-default');
|
|
66
75
|
if (!hasXDefault) {
|
|
67
76
|
return createResult({ id: 'i18n-hreflang-x-default', name: 'Hreflang X-Default', category: 'technical', severity: 'info' }, 'info', 'No x-default hreflang tag found', {
|
|
@@ -82,8 +91,9 @@ export const i18nRules = [
|
|
|
82
91
|
severity: 'warning',
|
|
83
92
|
description: 'Hreflang language codes must be valid ISO 639-1 codes',
|
|
84
93
|
check: (ctx) => {
|
|
85
|
-
if (!ctx.hreflangTags || ctx.hreflangTags.length === 0)
|
|
86
|
-
return
|
|
94
|
+
if (!ctx.hreflangTags || ctx.hreflangTags.length === 0) {
|
|
95
|
+
return createResult({ id: 'i18n-hreflang-valid-codes', name: 'Hreflang Valid Codes', category: 'technical', severity: 'warning' }, 'info', 'Not applicable (no hreflang tags)');
|
|
96
|
+
}
|
|
87
97
|
const validLanguageCodes = new Set([
|
|
88
98
|
'aa', 'ab', 'af', 'ak', 'sq', 'am', 'ar', 'an', 'hy', 'as', 'av', 'ae', 'ay', 'az',
|
|
89
99
|
'ba', 'bm', 'eu', 'be', 'bn', 'bh', 'bi', 'bo', 'bs', 'br', 'bg', 'my', 'ca', 'cs',
|
|
@@ -117,7 +127,7 @@ export const i18nRules = [
|
|
|
117
127
|
},
|
|
118
128
|
});
|
|
119
129
|
}
|
|
120
|
-
return
|
|
130
|
+
return createResult({ id: 'i18n-hreflang-valid-codes', name: 'Hreflang Valid Codes', category: 'technical', severity: 'warning' }, 'pass', 'All hreflang codes are valid ISO 639-1');
|
|
121
131
|
},
|
|
122
132
|
},
|
|
123
133
|
{
|
|
@@ -127,8 +137,11 @@ export const i18nRules = [
|
|
|
127
137
|
severity: 'warning',
|
|
128
138
|
description: 'All hreflang URLs should return links back to this page (bidirectional)',
|
|
129
139
|
check: (ctx) => {
|
|
130
|
-
if (!ctx.hreflangTags || ctx.hreflangTags.length < 2)
|
|
131
|
-
return
|
|
140
|
+
if (!ctx.hreflangTags || ctx.hreflangTags.length < 2) {
|
|
141
|
+
return createResult({ id: 'i18n-hreflang-return-links', name: 'Hreflang Return Links', category: 'technical', severity: 'warning' }, 'info', 'Not applicable (need 2+ hreflang tags)', {
|
|
142
|
+
recommendation: 'Add hreflang tags to enable return link verification',
|
|
143
|
+
});
|
|
144
|
+
}
|
|
132
145
|
return createResult({ id: 'i18n-hreflang-return-links', name: 'Hreflang Return Links', category: 'technical', severity: 'warning' }, 'info', 'Hreflang return links cannot be verified from HTML alone', {
|
|
133
146
|
recommendation: 'Ensure all alternate pages link back to this page with matching hreflang tags',
|
|
134
147
|
evidence: {
|
|
@@ -145,19 +158,17 @@ export const i18nRules = [
|
|
|
145
158
|
severity: 'info',
|
|
146
159
|
description: 'Content-Language header can indicate the language of the document',
|
|
147
160
|
check: (ctx) => {
|
|
148
|
-
if (!ctx.responseHeaders)
|
|
149
|
-
return
|
|
161
|
+
if (!ctx.responseHeaders) {
|
|
162
|
+
return createResult({ id: 'i18n-content-language', name: 'Content-Language Header', category: 'technical', severity: 'info' }, 'info', 'Cannot verify (response headers not available)');
|
|
163
|
+
}
|
|
150
164
|
const contentLang = ctx.responseHeaders['content-language'] || ctx.responseHeaders['Content-Language'];
|
|
151
165
|
if (!contentLang) {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
return null;
|
|
166
|
+
return createResult({ id: 'i18n-content-language', name: 'Content-Language Header', category: 'technical', severity: 'info' }, 'info', 'Content-Language header not set', {
|
|
167
|
+
recommendation: `Consider adding Content-Language: ${ctx.langValue || 'en'} header`,
|
|
168
|
+
evidence: {
|
|
169
|
+
impact: 'While not critical for SEO, it helps with content negotiation',
|
|
170
|
+
},
|
|
171
|
+
});
|
|
161
172
|
}
|
|
162
173
|
if (ctx.langValue) {
|
|
163
174
|
const headerLang = Array.isArray(contentLang) ? contentLang[0] : contentLang;
|
|
@@ -179,8 +190,18 @@ export const i18nRules = [
|
|
|
179
190
|
severity: 'warning',
|
|
180
191
|
description: 'HTML lang attribute should match the og:locale if present',
|
|
181
192
|
check: (ctx) => {
|
|
182
|
-
if (!ctx.hasLang
|
|
183
|
-
return
|
|
193
|
+
if (!ctx.hasLang && !ctx.ogLocale) {
|
|
194
|
+
return createResult({ id: 'i18n-lang-consistency', name: 'Language Consistency', category: 'technical', severity: 'warning' }, 'info', 'No lang attribute or og:locale to compare', {
|
|
195
|
+
recommendation: 'Add html lang attribute and og:locale for language consistency',
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
if (!ctx.hasLang || !ctx.ogLocale) {
|
|
199
|
+
return createResult({ id: 'i18n-lang-consistency', name: 'Language Consistency', category: 'technical', severity: 'warning' }, 'info', ctx.hasLang ? 'No og:locale to compare with html lang' : 'No html lang to compare with og:locale', {
|
|
200
|
+
recommendation: ctx.hasLang
|
|
201
|
+
? 'Add og:locale meta tag for social platforms'
|
|
202
|
+
: 'Add html lang attribute for language declaration',
|
|
203
|
+
});
|
|
204
|
+
}
|
|
184
205
|
const htmlLang = ctx.langValue?.toLowerCase().split('-')[0];
|
|
185
206
|
const ogLocaleLang = ctx.ogLocale.toLowerCase().split('_')[0];
|
|
186
207
|
if (htmlLang !== ogLocaleLang) {
|
|
@@ -192,7 +213,7 @@ export const i18nRules = [
|
|
|
192
213
|
},
|
|
193
214
|
});
|
|
194
215
|
}
|
|
195
|
-
return
|
|
216
|
+
return createResult({ id: 'i18n-lang-consistency', name: 'Language Consistency', category: 'technical', severity: 'warning' }, 'pass', `Language consistent: html lang="${ctx.langValue}", og:locale="${ctx.ogLocale}"`);
|
|
196
217
|
},
|
|
197
218
|
},
|
|
198
219
|
{
|
|
@@ -202,8 +223,11 @@ export const i18nRules = [
|
|
|
202
223
|
severity: 'info',
|
|
203
224
|
description: 'Consider using region-specific language codes for better targeting',
|
|
204
225
|
check: (ctx) => {
|
|
205
|
-
if (!ctx.hasLang || !ctx.langValue)
|
|
206
|
-
return
|
|
226
|
+
if (!ctx.hasLang || !ctx.langValue) {
|
|
227
|
+
return createResult({ id: 'i18n-lang-region', name: 'Language Region Specificity', category: 'technical', severity: 'info' }, 'info', 'No lang attribute to analyze', {
|
|
228
|
+
recommendation: 'Add html lang attribute first',
|
|
229
|
+
});
|
|
230
|
+
}
|
|
207
231
|
const multiRegionalLangs = ['en', 'es', 'pt', 'zh', 'fr', 'de', 'ar'];
|
|
208
232
|
const langPrimary = ctx.langValue.toLowerCase().split('-')[0];
|
|
209
233
|
if (multiRegionalLangs.includes(langPrimary) && !ctx.langValue.includes('-')) {
|
|
@@ -216,7 +240,7 @@ export const i18nRules = [
|
|
|
216
240
|
},
|
|
217
241
|
});
|
|
218
242
|
}
|
|
219
|
-
return
|
|
243
|
+
return createResult({ id: 'i18n-lang-region', name: 'Language Region Specificity', category: 'technical', severity: 'info' }, 'pass', `Lang attribute: ${ctx.langValue}`);
|
|
220
244
|
},
|
|
221
245
|
},
|
|
222
246
|
{
|
|
@@ -226,8 +250,12 @@ export const i18nRules = [
|
|
|
226
250
|
severity: 'warning',
|
|
227
251
|
description: 'Hreflang language should match page content language',
|
|
228
252
|
check: (ctx) => {
|
|
229
|
-
if (!ctx.hreflangTags ||
|
|
230
|
-
return
|
|
253
|
+
if (!ctx.hreflangTags || ctx.hreflangTags.length === 0) {
|
|
254
|
+
return createResult({ id: 'hreflang-language-mismatch', name: 'Hreflang Language Mismatch', category: 'technical', severity: 'warning' }, 'info', 'Not applicable (no hreflang tags)');
|
|
255
|
+
}
|
|
256
|
+
if (!ctx.detectedLanguage) {
|
|
257
|
+
return createResult({ id: 'hreflang-language-mismatch', name: 'Hreflang Language Mismatch', category: 'technical', severity: 'warning' }, 'info', 'Cannot verify (language detection not available)');
|
|
258
|
+
}
|
|
231
259
|
const selfHreflang = ctx.hreflangTags.find(tag => tag.href === ctx.url || tag.href === ctx.canonicalUrl);
|
|
232
260
|
if (selfHreflang && ctx.detectedLanguage) {
|
|
233
261
|
const hreflangLang = selfHreflang.lang.split('-')[0].toLowerCase();
|
|
@@ -243,7 +271,7 @@ export const i18nRules = [
|
|
|
243
271
|
});
|
|
244
272
|
}
|
|
245
273
|
}
|
|
246
|
-
return
|
|
274
|
+
return createResult({ id: 'hreflang-language-mismatch', name: 'Hreflang Language Mismatch', category: 'technical', severity: 'warning' }, 'pass', 'Hreflang language matches detected content language');
|
|
247
275
|
},
|
|
248
276
|
},
|
|
249
277
|
];
|