recker 1.0.43 → 1.0.44
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 +85152 -100207
- 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 +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 +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/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 +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 -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 +2 -0
- 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/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.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 +12 -6
- package/dist/browser/utils/optional-require.d.ts +0 -19
- package/dist/browser/utils/optional-require.js +0 -105
package/dist/seo/rules/crawl.js
CHANGED
|
@@ -31,8 +31,9 @@ export const crawlRules = [
|
|
|
31
31
|
severity: 'error',
|
|
32
32
|
description: 'Check if page is blocked from indexing',
|
|
33
33
|
check: (ctx) => {
|
|
34
|
-
if (!ctx.metaRobots)
|
|
35
|
-
return
|
|
34
|
+
if (!ctx.metaRobots) {
|
|
35
|
+
return createResult({ id: 'crawl-robots-noindex', name: 'Robots Noindex', category: 'technical', severity: 'error' }, 'info', 'Not applicable (robots meta tag not present)', { recommendation: 'This rule checks for noindex directives when robots meta tags are present' });
|
|
36
|
+
}
|
|
36
37
|
const hasNoindex = ctx.metaRobots.some(r => r.toLowerCase().includes('noindex'));
|
|
37
38
|
if (hasNoindex) {
|
|
38
39
|
return createResult({ id: 'crawl-robots-noindex', name: 'Robots Noindex', category: 'technical', severity: 'error' }, 'fail', 'Page is set to noindex', {
|
|
@@ -43,7 +44,7 @@ export const crawlRules = [
|
|
|
43
44
|
},
|
|
44
45
|
});
|
|
45
46
|
}
|
|
46
|
-
return
|
|
47
|
+
return createResult({ id: 'crawl-robots-noindex', name: 'Robots Noindex', category: 'technical', severity: 'error' }, 'info', 'Not applicable (noindex not detected)', { recommendation: 'This rule checks for noindex directives that would prevent page indexing' });
|
|
47
48
|
},
|
|
48
49
|
},
|
|
49
50
|
{
|
|
@@ -53,8 +54,9 @@ export const crawlRules = [
|
|
|
53
54
|
severity: 'warning',
|
|
54
55
|
description: 'Check if page links are blocked from following',
|
|
55
56
|
check: (ctx) => {
|
|
56
|
-
if (!ctx.metaRobots)
|
|
57
|
-
return
|
|
57
|
+
if (!ctx.metaRobots) {
|
|
58
|
+
return createResult({ id: 'crawl-robots-nofollow', name: 'Robots Nofollow', category: 'technical', severity: 'warning' }, 'info', 'Not applicable (robots meta tag not present)', { recommendation: 'This rule checks for nofollow directives when robots meta tags are present' });
|
|
59
|
+
}
|
|
58
60
|
const hasNofollow = ctx.metaRobots.some(r => r.toLowerCase().includes('nofollow'));
|
|
59
61
|
if (hasNofollow) {
|
|
60
62
|
return createResult({ id: 'crawl-robots-nofollow', name: 'Robots Nofollow', category: 'technical', severity: 'warning' }, 'warn', 'Page has nofollow directive', {
|
|
@@ -65,7 +67,7 @@ export const crawlRules = [
|
|
|
65
67
|
},
|
|
66
68
|
});
|
|
67
69
|
}
|
|
68
|
-
return
|
|
70
|
+
return createResult({ id: 'crawl-robots-nofollow', name: 'Robots Nofollow', category: 'technical', severity: 'warning' }, 'info', 'Not applicable (nofollow not detected)', { recommendation: 'This rule checks for nofollow directives that would prevent link following' });
|
|
69
71
|
},
|
|
70
72
|
},
|
|
71
73
|
{
|
|
@@ -105,12 +107,14 @@ export const crawlRules = [
|
|
|
105
107
|
severity: 'warning',
|
|
106
108
|
description: 'Check X-Robots-Tag HTTP header for indexing directives',
|
|
107
109
|
check: (ctx) => {
|
|
108
|
-
if (!ctx.responseHeaders)
|
|
109
|
-
return
|
|
110
|
+
if (!ctx.responseHeaders) {
|
|
111
|
+
return createResult({ id: 'crawl-x-robots-tag', name: 'X-Robots-Tag Header', category: 'technical', severity: 'warning' }, 'info', 'Not applicable (response headers unavailable)', { recommendation: 'This rule checks for X-Robots-Tag headers when HTTP response headers are available' });
|
|
112
|
+
}
|
|
110
113
|
const xRobotsTag = ctx.responseHeaders['x-robots-tag'] ||
|
|
111
114
|
ctx.responseHeaders['X-Robots-Tag'];
|
|
112
|
-
if (!xRobotsTag)
|
|
113
|
-
return
|
|
115
|
+
if (!xRobotsTag) {
|
|
116
|
+
return createResult({ id: 'crawl-x-robots-tag', name: 'X-Robots-Tag Header', category: 'technical', severity: 'warning' }, 'info', 'Not applicable (X-Robots-Tag header not present)', { recommendation: 'This rule checks for X-Robots-Tag headers when the header is present' });
|
|
117
|
+
}
|
|
114
118
|
const tagValue = Array.isArray(xRobotsTag) ? xRobotsTag.join(', ') : xRobotsTag;
|
|
115
119
|
if (tagValue.toLowerCase().includes('noindex')) {
|
|
116
120
|
return createResult({ id: 'crawl-x-robots-tag', name: 'X-Robots-Tag Header', category: 'technical', severity: 'warning' }, 'fail', 'X-Robots-Tag contains noindex', {
|
|
@@ -151,8 +155,9 @@ export const crawlRules = [
|
|
|
151
155
|
severity: 'info',
|
|
152
156
|
description: 'Canonical should point to the current page or explicit alternate',
|
|
153
157
|
check: (ctx) => {
|
|
154
|
-
if (!ctx.hasCanonical || !ctx.canonicalUrl || !ctx.url)
|
|
155
|
-
return
|
|
158
|
+
if (!ctx.hasCanonical || !ctx.canonicalUrl || !ctx.url) {
|
|
159
|
+
return createResult({ id: 'crawl-canonical-self', name: 'Canonical Self-Reference', category: 'technical', severity: 'info' }, 'info', 'Not applicable (canonical URL or page URL unavailable)', { recommendation: 'This rule checks canonical self-reference when canonical and page URL information is available' });
|
|
160
|
+
}
|
|
156
161
|
const normalizeUrl = (url) => {
|
|
157
162
|
try {
|
|
158
163
|
const u = new URL(url);
|
|
@@ -174,7 +179,7 @@ export const crawlRules = [
|
|
|
174
179
|
},
|
|
175
180
|
});
|
|
176
181
|
}
|
|
177
|
-
return
|
|
182
|
+
return createResult({ id: 'crawl-canonical-self', name: 'Canonical Self-Reference', category: 'technical', severity: 'info' }, 'info', 'Not applicable (canonical is self-referencing)', { recommendation: 'This rule checks for non-self-referencing canonicals which may indicate URL consolidation' });
|
|
178
183
|
},
|
|
179
184
|
},
|
|
180
185
|
{
|
|
@@ -184,8 +189,9 @@ export const crawlRules = [
|
|
|
184
189
|
severity: 'warning',
|
|
185
190
|
description: 'Canonical URL should be absolute, not relative',
|
|
186
191
|
check: (ctx) => {
|
|
187
|
-
if (!ctx.canonicalUrl)
|
|
188
|
-
return
|
|
192
|
+
if (!ctx.canonicalUrl) {
|
|
193
|
+
return createResult({ id: 'crawl-canonical-absolute', name: 'Canonical Absolute URL', category: 'technical', severity: 'warning' }, 'info', 'Not applicable (canonical URL not present)', { recommendation: 'This rule checks canonical URL format when a canonical URL exists' });
|
|
194
|
+
}
|
|
189
195
|
const isAbsolute = ctx.canonicalUrl.startsWith('http://') ||
|
|
190
196
|
ctx.canonicalUrl.startsWith('https://');
|
|
191
197
|
if (!isAbsolute) {
|
|
@@ -197,7 +203,7 @@ export const crawlRules = [
|
|
|
197
203
|
},
|
|
198
204
|
});
|
|
199
205
|
}
|
|
200
|
-
return
|
|
206
|
+
return createResult({ id: 'crawl-canonical-absolute', name: 'Canonical Absolute URL', category: 'technical', severity: 'warning' }, 'info', 'Not applicable (canonical URL is absolute)', { recommendation: 'This rule checks for relative canonical URLs which should be avoided' });
|
|
201
207
|
},
|
|
202
208
|
},
|
|
203
209
|
{
|
|
@@ -207,8 +213,9 @@ export const crawlRules = [
|
|
|
207
213
|
severity: 'warning',
|
|
208
214
|
description: 'Canonical URL should use HTTPS',
|
|
209
215
|
check: (ctx) => {
|
|
210
|
-
if (!ctx.canonicalUrl)
|
|
211
|
-
return
|
|
216
|
+
if (!ctx.canonicalUrl) {
|
|
217
|
+
return createResult({ id: 'crawl-canonical-https', name: 'Canonical HTTPS', category: 'technical', severity: 'warning' }, 'info', 'Not applicable (canonical URL not present)', { recommendation: 'This rule checks canonical URL protocol when a canonical URL exists' });
|
|
218
|
+
}
|
|
212
219
|
if (ctx.canonicalUrl.startsWith('http://')) {
|
|
213
220
|
return createResult({ id: 'crawl-canonical-https', name: 'Canonical HTTPS', category: 'technical', severity: 'warning' }, 'warn', 'Canonical URL uses HTTP instead of HTTPS', {
|
|
214
221
|
evidence: {
|
|
@@ -218,7 +225,7 @@ export const crawlRules = [
|
|
|
218
225
|
},
|
|
219
226
|
});
|
|
220
227
|
}
|
|
221
|
-
return
|
|
228
|
+
return createResult({ id: 'crawl-canonical-https', name: 'Canonical HTTPS', category: 'technical', severity: 'warning' }, 'info', 'Not applicable (canonical URL uses HTTPS)', { recommendation: 'This rule checks for HTTP canonical URLs which should use HTTPS instead' });
|
|
222
229
|
},
|
|
223
230
|
},
|
|
224
231
|
{
|
|
@@ -228,8 +235,9 @@ export const crawlRules = [
|
|
|
228
235
|
severity: 'info',
|
|
229
236
|
description: 'URLs with tracking parameters should have proper canonical',
|
|
230
237
|
check: (ctx) => {
|
|
231
|
-
if (!ctx.url)
|
|
232
|
-
return
|
|
238
|
+
if (!ctx.url) {
|
|
239
|
+
return createResult({ id: 'crawl-url-parameters', name: 'URL Parameters', category: 'technical', severity: 'info' }, 'info', 'Not applicable (page URL unavailable)', { recommendation: 'This rule checks for tracking parameters when page URL information is available' });
|
|
240
|
+
}
|
|
233
241
|
try {
|
|
234
242
|
const url = new URL(ctx.url);
|
|
235
243
|
const trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'fbclid', 'gclid', 'ref'];
|
|
@@ -247,7 +255,7 @@ export const crawlRules = [
|
|
|
247
255
|
}
|
|
248
256
|
catch {
|
|
249
257
|
}
|
|
250
|
-
return
|
|
258
|
+
return createResult({ id: 'crawl-url-parameters', name: 'URL Parameters', category: 'technical', severity: 'info' }, 'info', 'Not applicable (no tracking parameters detected or canonical is present)', { recommendation: 'This rule checks for tracking parameters without canonical tags' });
|
|
251
259
|
},
|
|
252
260
|
},
|
|
253
261
|
{
|
|
@@ -257,8 +265,9 @@ export const crawlRules = [
|
|
|
257
265
|
severity: 'info',
|
|
258
266
|
description: 'Paginated content should use proper rel attributes',
|
|
259
267
|
check: (ctx) => {
|
|
260
|
-
if (!ctx.isPaginatedPage)
|
|
261
|
-
return
|
|
268
|
+
if (!ctx.isPaginatedPage) {
|
|
269
|
+
return createResult({ id: 'crawl-pagination-rel', name: 'Pagination Links', category: 'technical', severity: 'info' }, 'info', 'Not applicable (page is not paginated)', { recommendation: 'This rule checks pagination links on paginated pages only' });
|
|
270
|
+
}
|
|
262
271
|
const hasPrevNext = ctx.hasRelPrev || ctx.hasRelNext;
|
|
263
272
|
if (!hasPrevNext) {
|
|
264
273
|
return createResult({ id: 'crawl-pagination-rel', name: 'Pagination Links', category: 'technical', severity: 'info' }, 'info', 'Paginated page missing rel="prev/next" (deprecated but still useful)', {
|
|
@@ -280,8 +289,9 @@ export const crawlRules = [
|
|
|
280
289
|
severity: 'info',
|
|
281
290
|
description: 'Check for noarchive and nocache directives',
|
|
282
291
|
check: (ctx) => {
|
|
283
|
-
if (!ctx.metaRobots)
|
|
284
|
-
return
|
|
292
|
+
if (!ctx.metaRobots) {
|
|
293
|
+
return createResult({ id: 'crawl-noarchive', name: 'Cache Directives', category: 'technical', severity: 'info' }, 'info', 'Not applicable (robots meta tag not present)', { recommendation: 'This rule checks for cache directives when robots meta tags are present' });
|
|
294
|
+
}
|
|
285
295
|
const directives = ctx.metaRobots.join(', ').toLowerCase();
|
|
286
296
|
const hasNoarchive = directives.includes('noarchive');
|
|
287
297
|
const hasNocache = directives.includes('nocache');
|
|
@@ -301,7 +311,7 @@ export const crawlRules = [
|
|
|
301
311
|
},
|
|
302
312
|
});
|
|
303
313
|
}
|
|
304
|
-
return
|
|
314
|
+
return createResult({ id: 'crawl-noarchive', name: 'Cache Directives', category: 'technical', severity: 'info' }, 'info', 'Not applicable (no cache restriction directives present)', { recommendation: 'This rule checks for cache restriction directives in robots meta tags' });
|
|
305
315
|
},
|
|
306
316
|
},
|
|
307
317
|
{
|
|
@@ -311,8 +321,9 @@ export const crawlRules = [
|
|
|
311
321
|
severity: 'info',
|
|
312
322
|
description: 'Website should have a robots.txt file',
|
|
313
323
|
check: (ctx) => {
|
|
314
|
-
if (ctx.robotsTxtExists === undefined)
|
|
315
|
-
return
|
|
324
|
+
if (ctx.robotsTxtExists === undefined) {
|
|
325
|
+
return createResult({ id: 'robots-txt-exists', name: 'robots.txt Exists', category: 'crawlability', severity: 'info' }, 'info', 'Not applicable (robots.txt status unavailable)', { recommendation: 'This rule checks for robots.txt file when robots.txt information is available' });
|
|
326
|
+
}
|
|
316
327
|
if (!ctx.robotsTxtExists) {
|
|
317
328
|
return createResult({ id: 'robots-txt-exists', name: 'robots.txt Exists', category: 'crawlability', severity: 'info' }, 'info', 'No robots.txt file found', {
|
|
318
329
|
recommendation: 'Create a robots.txt file to control search engine crawling',
|
|
@@ -333,8 +344,9 @@ export const crawlRules = [
|
|
|
333
344
|
severity: 'warning',
|
|
334
345
|
description: 'robots.txt should reference sitemap.xml location',
|
|
335
346
|
check: (ctx) => {
|
|
336
|
-
if (ctx.robotsTxtHasSitemap === undefined)
|
|
337
|
-
return
|
|
347
|
+
if (ctx.robotsTxtHasSitemap === undefined) {
|
|
348
|
+
return createResult({ id: 'sitemap-in-robots', name: 'Sitemap Reference in robots.txt', category: 'crawlability', severity: 'warning' }, 'info', 'Not applicable (robots.txt sitemap reference status unavailable)', { recommendation: 'This rule checks for sitemap references in robots.txt when robots.txt information is available' });
|
|
349
|
+
}
|
|
338
350
|
if (!ctx.robotsTxtHasSitemap) {
|
|
339
351
|
return createResult({ id: 'sitemap-in-robots', name: 'Sitemap Reference in robots.txt', category: 'crawlability', severity: 'warning' }, 'warn', 'robots.txt does not reference sitemap.xml', {
|
|
340
352
|
recommendation: 'Add sitemap location to robots.txt',
|
|
@@ -355,8 +367,9 @@ export const crawlRules = [
|
|
|
355
367
|
severity: 'warning',
|
|
356
368
|
description: 'CSS/JS resources should not be blocked by robots.txt',
|
|
357
369
|
check: (ctx) => {
|
|
358
|
-
if (ctx.blockedResources === undefined)
|
|
359
|
-
return
|
|
370
|
+
if (ctx.blockedResources === undefined) {
|
|
371
|
+
return createResult({ id: 'blocked-resources', name: 'Blocked Resources', category: 'crawlability', severity: 'warning' }, 'info', 'Not applicable (blocked resources information unavailable)', { recommendation: 'This rule checks for blocked resources when resource blocking information is available' });
|
|
372
|
+
}
|
|
360
373
|
if (ctx.blockedResources > 0) {
|
|
361
374
|
return createResult({ id: 'blocked-resources', name: 'Blocked Resources', category: 'crawlability', severity: 'warning' }, 'warn', `${ctx.blockedResources} resources blocked by robots.txt`, {
|
|
362
375
|
value: ctx.blockedResources,
|
|
@@ -369,7 +382,7 @@ export const crawlRules = [
|
|
|
369
382
|
}
|
|
370
383
|
});
|
|
371
384
|
}
|
|
372
|
-
return
|
|
385
|
+
return createResult({ id: 'blocked-resources', name: 'Blocked Resources', category: 'crawlability', severity: 'warning' }, 'info', 'Not applicable (no resources blocked by robots.txt)', { recommendation: 'This rule checks for CSS/JS resources blocked by robots.txt' });
|
|
373
386
|
},
|
|
374
387
|
},
|
|
375
388
|
{
|
|
@@ -379,8 +392,9 @@ export const crawlRules = [
|
|
|
379
392
|
severity: 'warning',
|
|
380
393
|
description: 'X-Robots-Tag should not block important pages',
|
|
381
394
|
check: (ctx) => {
|
|
382
|
-
if (!ctx.xRobotsTag)
|
|
383
|
-
return
|
|
395
|
+
if (!ctx.xRobotsTag) {
|
|
396
|
+
return createResult({ id: 'x-robots-tag-noindex', name: 'X-Robots-Tag Noindex', category: 'crawlability', severity: 'warning' }, 'info', 'Not applicable (X-Robots-Tag header not present)', { recommendation: 'This rule checks X-Robots-Tag for noindex directives when the header is present' });
|
|
397
|
+
}
|
|
384
398
|
const tag = ctx.xRobotsTag.toLowerCase();
|
|
385
399
|
if (tag.includes('noindex')) {
|
|
386
400
|
return createResult({ id: 'x-robots-tag-noindex', name: 'X-Robots-Tag Noindex', category: 'crawlability', severity: 'warning' }, 'warn', 'Page blocked by X-Robots-Tag: noindex', {
|
|
@@ -392,7 +406,7 @@ export const crawlRules = [
|
|
|
392
406
|
}
|
|
393
407
|
});
|
|
394
408
|
}
|
|
395
|
-
return
|
|
409
|
+
return createResult({ id: 'x-robots-tag-noindex', name: 'X-Robots-Tag Noindex', category: 'crawlability', severity: 'warning' }, 'info', 'Not applicable (X-Robots-Tag does not contain noindex)', { recommendation: 'This rule checks for noindex in X-Robots-Tag header' });
|
|
396
410
|
},
|
|
397
411
|
},
|
|
398
412
|
{
|
|
@@ -402,8 +416,9 @@ export const crawlRules = [
|
|
|
402
416
|
severity: 'info',
|
|
403
417
|
description: 'External resources blocked by robots.txt may affect rendering',
|
|
404
418
|
check: (ctx) => {
|
|
405
|
-
if (ctx.blockedExternalResources === undefined)
|
|
406
|
-
return
|
|
419
|
+
if (ctx.blockedExternalResources === undefined) {
|
|
420
|
+
return createResult({ id: 'blocked-external-resources', name: 'Blocked External Resources', category: 'crawlability', severity: 'info' }, 'info', 'Not applicable (blocked external resources information unavailable)', { recommendation: 'This rule checks for blocked external resources when resource blocking information is available' });
|
|
421
|
+
}
|
|
407
422
|
if (ctx.blockedExternalResources > 0) {
|
|
408
423
|
return createResult({ id: 'blocked-external-resources', name: 'Blocked External Resources', category: 'crawlability', severity: 'info' }, 'info', `${ctx.blockedExternalResources} external resources blocked by robots.txt`, {
|
|
409
424
|
value: ctx.blockedExternalResources,
|
|
@@ -414,7 +429,7 @@ export const crawlRules = [
|
|
|
414
429
|
}
|
|
415
430
|
});
|
|
416
431
|
}
|
|
417
|
-
return
|
|
432
|
+
return createResult({ id: 'blocked-external-resources', name: 'Blocked External Resources', category: 'crawlability', severity: 'info' }, 'info', 'Not applicable (no external resources blocked by robots.txt)', { recommendation: 'This rule checks for external resources blocked by robots.txt' });
|
|
418
433
|
},
|
|
419
434
|
},
|
|
420
435
|
];
|
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,34 @@ 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
|
+
evidence: {
|
|
19
|
+
found: 'No JSON-LD structured data',
|
|
20
|
+
expected: 'Product schema with name, image, price, availability',
|
|
21
|
+
impact: 'Missing product schema prevents rich snippets in search results',
|
|
22
|
+
example: `<script type="application/ld+json">
|
|
23
|
+
{
|
|
24
|
+
"@context": "https://schema.org",
|
|
25
|
+
"@type": "Product",
|
|
26
|
+
"name": "Product Name",
|
|
27
|
+
"image": "https://example.com/image.jpg",
|
|
28
|
+
"offers": {
|
|
29
|
+
"@type": "Offer",
|
|
30
|
+
"price": "99.99",
|
|
31
|
+
"priceCurrency": "USD"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
</script>`,
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
}
|
|
14
38
|
const hasProduct = ctx.jsonLdTypes.includes('Product');
|
|
15
39
|
if (!hasProduct) {
|
|
16
40
|
return createResult({ id: 'ecommerce-product-schema', name: 'Product Schema', category: 'structured-data', severity: 'warning' }, 'warn', 'Product page missing Product schema', {
|
|
@@ -42,8 +66,11 @@ export const ecommerceRules = [
|
|
|
42
66
|
severity: 'warning',
|
|
43
67
|
description: 'Product schema should include price information',
|
|
44
68
|
check: (ctx) => {
|
|
45
|
-
if (!ctx.productSchema)
|
|
46
|
-
return
|
|
69
|
+
if (!ctx.productSchema) {
|
|
70
|
+
return createResult({ id: 'ecommerce-product-price', name: 'Product Price', category: 'structured-data', severity: 'warning' }, 'info', 'Not applicable (no Product schema)', {
|
|
71
|
+
recommendation: 'Add Product schema first, then include price information',
|
|
72
|
+
});
|
|
73
|
+
}
|
|
47
74
|
const hasPrice = ctx.productSchema.offers?.price !== undefined ||
|
|
48
75
|
ctx.productSchema.offers?.lowPrice !== undefined;
|
|
49
76
|
const hasCurrency = ctx.productSchema.offers?.priceCurrency !== undefined;
|
|
@@ -51,8 +78,10 @@ export const ecommerceRules = [
|
|
|
51
78
|
return createResult({ id: 'ecommerce-product-price', name: 'Product Price', category: 'structured-data', severity: 'warning' }, 'warn', 'Product schema missing price', {
|
|
52
79
|
recommendation: 'Add price to Product offers for price display in search results',
|
|
53
80
|
evidence: {
|
|
81
|
+
found: 'Product offers without price',
|
|
54
82
|
expected: 'offers.price or offers.lowPrice with priceCurrency',
|
|
55
83
|
impact: 'Products without price may not show in Google Shopping results',
|
|
84
|
+
example: '"offers": {\n "@type": "Offer",\n "price": "99.99",\n "priceCurrency": "USD"\n}',
|
|
56
85
|
},
|
|
57
86
|
});
|
|
58
87
|
}
|
|
@@ -62,6 +91,8 @@ export const ecommerceRules = [
|
|
|
62
91
|
evidence: {
|
|
63
92
|
found: `price: ${ctx.productSchema.offers?.price}`,
|
|
64
93
|
expected: 'priceCurrency: "USD" or similar ISO 4217 code',
|
|
94
|
+
impact: 'Currency is required for Google Shopping and price display in search results',
|
|
95
|
+
example: '"priceCurrency": "USD"',
|
|
65
96
|
},
|
|
66
97
|
});
|
|
67
98
|
}
|
|
@@ -75,8 +106,11 @@ export const ecommerceRules = [
|
|
|
75
106
|
severity: 'info',
|
|
76
107
|
description: 'Product schema should include availability status',
|
|
77
108
|
check: (ctx) => {
|
|
78
|
-
if (!ctx.productSchema?.offers)
|
|
79
|
-
return
|
|
109
|
+
if (!ctx.productSchema?.offers) {
|
|
110
|
+
return createResult({ id: 'ecommerce-product-availability', name: 'Product Availability', category: 'structured-data', severity: 'info' }, 'info', 'Not applicable (no Product offers schema)', {
|
|
111
|
+
recommendation: 'Add Product schema with offers to include availability',
|
|
112
|
+
});
|
|
113
|
+
}
|
|
80
114
|
const availability = ctx.productSchema.offers.availability;
|
|
81
115
|
if (!availability) {
|
|
82
116
|
return createResult({ id: 'ecommerce-product-availability', name: 'Product Availability', category: 'structured-data', severity: 'info' }, 'info', 'Product schema missing availability', {
|
|
@@ -99,15 +133,20 @@ export const ecommerceRules = [
|
|
|
99
133
|
severity: 'warning',
|
|
100
134
|
description: 'Product schema should include high-quality images',
|
|
101
135
|
check: (ctx) => {
|
|
102
|
-
if (!ctx.productSchema)
|
|
103
|
-
return
|
|
136
|
+
if (!ctx.productSchema) {
|
|
137
|
+
return createResult({ id: 'ecommerce-product-image', name: 'Product Image', category: 'structured-data', severity: 'warning' }, 'info', 'Not applicable (no Product schema)', {
|
|
138
|
+
recommendation: 'Add Product schema first, then include images',
|
|
139
|
+
});
|
|
140
|
+
}
|
|
104
141
|
const hasImage = ctx.productSchema.image !== undefined;
|
|
105
142
|
if (!hasImage) {
|
|
106
143
|
return createResult({ id: 'ecommerce-product-image', name: 'Product Image', category: 'structured-data', severity: 'warning' }, 'warn', 'Product schema missing image', {
|
|
107
144
|
recommendation: 'Add product images for visual search results',
|
|
108
145
|
evidence: {
|
|
146
|
+
found: 'Product schema without image',
|
|
109
147
|
expected: 'At least one high-quality product image',
|
|
110
148
|
impact: 'Products without images are less likely to appear in image search and shopping results',
|
|
149
|
+
example: '"image": "https://example.com/product-image.jpg"',
|
|
111
150
|
},
|
|
112
151
|
});
|
|
113
152
|
}
|
|
@@ -124,8 +163,11 @@ export const ecommerceRules = [
|
|
|
124
163
|
severity: 'info',
|
|
125
164
|
description: 'Product schema can include aggregate ratings for star snippets',
|
|
126
165
|
check: (ctx) => {
|
|
127
|
-
if (!ctx.productSchema)
|
|
128
|
-
return
|
|
166
|
+
if (!ctx.productSchema) {
|
|
167
|
+
return createResult({ id: 'ecommerce-product-reviews', name: 'Product Reviews', category: 'structured-data', severity: 'info' }, 'info', 'Not applicable (no Product schema)', {
|
|
168
|
+
recommendation: 'Add Product schema first, then include reviews/ratings',
|
|
169
|
+
});
|
|
170
|
+
}
|
|
129
171
|
const hasRating = ctx.productSchema.aggregateRating !== undefined;
|
|
130
172
|
const hasReviews = ctx.productSchema.review !== undefined;
|
|
131
173
|
if (!hasRating && !hasReviews) {
|
|
@@ -155,8 +197,11 @@ export const ecommerceRules = [
|
|
|
155
197
|
severity: 'info',
|
|
156
198
|
description: 'Product schema should include brand information',
|
|
157
199
|
check: (ctx) => {
|
|
158
|
-
if (!ctx.productSchema)
|
|
159
|
-
return
|
|
200
|
+
if (!ctx.productSchema) {
|
|
201
|
+
return createResult({ id: 'ecommerce-product-brand', name: 'Product Brand', category: 'structured-data', severity: 'info' }, 'info', 'Not applicable (no Product schema)', {
|
|
202
|
+
recommendation: 'Add Product schema first, then include brand',
|
|
203
|
+
});
|
|
204
|
+
}
|
|
160
205
|
const hasBrand = ctx.productSchema.brand !== undefined;
|
|
161
206
|
if (!hasBrand) {
|
|
162
207
|
return createResult({ id: 'ecommerce-product-brand', name: 'Product Brand', category: 'structured-data', severity: 'info' }, 'info', 'Product schema missing brand', {
|
|
@@ -180,8 +225,11 @@ export const ecommerceRules = [
|
|
|
180
225
|
severity: 'info',
|
|
181
226
|
description: 'Product schema should include unique identifiers (SKU, GTIN, MPN)',
|
|
182
227
|
check: (ctx) => {
|
|
183
|
-
if (!ctx.productSchema)
|
|
184
|
-
return
|
|
228
|
+
if (!ctx.productSchema) {
|
|
229
|
+
return createResult({ id: 'ecommerce-product-sku', name: 'Product Identifiers', category: 'structured-data', severity: 'info' }, 'info', 'Not applicable (no Product schema)', {
|
|
230
|
+
recommendation: 'Add Product schema first, then include identifiers',
|
|
231
|
+
});
|
|
232
|
+
}
|
|
185
233
|
const hasSku = ctx.productSchema.sku !== undefined;
|
|
186
234
|
const hasGtin = ctx.productSchema.gtin !== undefined ||
|
|
187
235
|
ctx.productSchema.gtin13 !== undefined ||
|
|
@@ -215,21 +263,25 @@ export const ecommerceRules = [
|
|
|
215
263
|
severity: 'info',
|
|
216
264
|
description: 'Time-sensitive offers should have valid date ranges',
|
|
217
265
|
check: (ctx) => {
|
|
218
|
-
if (!ctx.productSchema?.offers)
|
|
219
|
-
return
|
|
266
|
+
if (!ctx.productSchema?.offers) {
|
|
267
|
+
return createResult({ id: 'ecommerce-offer-valid-dates', name: 'Offer Valid Dates', category: 'structured-data', severity: 'info' }, 'info', 'Not applicable (no Product offers schema)', {
|
|
268
|
+
recommendation: 'Add Product schema with offers to enable date validation',
|
|
269
|
+
});
|
|
270
|
+
}
|
|
220
271
|
const offers = ctx.productSchema.offers;
|
|
221
272
|
const priceValidUntil = offers.priceValidUntil;
|
|
222
|
-
const validFrom = offers.validFrom;
|
|
223
273
|
const validThrough = offers.validThrough;
|
|
224
274
|
if (priceValidUntil) {
|
|
225
275
|
const endDate = new Date(priceValidUntil);
|
|
226
276
|
const now = new Date();
|
|
227
277
|
if (endDate < now) {
|
|
228
278
|
return createResult({ id: 'ecommerce-offer-valid-dates', name: 'Offer Valid Dates', category: 'structured-data', severity: 'info' }, 'warn', 'Offer priceValidUntil date has passed', {
|
|
279
|
+
recommendation: 'Update or remove expired priceValidUntil date',
|
|
229
280
|
evidence: {
|
|
230
281
|
found: priceValidUntil,
|
|
231
|
-
|
|
282
|
+
expected: 'Future date or no priceValidUntil',
|
|
232
283
|
impact: 'Expired dates may cause Google to distrust your pricing data',
|
|
284
|
+
example: `"priceValidUntil": "${new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0]}"`,
|
|
233
285
|
},
|
|
234
286
|
});
|
|
235
287
|
}
|
|
@@ -239,14 +291,22 @@ export const ecommerceRules = [
|
|
|
239
291
|
const now = new Date();
|
|
240
292
|
if (endDate < now) {
|
|
241
293
|
return createResult({ id: 'ecommerce-offer-valid-dates', name: 'Offer Valid Dates', category: 'structured-data', severity: 'info' }, 'warn', 'Offer validThrough date has passed', {
|
|
294
|
+
recommendation: 'Update or remove expired validThrough date',
|
|
242
295
|
evidence: {
|
|
243
296
|
found: validThrough,
|
|
244
|
-
|
|
297
|
+
expected: 'Future date or no validThrough',
|
|
298
|
+
impact: 'Expired validity dates may prevent offer from showing in search results',
|
|
299
|
+
example: `"validThrough": "${new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0]}"`,
|
|
245
300
|
},
|
|
246
301
|
});
|
|
247
302
|
}
|
|
248
303
|
}
|
|
249
|
-
|
|
304
|
+
if (priceValidUntil || validThrough) {
|
|
305
|
+
return createResult({ id: 'ecommerce-offer-valid-dates', name: 'Offer Valid Dates', category: 'structured-data', severity: 'info' }, 'pass', 'Offer dates are valid');
|
|
306
|
+
}
|
|
307
|
+
return createResult({ id: 'ecommerce-offer-valid-dates', name: 'Offer Valid Dates', category: 'structured-data', severity: 'info' }, 'info', 'No offer date constraints specified', {
|
|
308
|
+
recommendation: 'Consider adding priceValidUntil for time-limited offers',
|
|
309
|
+
});
|
|
250
310
|
},
|
|
251
311
|
},
|
|
252
312
|
];
|