recker 1.0.43 → 1.0.44-next.084c2ef
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -0
- package/dist/bin/recker-linux-x64 +0 -0
- package/dist/bin/recker-macos-x64 +0 -0
- package/dist/bin/recker-win-x64.exe +0 -0
- package/dist/bin/rek.cjs +85617 -99560
- package/dist/browser/ai/adaptive-timeout.d.ts +50 -0
- package/dist/browser/ai/adaptive-timeout.js +208 -0
- package/dist/browser/ai/client.d.ts +22 -0
- package/dist/browser/ai/client.js +294 -0
- package/dist/browser/ai/index.d.ts +14 -0
- package/dist/browser/ai/index.js +11 -0
- package/dist/browser/ai/providers/anthropic.d.ts +63 -0
- package/dist/browser/ai/providers/anthropic.js +370 -0
- package/dist/browser/ai/providers/base.d.ts +48 -0
- package/dist/browser/ai/providers/base.js +150 -0
- package/dist/browser/ai/providers/google.d.ts +59 -0
- package/dist/browser/ai/providers/google.js +305 -0
- package/dist/browser/ai/providers/ollama.d.ts +44 -0
- package/dist/browser/ai/providers/ollama.js +240 -0
- package/dist/browser/ai/providers/openai.d.ts +64 -0
- package/dist/browser/ai/providers/openai.js +298 -0
- package/dist/browser/ai/rate-limiter.d.ts +43 -0
- package/dist/browser/ai/rate-limiter.js +215 -0
- package/dist/browser/ai/vector/index.d.ts +2 -0
- package/dist/browser/ai/vector/index.js +2 -0
- package/dist/browser/ai/vector/similarity.d.ts +2 -0
- package/dist/browser/ai/vector/similarity.js +27 -0
- package/dist/browser/ai/vector/store.d.ts +27 -0
- package/dist/browser/ai/vector/store.js +82 -0
- package/dist/browser/browser/cache.d.ts +2 -40
- package/dist/browser/browser/cache.js +2 -199
- package/dist/browser/browser/index.d.ts +8 -0
- package/dist/browser/browser/index.js +8 -0
- package/dist/browser/browser/recker.d.ts +8 -1
- package/dist/browser/browser/recker.js +8 -2
- package/dist/browser/cache/indexed-db.d.ts +10 -0
- package/dist/browser/cache/indexed-db.js +88 -0
- package/dist/browser/cache/service-worker-cache.d.ts +18 -0
- package/dist/browser/cache/service-worker-cache.js +103 -0
- package/dist/browser/cache.d.ts +2 -40
- package/dist/browser/cache.js +2 -199
- package/dist/browser/constants/user-agents.d.ts +7 -0
- package/dist/browser/constants/user-agents.js +7 -0
- package/dist/browser/core/client.d.ts +2 -0
- package/dist/browser/core/client.js +19 -1
- package/dist/browser/index.d.ts +8 -0
- package/dist/browser/index.js +8 -0
- package/dist/browser/plugins/har-recorder.d.ts +40 -0
- package/dist/browser/plugins/har-recorder.js +120 -0
- package/dist/browser/plugins/network-simulation.d.ts +7 -0
- package/dist/browser/plugins/network-simulation.js +13 -0
- package/dist/browser/presets/android.d.ts +2 -0
- package/dist/browser/presets/android.js +16 -0
- package/dist/browser/presets/anthropic.d.ts +8 -0
- package/dist/browser/presets/anthropic.js +27 -0
- package/dist/browser/presets/aws.d.ts +19 -0
- package/dist/browser/presets/aws.js +68 -0
- package/dist/browser/presets/azure-openai.d.ts +10 -0
- package/dist/browser/presets/azure-openai.js +35 -0
- package/dist/browser/presets/azure.d.ts +41 -0
- package/dist/browser/presets/azure.js +104 -0
- package/dist/browser/presets/chaturbate.d.ts +2 -0
- package/dist/browser/presets/chaturbate.js +17 -0
- package/dist/browser/presets/cloudflare.d.ts +12 -0
- package/dist/browser/presets/cloudflare.js +39 -0
- package/dist/browser/presets/cohere.d.ts +7 -0
- package/dist/browser/presets/cohere.js +22 -0
- package/dist/browser/presets/deepseek.d.ts +7 -0
- package/dist/browser/presets/deepseek.js +22 -0
- package/dist/browser/presets/digitalocean.d.ts +5 -0
- package/dist/browser/presets/digitalocean.js +16 -0
- package/dist/browser/presets/discord.d.ts +6 -0
- package/dist/browser/presets/discord.js +17 -0
- package/dist/browser/presets/elevenlabs.d.ts +6 -0
- package/dist/browser/presets/elevenlabs.js +20 -0
- package/dist/browser/presets/enhancers.d.ts +20 -0
- package/dist/browser/presets/enhancers.js +85 -0
- package/dist/browser/presets/fireworks.d.ts +7 -0
- package/dist/browser/presets/fireworks.js +22 -0
- package/dist/browser/presets/gcp.d.ts +34 -0
- package/dist/browser/presets/gcp.js +91 -0
- package/dist/browser/presets/gemini.d.ts +7 -0
- package/dist/browser/presets/gemini.js +23 -0
- package/dist/browser/presets/github.d.ts +6 -0
- package/dist/browser/presets/github.js +17 -0
- package/dist/browser/presets/gitlab.d.ts +6 -0
- package/dist/browser/presets/gitlab.js +16 -0
- package/dist/browser/presets/groq.d.ts +7 -0
- package/dist/browser/presets/groq.js +22 -0
- package/dist/browser/presets/hubspot.d.ts +9 -0
- package/dist/browser/presets/hubspot.js +28 -0
- package/dist/browser/presets/huggingface.d.ts +7 -0
- package/dist/browser/presets/huggingface.js +23 -0
- package/dist/browser/presets/index.d.ts +47 -0
- package/dist/browser/presets/index.js +47 -0
- package/dist/browser/presets/ios.d.ts +2 -0
- package/dist/browser/presets/ios.js +13 -0
- package/dist/browser/presets/linear.d.ts +5 -0
- package/dist/browser/presets/linear.js +16 -0
- package/dist/browser/presets/mailgun.d.ts +7 -0
- package/dist/browser/presets/mailgun.js +20 -0
- package/dist/browser/presets/meta.d.ts +10 -0
- package/dist/browser/presets/meta.js +33 -0
- package/dist/browser/presets/mistral.d.ts +7 -0
- package/dist/browser/presets/mistral.js +22 -0
- package/dist/browser/presets/notion.d.ts +6 -0
- package/dist/browser/presets/notion.js +17 -0
- package/dist/browser/presets/openai.d.ts +9 -0
- package/dist/browser/presets/openai.js +30 -0
- package/dist/browser/presets/oracle.d.ts +19 -0
- package/dist/browser/presets/oracle.js +117 -0
- package/dist/browser/presets/perplexity.d.ts +7 -0
- package/dist/browser/presets/perplexity.js +22 -0
- package/dist/browser/presets/pinecone.d.ts +8 -0
- package/dist/browser/presets/pinecone.js +42 -0
- package/dist/browser/presets/registry.d.ts +23 -0
- package/dist/browser/presets/registry.js +519 -0
- package/dist/browser/presets/replicate.d.ts +7 -0
- package/dist/browser/presets/replicate.js +23 -0
- package/dist/browser/presets/sendgrid.d.ts +6 -0
- package/dist/browser/presets/sendgrid.js +20 -0
- package/dist/browser/presets/sentry.d.ts +11 -0
- package/dist/browser/presets/sentry.js +48 -0
- package/dist/browser/presets/sinch.d.ts +9 -0
- package/dist/browser/presets/sinch.js +39 -0
- package/dist/browser/presets/slack.d.ts +5 -0
- package/dist/browser/presets/slack.js +16 -0
- package/dist/browser/presets/square.d.ts +10 -0
- package/dist/browser/presets/square.js +33 -0
- package/dist/browser/presets/stripe.d.ts +7 -0
- package/dist/browser/presets/stripe.js +23 -0
- package/dist/browser/presets/supabase.d.ts +6 -0
- package/dist/browser/presets/supabase.js +18 -0
- package/dist/browser/presets/tiktok.d.ts +10 -0
- package/dist/browser/presets/tiktok.js +38 -0
- package/dist/browser/presets/together.d.ts +7 -0
- package/dist/browser/presets/together.js +22 -0
- package/dist/browser/presets/twilio.d.ts +6 -0
- package/dist/browser/presets/twilio.js +17 -0
- package/dist/browser/presets/vercel.d.ts +6 -0
- package/dist/browser/presets/vercel.js +23 -0
- package/dist/browser/presets/vultr.d.ts +5 -0
- package/dist/browser/presets/vultr.js +16 -0
- package/dist/browser/presets/xai.d.ts +8 -0
- package/dist/browser/presets/xai.js +23 -0
- package/dist/browser/presets/youtube.d.ts +5 -0
- package/dist/browser/presets/youtube.js +20 -0
- package/dist/browser/recker.d.ts +8 -1
- package/dist/browser/recker.js +8 -2
- package/dist/browser/scrape/document.d.ts +5 -4
- package/dist/browser/scrape/document.js +89 -76
- package/dist/browser/scrape/element.d.ts +10 -8
- package/dist/browser/scrape/element.js +295 -81
- package/dist/browser/scrape/extractors.d.ts +11 -11
- package/dist/browser/scrape/extractors.js +145 -113
- package/dist/browser/scrape/parser/back.d.ts +1 -0
- package/dist/browser/scrape/parser/back.js +3 -0
- package/dist/browser/scrape/parser/index.d.ts +20 -0
- package/dist/browser/scrape/parser/index.js +19 -0
- package/dist/browser/scrape/parser/matcher.d.ts +30 -0
- package/dist/browser/scrape/parser/matcher.js +99 -0
- package/dist/browser/scrape/parser/nodes/comment.d.ts +12 -0
- package/dist/browser/scrape/parser/nodes/comment.js +21 -0
- package/dist/browser/scrape/parser/nodes/html.d.ts +110 -0
- package/dist/browser/scrape/parser/nodes/html.js +978 -0
- package/dist/browser/scrape/parser/nodes/node.d.ts +18 -0
- package/dist/browser/scrape/parser/nodes/node.js +31 -0
- package/dist/browser/scrape/parser/nodes/text.d.ts +14 -0
- package/dist/browser/scrape/parser/nodes/text.js +30 -0
- package/dist/browser/scrape/parser/nodes/type.d.ts +6 -0
- package/dist/browser/scrape/parser/nodes/type.js +7 -0
- package/dist/browser/scrape/parser/parse.d.ts +1 -0
- package/dist/browser/scrape/parser/parse.js +1 -0
- package/dist/browser/scrape/parser/valid.d.ts +2 -0
- package/dist/browser/scrape/parser/valid.js +5 -0
- package/dist/browser/scrape/parser/void-tag.d.ts +7 -0
- package/dist/browser/scrape/parser/void-tag.js +43 -0
- package/dist/browser/scrape/types.d.ts +7 -0
- package/dist/browser/seo/analyzer.d.ts +59 -0
- package/dist/browser/seo/analyzer.js +1399 -0
- package/dist/browser/seo/keywords.d.ts +16 -0
- package/dist/browser/seo/keywords.js +55 -0
- package/dist/browser/seo/rules/accessibility.d.ts +2 -0
- package/dist/browser/seo/rules/accessibility.js +733 -0
- package/dist/browser/seo/rules/ai-search.d.ts +2 -0
- package/dist/browser/seo/rules/ai-search.js +436 -0
- package/dist/browser/seo/rules/analytics.d.ts +2 -0
- package/dist/browser/seo/rules/analytics.js +306 -0
- package/dist/browser/seo/rules/best-practices.d.ts +2 -0
- package/dist/browser/seo/rules/best-practices.js +195 -0
- package/dist/browser/seo/rules/canonical.d.ts +12 -0
- package/dist/browser/seo/rules/canonical.js +270 -0
- package/dist/browser/seo/rules/content.d.ts +2 -0
- package/dist/browser/seo/rules/content.js +522 -0
- package/dist/browser/seo/rules/crawl.d.ts +2 -0
- package/dist/browser/seo/rules/crawl.js +435 -0
- package/dist/browser/seo/rules/cwv.d.ts +2 -0
- package/dist/browser/seo/rules/cwv.js +248 -0
- package/dist/browser/seo/rules/ecommerce.d.ts +2 -0
- package/dist/browser/seo/rules/ecommerce.js +312 -0
- package/dist/browser/seo/rules/i18n.d.ts +2 -0
- package/dist/browser/seo/rules/i18n.js +288 -0
- package/dist/browser/seo/rules/images.d.ts +2 -0
- package/dist/browser/seo/rules/images.js +255 -0
- package/dist/browser/seo/rules/index.d.ts +52 -0
- package/dist/browser/seo/rules/index.js +163 -0
- package/dist/browser/seo/rules/internal-linking.d.ts +2 -0
- package/dist/browser/seo/rules/internal-linking.js +394 -0
- package/dist/browser/seo/rules/links.d.ts +2 -0
- package/dist/browser/seo/rules/links.js +498 -0
- package/dist/browser/seo/rules/local.d.ts +2 -0
- package/dist/browser/seo/rules/local.js +289 -0
- package/dist/browser/seo/rules/manifest.d.ts +2 -0
- package/dist/browser/seo/rules/manifest.js +621 -0
- package/dist/browser/seo/rules/meta.d.ts +2 -0
- package/dist/browser/seo/rules/meta.js +805 -0
- package/dist/browser/seo/rules/mobile.d.ts +2 -0
- package/dist/browser/seo/rules/mobile.js +161 -0
- package/dist/browser/seo/rules/performance.d.ts +2 -0
- package/dist/browser/seo/rules/performance.js +738 -0
- package/dist/browser/seo/rules/pwa.d.ts +2 -0
- package/dist/browser/seo/rules/pwa.js +299 -0
- package/dist/browser/seo/rules/readability.d.ts +2 -0
- package/dist/browser/seo/rules/readability.js +264 -0
- package/dist/browser/seo/rules/redirects.d.ts +16 -0
- package/dist/browser/seo/rules/redirects.js +199 -0
- package/dist/browser/seo/rules/resources.d.ts +2 -0
- package/dist/browser/seo/rules/resources.js +390 -0
- package/dist/browser/seo/rules/schema.d.ts +2 -0
- package/dist/browser/seo/rules/schema.js +379 -0
- package/dist/browser/seo/rules/security.d.ts +2 -0
- package/dist/browser/seo/rules/security.js +877 -0
- package/dist/browser/seo/rules/social.d.ts +2 -0
- package/dist/browser/seo/rules/social.js +603 -0
- package/dist/browser/seo/rules/structural.d.ts +2 -0
- package/dist/browser/seo/rules/structural.js +223 -0
- package/dist/browser/seo/rules/technical-advanced.d.ts +10 -0
- package/dist/browser/seo/rules/technical-advanced.js +289 -0
- package/dist/browser/seo/rules/technical.d.ts +3 -0
- package/dist/browser/seo/rules/technical.js +509 -0
- package/dist/browser/seo/rules/thresholds.d.ts +196 -0
- package/dist/browser/seo/rules/thresholds.js +118 -0
- package/dist/browser/seo/rules/types.d.ts +518 -0
- package/dist/browser/seo/rules/types.js +11 -0
- package/dist/browser/seo/types.d.ts +211 -0
- package/dist/browser/seo/types.js +1 -0
- package/dist/browser/transport/curl.d.ts +4 -0
- package/dist/browser/transport/curl.js +101 -0
- package/dist/browser/transport/undici.js +1 -2
- package/dist/browser/transport/worker.d.ts +18 -0
- package/dist/browser/transport/worker.js +278 -0
- package/dist/browser/types/index.d.ts +4 -1
- package/dist/browser/utils/binary-manager.d.ts +4 -0
- package/dist/browser/utils/binary-manager.js +72 -0
- package/dist/browser/utils/user-agent.js +2 -13
- package/dist/cache/indexed-db.d.ts +10 -0
- package/dist/cache/indexed-db.js +88 -0
- package/dist/cache/service-worker-cache.d.ts +18 -0
- package/dist/cache/service-worker-cache.js +103 -0
- package/dist/cli/commands/ai.d.ts +2 -0
- package/dist/cli/commands/ai.js +162 -0
- package/dist/cli/commands/bench.d.ts +2 -0
- package/dist/cli/commands/bench.js +51 -0
- package/dist/cli/commands/dns.d.ts +2 -0
- package/dist/cli/commands/dns.js +295 -0
- package/dist/cli/commands/har.d.ts +2 -0
- package/dist/cli/commands/har.js +171 -0
- package/dist/cli/commands/hls.d.ts +2 -0
- package/dist/cli/commands/hls.js +192 -0
- package/dist/cli/commands/network.d.ts +2 -0
- package/dist/cli/commands/network.js +288 -0
- package/dist/cli/commands/protocols.d.ts +2 -0
- package/dist/cli/commands/protocols.js +344 -0
- package/dist/cli/commands/scrape.d.ts +2 -0
- package/dist/cli/commands/scrape.js +176 -0
- package/dist/cli/commands/security.d.ts +2 -0
- package/dist/cli/commands/security.js +57 -0
- package/dist/cli/commands/seo.d.ts +2 -0
- package/dist/cli/commands/seo.js +125 -0
- package/dist/cli/commands/serve.d.ts +2 -0
- package/dist/cli/commands/serve.js +531 -0
- package/dist/cli/commands/spider.d.ts +3 -0
- package/dist/cli/commands/spider.js +478 -0
- package/dist/cli/commands/utils.d.ts +2 -0
- package/dist/cli/commands/utils.js +176 -0
- package/dist/cli/commands/vector.d.ts +2 -0
- package/dist/cli/commands/vector.js +158 -0
- package/dist/cli/handler.d.ts +2 -2
- package/dist/cli/handler.js +6 -6
- package/dist/cli/helpers.d.ts +7 -0
- package/dist/cli/helpers.js +128 -0
- package/dist/cli/index.js +96 -5228
- package/dist/cli/parser/help.d.ts +2 -0
- package/dist/cli/parser/help.js +52 -0
- package/dist/cli/parser/index.d.ts +3 -0
- package/dist/cli/parser/index.js +3 -0
- package/dist/cli/parser/parser.d.ts +4 -0
- package/dist/cli/parser/parser.js +146 -0
- package/dist/cli/parser/types.d.ts +41 -0
- package/dist/cli/parser/types.js +1 -0
- package/dist/cli/presets.d.ts +1 -1
- package/dist/cli/presets.js +1 -1
- package/dist/cli/router.d.ts +36 -0
- package/dist/cli/router.js +195 -0
- package/dist/cli/tui/ai-chat.js +1 -1
- package/dist/cli/tui/commands/context.d.ts +9 -0
- package/dist/cli/tui/commands/context.js +1 -0
- package/dist/cli/tui/commands/dns.d.ts +10 -0
- package/dist/cli/tui/commands/dns.js +461 -0
- package/dist/cli/tui/commands/hls.d.ts +2 -0
- package/dist/cli/tui/commands/hls.js +162 -0
- package/dist/cli/tui/commands/ip.d.ts +2 -0
- package/dist/cli/tui/commands/ip.js +45 -0
- package/dist/cli/tui/commands/network.d.ts +3 -0
- package/dist/cli/tui/commands/network.js +81 -0
- package/dist/cli/tui/commands/protocols.d.ts +6 -0
- package/dist/cli/tui/commands/protocols.js +531 -0
- package/dist/cli/tui/commands/security.d.ts +2 -0
- package/dist/cli/tui/commands/security.js +48 -0
- package/dist/cli/tui/commands/seo.d.ts +2 -0
- package/dist/cli/tui/commands/seo.js +74 -0
- package/dist/cli/tui/context.d.ts +12 -0
- package/dist/cli/tui/context.js +1 -0
- package/dist/cli/tui/shell.d.ts +11 -20
- package/dist/cli/tui/shell.js +216 -1873
- package/dist/constants/user-agents.d.ts +7 -0
- package/dist/constants/user-agents.js +7 -0
- package/dist/core/client.d.ts +2 -0
- package/dist/core/client.js +19 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/mcp/cli.js +2 -3
- package/dist/mcp/data/embeddings.json +1 -1
- package/dist/mcp/tools/network.js +298 -158
- package/dist/plugins/har-player.d.ts +23 -0
- package/dist/plugins/har-player.js +49 -0
- package/dist/plugins/har-recorder.d.ts +37 -3
- package/dist/plugins/har-recorder.js +116 -63
- package/dist/plugins/network-simulation.d.ts +7 -0
- package/dist/plugins/network-simulation.js +13 -0
- package/dist/presets/android.d.ts +2 -0
- package/dist/presets/android.js +16 -0
- package/dist/presets/chaturbate.d.ts +2 -0
- package/dist/presets/chaturbate.js +17 -0
- package/dist/presets/elevenlabs.d.ts +6 -0
- package/dist/presets/elevenlabs.js +20 -0
- package/dist/presets/enhancers.d.ts +20 -0
- package/dist/presets/enhancers.js +85 -0
- package/dist/presets/hubspot.d.ts +9 -0
- package/dist/presets/hubspot.js +28 -0
- package/dist/presets/index.d.ts +10 -0
- package/dist/presets/index.js +10 -0
- package/dist/presets/ios.d.ts +2 -0
- package/dist/presets/ios.js +13 -0
- package/dist/presets/pinecone.d.ts +8 -0
- package/dist/presets/pinecone.js +42 -0
- package/dist/presets/registry.js +60 -0
- package/dist/presets/sendgrid.d.ts +6 -0
- package/dist/presets/sendgrid.js +20 -0
- package/dist/presets/sentry.d.ts +11 -0
- package/dist/presets/sentry.js +48 -0
- package/dist/presets/square.d.ts +10 -0
- package/dist/presets/square.js +33 -0
- package/dist/recker.d.ts +3 -0
- package/dist/recker.js +4 -0
- package/dist/scrape/document.d.ts +5 -4
- package/dist/scrape/document.js +89 -76
- package/dist/scrape/element.d.ts +10 -8
- package/dist/scrape/element.js +295 -81
- package/dist/scrape/extractors.d.ts +11 -11
- package/dist/scrape/extractors.js +145 -113
- package/dist/scrape/index.d.ts +2 -0
- package/dist/scrape/index.js +1 -0
- package/dist/scrape/parser/back.d.ts +1 -0
- package/dist/scrape/parser/back.js +3 -0
- package/dist/scrape/parser/index.d.ts +20 -0
- package/dist/scrape/parser/index.js +19 -0
- package/dist/scrape/parser/matcher.d.ts +30 -0
- package/dist/scrape/parser/matcher.js +99 -0
- package/dist/scrape/parser/nodes/comment.d.ts +12 -0
- package/dist/scrape/parser/nodes/comment.js +21 -0
- package/dist/scrape/parser/nodes/html.d.ts +110 -0
- package/dist/scrape/parser/nodes/html.js +978 -0
- package/dist/scrape/parser/nodes/node.d.ts +18 -0
- package/dist/scrape/parser/nodes/node.js +31 -0
- package/dist/scrape/parser/nodes/text.d.ts +14 -0
- package/dist/scrape/parser/nodes/text.js +30 -0
- package/dist/scrape/parser/nodes/type.d.ts +6 -0
- package/dist/scrape/parser/nodes/type.js +7 -0
- package/dist/scrape/parser/parse.d.ts +1 -0
- package/dist/scrape/parser/parse.js +1 -0
- package/dist/scrape/parser/valid.d.ts +2 -0
- package/dist/scrape/parser/valid.js +5 -0
- package/dist/scrape/parser/void-tag.d.ts +7 -0
- package/dist/scrape/parser/void-tag.js +43 -0
- package/dist/scrape/spider.d.ts +19 -0
- package/dist/scrape/spider.js +28 -3
- package/dist/scrape/types.d.ts +7 -0
- package/dist/seo/analyzer.d.ts +15 -5
- package/dist/seo/analyzer.js +636 -175
- package/dist/seo/formatter.d.ts +16 -0
- package/dist/seo/formatter.js +228 -0
- package/dist/seo/index.d.ts +2 -0
- package/dist/seo/index.js +1 -0
- package/dist/seo/keywords.d.ts +16 -0
- package/dist/seo/keywords.js +55 -0
- package/dist/seo/rules/accessibility.js +96 -57
- package/dist/seo/rules/ai-search.js +44 -31
- package/dist/seo/rules/analytics.d.ts +2 -0
- package/dist/seo/rules/analytics.js +306 -0
- package/dist/seo/rules/best-practices.js +21 -14
- package/dist/seo/rules/canonical.js +53 -32
- package/dist/seo/rules/content.js +317 -31
- package/dist/seo/rules/crawl.js +55 -40
- package/dist/seo/rules/cwv.js +21 -15
- package/dist/seo/rules/ecommerce.js +82 -22
- package/dist/seo/rules/i18n.js +75 -36
- package/dist/seo/rules/images.js +109 -30
- package/dist/seo/rules/index.js +7 -1
- package/dist/seo/rules/internal-linking.js +58 -39
- package/dist/seo/rules/links.js +79 -52
- package/dist/seo/rules/local.js +49 -25
- package/dist/seo/rules/manifest.d.ts +2 -0
- package/dist/seo/rules/manifest.js +621 -0
- package/dist/seo/rules/meta.js +339 -81
- package/dist/seo/rules/mobile.js +112 -2
- package/dist/seo/rules/performance.js +434 -66
- package/dist/seo/rules/pwa.js +36 -39
- package/dist/seo/rules/readability.js +31 -22
- package/dist/seo/rules/redirects.js +21 -15
- package/dist/seo/rules/resources.js +59 -42
- package/dist/seo/rules/schema.js +333 -8
- package/dist/seo/rules/security.js +142 -80
- package/dist/seo/rules/social.js +277 -47
- package/dist/seo/rules/structural.js +87 -19
- package/dist/seo/rules/technical-advanced.js +30 -24
- package/dist/seo/rules/technical.d.ts +1 -0
- package/dist/seo/rules/technical.js +272 -42
- package/dist/seo/rules/types.d.ts +74 -2
- package/dist/seo/seo-spider.d.ts +35 -0
- package/dist/seo/seo-spider.js +152 -13
- package/dist/seo/types.d.ts +8 -1
- package/dist/seo/validators/llms-txt.js +19 -0
- package/dist/seo/validators/rss.d.ts +11 -0
- package/dist/seo/validators/rss.js +93 -0
- package/dist/seo/validators/sitemap.js +36 -26
- package/dist/transport/curl.d.ts +4 -0
- package/dist/transport/curl.js +101 -0
- package/dist/transport/udp.js +0 -1
- package/dist/transport/undici.js +1 -2
- package/dist/transport/worker.d.ts +18 -0
- package/dist/transport/worker.js +278 -0
- package/dist/types/index.d.ts +4 -1
- package/dist/utils/binary-manager.d.ts +4 -0
- package/dist/utils/binary-manager.js +72 -0
- package/dist/utils/optional-require.d.ts +7 -8
- package/dist/utils/optional-require.js +2 -21
- package/dist/utils/upload.d.ts +6 -0
- package/dist/utils/upload.js +11 -0
- package/dist/utils/user-agent.js +2 -13
- package/dist/version.js +1 -1
- package/package.json +12 -6
- package/dist/browser/utils/optional-require.d.ts +0 -19
- package/dist/browser/utils/optional-require.js +0 -105
|
@@ -1,8 +1,25 @@
|
|
|
1
1
|
import type { SeoStatus } from '../types.js';
|
|
2
2
|
import type { ExtractedLink } from '../../scrape/types.js';
|
|
3
3
|
export type RuleSeverity = 'error' | 'warning' | 'info';
|
|
4
|
-
export type RuleCategory = 'title' | 'meta' | 'og' | 'twitter' | 'headings' | 'images' | 'links' | 'content' | 'technical' | 'security' | 'mobile' | 'structured-data' | 'performance' | 'accessibility' | 'ai-search' | 'resources' | 'crawlability' | 'canonicalization';
|
|
4
|
+
export type RuleCategory = 'title' | 'meta' | 'og' | 'twitter' | 'headings' | 'images' | 'links' | 'content' | 'technical' | 'security' | 'mobile' | 'structured-data' | 'performance' | 'accessibility' | 'i18n' | 'ai-search' | 'resources' | 'crawlability' | 'canonicalization';
|
|
5
5
|
export interface RuleContext {
|
|
6
|
+
keywordsInTitle?: boolean;
|
|
7
|
+
keywordsInDescription?: boolean;
|
|
8
|
+
keywordsInH1?: boolean;
|
|
9
|
+
keywordsInUrl?: boolean;
|
|
10
|
+
keywordsInFirstParagraph?: boolean;
|
|
11
|
+
keywordsInAltText?: boolean;
|
|
12
|
+
keywordConsistencyScore?: number;
|
|
13
|
+
keywordConsistencyDetails?: {
|
|
14
|
+
inTitle: boolean;
|
|
15
|
+
inDescription: boolean;
|
|
16
|
+
inH1: boolean;
|
|
17
|
+
inUrl: boolean;
|
|
18
|
+
inFirstParagraph: boolean;
|
|
19
|
+
inAltText: boolean;
|
|
20
|
+
};
|
|
21
|
+
topKeywords?: string[];
|
|
22
|
+
mainKeyword?: string;
|
|
6
23
|
title?: string;
|
|
7
24
|
titleLength?: number;
|
|
8
25
|
metaDescription?: string;
|
|
@@ -34,9 +51,12 @@ export interface RuleContext {
|
|
|
34
51
|
imagesWithDimensions?: number;
|
|
35
52
|
imagesMissingDimensions?: number;
|
|
36
53
|
imagesWithEmptyAlt?: number;
|
|
54
|
+
imagesWithSrcset?: number;
|
|
55
|
+
largeBase64ImagesCount?: number;
|
|
37
56
|
imagesDecorativeCount?: number;
|
|
38
57
|
imagesUsingModernFormats?: number;
|
|
39
58
|
altTextLengths?: number[];
|
|
59
|
+
imageAltTexts?: string[];
|
|
40
60
|
imageFilenames?: string[];
|
|
41
61
|
imagesWithAsyncDecoding?: number;
|
|
42
62
|
brokenExternalImages?: number;
|
|
@@ -70,6 +90,8 @@ export interface RuleContext {
|
|
|
70
90
|
totalLinks?: number;
|
|
71
91
|
internalLinks?: number;
|
|
72
92
|
externalLinks?: number;
|
|
93
|
+
internalHttpLinks?: number;
|
|
94
|
+
internalHttpLinkUrls?: string[];
|
|
73
95
|
linksWithoutText?: number;
|
|
74
96
|
nofollowLinks?: number;
|
|
75
97
|
sponsoredLinks?: number;
|
|
@@ -89,6 +111,8 @@ export interface RuleContext {
|
|
|
89
111
|
missingNoreferrer?: ExtractedLink[];
|
|
90
112
|
};
|
|
91
113
|
wordCount?: number;
|
|
114
|
+
emailsFound?: string[];
|
|
115
|
+
socialLinksFound?: string[];
|
|
92
116
|
characterCount?: number;
|
|
93
117
|
sentenceCount?: number;
|
|
94
118
|
paragraphCount?: number;
|
|
@@ -102,7 +126,6 @@ export interface RuleContext {
|
|
|
102
126
|
avgSentenceLength?: number;
|
|
103
127
|
faqCount?: number;
|
|
104
128
|
imagePerWordRatio?: number;
|
|
105
|
-
mainKeyword?: string;
|
|
106
129
|
keywordDensity?: number;
|
|
107
130
|
fleschReadingEase?: number;
|
|
108
131
|
hasQuestionHeadings?: boolean;
|
|
@@ -120,6 +143,7 @@ export interface RuleContext {
|
|
|
120
143
|
hasBreadcrumbsSchema?: boolean;
|
|
121
144
|
videoCount?: number;
|
|
122
145
|
audioCount?: number;
|
|
146
|
+
hasAutoplay?: boolean;
|
|
123
147
|
hasCanonical?: boolean;
|
|
124
148
|
canonicalUrl?: string;
|
|
125
149
|
hasViewport?: boolean;
|
|
@@ -134,7 +158,10 @@ export interface RuleContext {
|
|
|
134
158
|
textHtmlRatio?: number;
|
|
135
159
|
hasDeprecatedPlugins?: boolean;
|
|
136
160
|
deprecatedPluginTypes?: string[];
|
|
161
|
+
deprecatedTagsCount?: number;
|
|
162
|
+
deprecatedTagsFound?: string[];
|
|
137
163
|
hasFrameTags?: boolean;
|
|
164
|
+
iframeCount?: number;
|
|
138
165
|
hasFavicon?: boolean;
|
|
139
166
|
faviconUrl?: string;
|
|
140
167
|
hasPreconnect?: boolean;
|
|
@@ -307,6 +334,26 @@ export interface RuleContext {
|
|
|
307
334
|
manifestShortName?: string;
|
|
308
335
|
manifestName?: string;
|
|
309
336
|
manifestBackgroundColor?: string;
|
|
337
|
+
manifestThemeColor?: string;
|
|
338
|
+
manifestDescription?: string;
|
|
339
|
+
manifestOrientation?: string;
|
|
340
|
+
manifestLang?: string;
|
|
341
|
+
manifestDir?: string;
|
|
342
|
+
manifestCategories?: string[];
|
|
343
|
+
manifestId?: string;
|
|
344
|
+
manifestScreenshots?: Array<{
|
|
345
|
+
src: string;
|
|
346
|
+
sizes?: string;
|
|
347
|
+
type?: string;
|
|
348
|
+
form_factor?: string;
|
|
349
|
+
label?: string;
|
|
350
|
+
}>;
|
|
351
|
+
manifestShortcuts?: Array<{
|
|
352
|
+
name: string;
|
|
353
|
+
url: string;
|
|
354
|
+
description?: string;
|
|
355
|
+
}>;
|
|
356
|
+
hasServiceWorker?: boolean;
|
|
310
357
|
ogImageDimensions?: {
|
|
311
358
|
width: number;
|
|
312
359
|
height: number;
|
|
@@ -321,6 +368,21 @@ export interface RuleContext {
|
|
|
321
368
|
pinterestRichPinSupport?: boolean;
|
|
322
369
|
hasPinterestNopin?: boolean;
|
|
323
370
|
fbAppId?: string;
|
|
371
|
+
totalSocialLinks?: number;
|
|
372
|
+
socialLinksInHeader?: number;
|
|
373
|
+
socialLinksInFooter?: number;
|
|
374
|
+
socialLinksWithoutAccessibility?: number;
|
|
375
|
+
socialLinksWithoutNewTab?: number;
|
|
376
|
+
socialLinksWithoutNoopener?: number;
|
|
377
|
+
platformsFound?: string[];
|
|
378
|
+
socialLinkDetails?: Array<{
|
|
379
|
+
href: string;
|
|
380
|
+
platform: string;
|
|
381
|
+
hasAccessibility: boolean;
|
|
382
|
+
hasNewTab: boolean;
|
|
383
|
+
hasNoopener: boolean;
|
|
384
|
+
location: 'header' | 'footer' | 'body';
|
|
385
|
+
}>;
|
|
324
386
|
navLinkCount?: number;
|
|
325
387
|
footerLinkCount?: number;
|
|
326
388
|
contextualLinkCount?: number;
|
|
@@ -408,6 +470,16 @@ export interface RuleContext {
|
|
|
408
470
|
tlsVersion?: string;
|
|
409
471
|
hasPasswordField?: boolean;
|
|
410
472
|
formsOnHttp?: number;
|
|
473
|
+
analyticsDetected?: boolean;
|
|
474
|
+
analyticsProviders?: string[];
|
|
475
|
+
hasRssFeed?: boolean;
|
|
476
|
+
rssFeedUrl?: string;
|
|
477
|
+
hasAtomFeed?: boolean;
|
|
478
|
+
atomFeedUrl?: string;
|
|
479
|
+
ctaButtonsCount?: number;
|
|
480
|
+
formCount?: number;
|
|
481
|
+
hasWhatsAppLink?: boolean;
|
|
482
|
+
pageInSitemap?: boolean;
|
|
411
483
|
}
|
|
412
484
|
export interface RuleEvidence {
|
|
413
485
|
found?: string | number | string[];
|
package/dist/seo/seo-spider.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SpiderOptions, SpiderResult, SpiderPageResult } from '../scrape/spider.js';
|
|
2
2
|
import type { SeoReport } from './types.js';
|
|
3
|
+
import { type SitemapValidationResult } from './validators/sitemap.js';
|
|
3
4
|
export interface SeoSpiderOptions extends SpiderOptions {
|
|
4
5
|
seo?: boolean;
|
|
5
6
|
output?: string;
|
|
@@ -20,6 +21,37 @@ export interface SiteWideIssue {
|
|
|
20
21
|
export interface SeoSpiderResult extends Omit<SpiderResult, 'pages'> {
|
|
21
22
|
pages: SeoPageResult[];
|
|
22
23
|
siteWideIssues: SiteWideIssue[];
|
|
24
|
+
discovery?: {
|
|
25
|
+
humans: {
|
|
26
|
+
found: boolean;
|
|
27
|
+
content?: string;
|
|
28
|
+
url: string;
|
|
29
|
+
};
|
|
30
|
+
llms: {
|
|
31
|
+
found: boolean;
|
|
32
|
+
content?: string;
|
|
33
|
+
url: string;
|
|
34
|
+
};
|
|
35
|
+
sitemap: {
|
|
36
|
+
found: boolean;
|
|
37
|
+
url: string;
|
|
38
|
+
urlCount?: number;
|
|
39
|
+
};
|
|
40
|
+
manifest: {
|
|
41
|
+
found: boolean;
|
|
42
|
+
url: string;
|
|
43
|
+
content?: Record<string, unknown>;
|
|
44
|
+
valid?: boolean;
|
|
45
|
+
issues?: string[];
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
rssFeeds?: Array<{
|
|
49
|
+
url: string;
|
|
50
|
+
type: 'rss' | 'atom' | 'unknown';
|
|
51
|
+
title?: string;
|
|
52
|
+
itemCount: number;
|
|
53
|
+
}>;
|
|
54
|
+
sitemapValidation?: SitemapValidationResult;
|
|
23
55
|
summary: {
|
|
24
56
|
totalPages: number;
|
|
25
57
|
pagesWithErrors: number;
|
|
@@ -37,6 +69,9 @@ export declare class SeoSpider {
|
|
|
37
69
|
private seoResults;
|
|
38
70
|
constructor(options?: SeoSpiderOptions);
|
|
39
71
|
crawl(startUrl: string): Promise<SeoSpiderResult>;
|
|
72
|
+
private checkSiteFiles;
|
|
73
|
+
private validateManifest;
|
|
74
|
+
private validateSitemap;
|
|
40
75
|
private analyzePages;
|
|
41
76
|
private createReportFromPageData;
|
|
42
77
|
private detectSiteWideIssues;
|
package/dist/seo/seo-spider.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Spider } from '../scrape/spider.js';
|
|
2
2
|
import { analyzeSeo } from './analyzer.js';
|
|
3
3
|
import { createClient } from '../core/client.js';
|
|
4
|
+
import { discoverFeeds } from './validators/rss.js';
|
|
5
|
+
import { fetchAndValidateSitemap } from './validators/sitemap.js';
|
|
4
6
|
import * as fs from 'fs/promises';
|
|
5
7
|
export class SeoSpider {
|
|
6
8
|
spider;
|
|
@@ -32,10 +34,23 @@ export class SeoSpider {
|
|
|
32
34
|
const seoPages = await this.analyzePages(result.pages);
|
|
33
35
|
const siteWideIssues = this.detectSiteWideIssues(seoPages);
|
|
34
36
|
const summary = this.calculateSummary(seoPages, siteWideIssues);
|
|
37
|
+
const discovery = await this.checkSiteFiles(startUrl);
|
|
38
|
+
let homeHtml = '';
|
|
39
|
+
try {
|
|
40
|
+
const client = createClient({ timeout: 10000 });
|
|
41
|
+
const res = await client.get(startUrl);
|
|
42
|
+
homeHtml = await res.text();
|
|
43
|
+
}
|
|
44
|
+
catch { }
|
|
45
|
+
const rssFeeds = await discoverFeeds(new URL(startUrl).origin, homeHtml);
|
|
46
|
+
const sitemapValidation = await this.validateSitemap(startUrl);
|
|
35
47
|
const seoResult = {
|
|
36
48
|
...result,
|
|
37
49
|
pages: seoPages,
|
|
38
50
|
siteWideIssues,
|
|
51
|
+
discovery,
|
|
52
|
+
rssFeeds,
|
|
53
|
+
sitemapValidation,
|
|
39
54
|
summary,
|
|
40
55
|
};
|
|
41
56
|
if (this.options.output) {
|
|
@@ -43,6 +58,132 @@ export class SeoSpider {
|
|
|
43
58
|
}
|
|
44
59
|
return seoResult;
|
|
45
60
|
}
|
|
61
|
+
async checkSiteFiles(startUrl) {
|
|
62
|
+
try {
|
|
63
|
+
const baseUrl = new URL(startUrl).origin;
|
|
64
|
+
const client = createClient({ timeout: 5000 });
|
|
65
|
+
const results = {
|
|
66
|
+
humans: { found: false, content: undefined, url: `${baseUrl}/humans.txt` },
|
|
67
|
+
llms: { found: false, content: undefined, url: `${baseUrl}/llms.txt` },
|
|
68
|
+
sitemap: { found: false, url: `${baseUrl}/sitemap.xml`, urlCount: undefined },
|
|
69
|
+
manifest: { found: false, url: `${baseUrl}/manifest.json`, content: undefined, valid: undefined, issues: undefined },
|
|
70
|
+
};
|
|
71
|
+
try {
|
|
72
|
+
const res = await client.get(results.humans.url);
|
|
73
|
+
if (res.status === 200) {
|
|
74
|
+
results.humans.found = true;
|
|
75
|
+
results.humans.content = await res.text();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch { }
|
|
79
|
+
try {
|
|
80
|
+
const res = await client.get(results.llms.url);
|
|
81
|
+
if (res.status === 200) {
|
|
82
|
+
results.llms.found = true;
|
|
83
|
+
results.llms.content = await res.text();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch { }
|
|
87
|
+
try {
|
|
88
|
+
const res = await client.get(results.sitemap.url);
|
|
89
|
+
if (res.status === 200) {
|
|
90
|
+
results.sitemap.found = true;
|
|
91
|
+
const content = await res.text();
|
|
92
|
+
const urlMatches = content.match(/<loc>/g);
|
|
93
|
+
results.sitemap.urlCount = urlMatches ? urlMatches.length : 0;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch { }
|
|
97
|
+
try {
|
|
98
|
+
let res = await client.get(results.manifest.url);
|
|
99
|
+
if (res.status !== 200) {
|
|
100
|
+
results.manifest.url = `${baseUrl}/site.webmanifest`;
|
|
101
|
+
res = await client.get(results.manifest.url);
|
|
102
|
+
}
|
|
103
|
+
if (res.status === 200) {
|
|
104
|
+
results.manifest.found = true;
|
|
105
|
+
const text = await res.text();
|
|
106
|
+
try {
|
|
107
|
+
const manifest = JSON.parse(text);
|
|
108
|
+
results.manifest.content = manifest;
|
|
109
|
+
results.manifest.valid = true;
|
|
110
|
+
results.manifest.issues = this.validateManifest(manifest);
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
results.manifest.valid = false;
|
|
114
|
+
results.manifest.issues = ['Invalid JSON format'];
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch { }
|
|
119
|
+
return results;
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
validateManifest(manifest) {
|
|
126
|
+
const issues = [];
|
|
127
|
+
if (!manifest.name && !manifest.short_name) {
|
|
128
|
+
issues.push('Missing required field: name or short_name');
|
|
129
|
+
}
|
|
130
|
+
if (!manifest.icons || !Array.isArray(manifest.icons) || manifest.icons.length === 0) {
|
|
131
|
+
issues.push('Missing icons array');
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
const icons = manifest.icons;
|
|
135
|
+
const sizes = icons.map(i => i.sizes).filter(Boolean);
|
|
136
|
+
const has192 = sizes.some(s => s?.includes('192'));
|
|
137
|
+
const has512 = sizes.some(s => s?.includes('512'));
|
|
138
|
+
if (!has192)
|
|
139
|
+
issues.push('Missing 192x192 icon (required for Add to Home Screen)');
|
|
140
|
+
if (!has512)
|
|
141
|
+
issues.push('Missing 512x512 icon (required for splash screen)');
|
|
142
|
+
const hasMaskable = icons.some(i => i.purpose?.includes('maskable'));
|
|
143
|
+
if (!hasMaskable)
|
|
144
|
+
issues.push('No maskable icon found (recommended for Android adaptive icons)');
|
|
145
|
+
}
|
|
146
|
+
if (!manifest.display) {
|
|
147
|
+
issues.push('Missing display mode (recommended: standalone)');
|
|
148
|
+
}
|
|
149
|
+
if (!manifest.start_url) {
|
|
150
|
+
issues.push('Missing start_url');
|
|
151
|
+
}
|
|
152
|
+
if (!manifest.theme_color) {
|
|
153
|
+
issues.push('Missing theme_color');
|
|
154
|
+
}
|
|
155
|
+
if (!manifest.background_color) {
|
|
156
|
+
issues.push('Missing background_color');
|
|
157
|
+
}
|
|
158
|
+
if (!manifest.scope) {
|
|
159
|
+
issues.push('Missing scope (defines navigation boundaries)');
|
|
160
|
+
}
|
|
161
|
+
if (manifest.short_name && typeof manifest.short_name === 'string' && manifest.short_name.length > 12) {
|
|
162
|
+
issues.push(`short_name too long (${manifest.short_name.length} chars, max 12)`);
|
|
163
|
+
}
|
|
164
|
+
return issues;
|
|
165
|
+
}
|
|
166
|
+
async validateSitemap(startUrl) {
|
|
167
|
+
try {
|
|
168
|
+
const baseUrl = new URL(startUrl).origin;
|
|
169
|
+
const sitemapUrl = `${baseUrl}/sitemap.xml`;
|
|
170
|
+
const client = createClient({ timeout: this.options.timeout || 15000 });
|
|
171
|
+
const fetcher = async (url) => {
|
|
172
|
+
const res = await client.get(url);
|
|
173
|
+
const text = await res.text();
|
|
174
|
+
return {
|
|
175
|
+
status: res.status,
|
|
176
|
+
text,
|
|
177
|
+
headers: Object.fromEntries([...res.headers.entries()]),
|
|
178
|
+
};
|
|
179
|
+
};
|
|
180
|
+
const result = await fetchAndValidateSitemap(sitemapUrl, fetcher);
|
|
181
|
+
return result;
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
46
187
|
async analyzePages(pages) {
|
|
47
188
|
const results = [];
|
|
48
189
|
const client = createClient({
|
|
@@ -93,8 +234,9 @@ export class SeoSpider {
|
|
|
93
234
|
if (titleLength < 30) {
|
|
94
235
|
checks.push({
|
|
95
236
|
name: 'Title Length',
|
|
237
|
+
category: 'title',
|
|
96
238
|
status: 'warn',
|
|
97
|
-
message: `Title is
|
|
239
|
+
message: `Title is ${titleLength} characters`,
|
|
98
240
|
value: titleLength,
|
|
99
241
|
recommendation: 'Title should be 50-60 characters',
|
|
100
242
|
});
|
|
@@ -102,6 +244,7 @@ export class SeoSpider {
|
|
|
102
244
|
else if (titleLength > 60) {
|
|
103
245
|
checks.push({
|
|
104
246
|
name: 'Title Length',
|
|
247
|
+
category: 'title',
|
|
105
248
|
status: 'warn',
|
|
106
249
|
message: `Title is too long (${titleLength} chars)`,
|
|
107
250
|
value: titleLength,
|
|
@@ -111,6 +254,7 @@ export class SeoSpider {
|
|
|
111
254
|
else {
|
|
112
255
|
checks.push({
|
|
113
256
|
name: 'Title Length',
|
|
257
|
+
category: 'title',
|
|
114
258
|
status: 'pass',
|
|
115
259
|
message: `Good title length (${titleLength} chars)`,
|
|
116
260
|
value: titleLength,
|
|
@@ -120,6 +264,7 @@ export class SeoSpider {
|
|
|
120
264
|
else {
|
|
121
265
|
checks.push({
|
|
122
266
|
name: 'Title',
|
|
267
|
+
category: 'title',
|
|
123
268
|
status: 'fail',
|
|
124
269
|
message: 'Page has no title',
|
|
125
270
|
recommendation: 'Add a descriptive <title> tag',
|
|
@@ -130,6 +275,7 @@ export class SeoSpider {
|
|
|
130
275
|
if (internalLinks === 0) {
|
|
131
276
|
checks.push({
|
|
132
277
|
name: 'Internal Links',
|
|
278
|
+
category: 'links',
|
|
133
279
|
status: 'warn',
|
|
134
280
|
message: 'No internal links found',
|
|
135
281
|
recommendation: 'Add internal links to improve site structure',
|
|
@@ -138,6 +284,7 @@ export class SeoSpider {
|
|
|
138
284
|
else {
|
|
139
285
|
checks.push({
|
|
140
286
|
name: 'Internal Links',
|
|
287
|
+
category: 'links',
|
|
141
288
|
status: 'pass',
|
|
142
289
|
message: `${internalLinks} internal links found`,
|
|
143
290
|
value: internalLinks,
|
|
@@ -232,27 +379,19 @@ export class SeoSpider {
|
|
|
232
379
|
missingDimensions: 0,
|
|
233
380
|
modernFormats: 0,
|
|
234
381
|
altTextLengths: [],
|
|
382
|
+
imageAltTexts: [],
|
|
235
383
|
imageFilenames: [],
|
|
236
384
|
imagesWithAsyncDecoding: 0,
|
|
237
385
|
},
|
|
238
386
|
social: {
|
|
239
387
|
openGraph: {
|
|
240
|
-
present: false,
|
|
241
|
-
hasTitle: false,
|
|
242
|
-
hasDescription: false,
|
|
243
|
-
hasImage: false,
|
|
244
|
-
hasUrl: false,
|
|
245
|
-
issues: [],
|
|
388
|
+
present: false, hasTitle: false, hasDescription: false, hasImage: false, hasUrl: false, issues: []
|
|
246
389
|
},
|
|
247
390
|
twitterCard: {
|
|
248
|
-
present: false,
|
|
249
|
-
hasCard: false,
|
|
250
|
-
hasTitle: false,
|
|
251
|
-
hasDescription: false,
|
|
252
|
-
hasImage: false,
|
|
253
|
-
issues: [],
|
|
391
|
+
present: false, hasCard: false, hasTitle: false, hasDescription: false, hasImage: false, issues: []
|
|
254
392
|
},
|
|
255
393
|
},
|
|
394
|
+
keywords: { totalWords: 0, uniqueWords: 0, topKeywords: [] },
|
|
256
395
|
technical: {
|
|
257
396
|
hasCanonical: false,
|
|
258
397
|
hasRobotsMeta: false,
|
package/dist/seo/types.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { KeywordCloud } from './keywords.js';
|
|
2
|
+
export type { KeywordCloud, KeywordItem } from './keywords.js';
|
|
1
3
|
export type SeoStatus = 'pass' | 'warn' | 'fail' | 'info';
|
|
2
4
|
export interface SeoCheckEvidence {
|
|
3
5
|
found?: string | number | string[];
|
|
@@ -10,6 +12,7 @@ export interface SeoCheckEvidence {
|
|
|
10
12
|
}
|
|
11
13
|
export interface SeoCheckResult {
|
|
12
14
|
name: string;
|
|
15
|
+
category: string;
|
|
13
16
|
status: SeoStatus;
|
|
14
17
|
message: string;
|
|
15
18
|
value?: string | number;
|
|
@@ -50,6 +53,8 @@ export interface LinkAnalysis {
|
|
|
50
53
|
withoutText: number;
|
|
51
54
|
sponsoredLinks: number;
|
|
52
55
|
ugcLinks: number;
|
|
56
|
+
internalHttpLinks?: number;
|
|
57
|
+
internalHttpLinkUrls?: string[];
|
|
53
58
|
}
|
|
54
59
|
export interface ImageAnalysis {
|
|
55
60
|
total: number;
|
|
@@ -59,6 +64,7 @@ export interface ImageAnalysis {
|
|
|
59
64
|
missingDimensions: number;
|
|
60
65
|
modernFormats: number;
|
|
61
66
|
altTextLengths: number[];
|
|
67
|
+
imageAltTexts: string[];
|
|
62
68
|
imageFilenames: string[];
|
|
63
69
|
imagesWithAsyncDecoding: number;
|
|
64
70
|
}
|
|
@@ -172,8 +178,9 @@ export interface SeoReport {
|
|
|
172
178
|
types: string[];
|
|
173
179
|
items: Record<string, unknown>[];
|
|
174
180
|
};
|
|
175
|
-
headings: HeadingAnalysis;
|
|
176
181
|
content: ContentMetrics;
|
|
182
|
+
headings: HeadingAnalysis;
|
|
183
|
+
keywords: KeywordCloud;
|
|
177
184
|
links: LinkAnalysis;
|
|
178
185
|
images: ImageAnalysis;
|
|
179
186
|
social: SocialMetaAnalysis;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
const MAX_FILE_SIZE = 100 * 1024;
|
|
2
2
|
const MIN_DESCRIPTION_LENGTH = 50;
|
|
3
3
|
const MAX_DESCRIPTION_LENGTH = 500;
|
|
4
|
+
const OPTIMAL_MIN_LINKS = 10;
|
|
5
|
+
const OPTIMAL_MAX_LINKS = 30;
|
|
4
6
|
export function parseLlmsTxt(content) {
|
|
5
7
|
const errors = [];
|
|
6
8
|
const warnings = [];
|
|
@@ -181,6 +183,23 @@ export function validateLlmsTxt(content, baseUrl) {
|
|
|
181
183
|
seenUrls.add(normalized);
|
|
182
184
|
}
|
|
183
185
|
}
|
|
186
|
+
const linkCount = parseResult.links.length;
|
|
187
|
+
if (linkCount > 0 && linkCount < OPTIMAL_MIN_LINKS) {
|
|
188
|
+
issues.push({
|
|
189
|
+
type: 'info',
|
|
190
|
+
code: 'FEW_LINKS',
|
|
191
|
+
message: `Only ${linkCount} link(s) found in llms.txt`,
|
|
192
|
+
recommendation: `Consider adding ${OPTIMAL_MIN_LINKS}-${OPTIMAL_MAX_LINKS} of your most valuable pages for better AI coverage`,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
else if (linkCount > OPTIMAL_MAX_LINKS) {
|
|
196
|
+
issues.push({
|
|
197
|
+
type: 'info',
|
|
198
|
+
code: 'MANY_LINKS',
|
|
199
|
+
message: `${linkCount} links found in llms.txt`,
|
|
200
|
+
recommendation: `Focus on quality over quantity. ${OPTIMAL_MIN_LINKS}-${OPTIMAL_MAX_LINKS} high-value links are recommended to help AI systems identify your truly important content`,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
184
203
|
return {
|
|
185
204
|
valid: issues.filter(i => i.type === 'error').length === 0,
|
|
186
205
|
issues,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface RssFeed {
|
|
2
|
+
url: string;
|
|
3
|
+
type: 'rss' | 'atom' | 'unknown';
|
|
4
|
+
title?: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
itemCount: number;
|
|
7
|
+
lastBuildDate?: string;
|
|
8
|
+
isValid: boolean;
|
|
9
|
+
error?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function discoverFeeds(baseUrl: string, html?: string): Promise<RssFeed[]>;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { createClient } from '../../core/client.js';
|
|
2
|
+
const COMMON_PATHS = [
|
|
3
|
+
'/rss.xml',
|
|
4
|
+
'/feed.xml',
|
|
5
|
+
'/rss',
|
|
6
|
+
'/feed',
|
|
7
|
+
'/atom.xml',
|
|
8
|
+
'/feeds/posts/default',
|
|
9
|
+
'/index.xml'
|
|
10
|
+
];
|
|
11
|
+
export async function discoverFeeds(baseUrl, html) {
|
|
12
|
+
const candidateUrls = new Set();
|
|
13
|
+
const feeds = [];
|
|
14
|
+
if (html) {
|
|
15
|
+
const linkRegex = /<link[^>]+?type=["']application\/(rss\+xml|atom\+xml)["'][^>]*?>/gi;
|
|
16
|
+
const hrefRegex = /href=["']([^"']+)["']/;
|
|
17
|
+
const titleRegex = /title=["']([^"']+)["']/;
|
|
18
|
+
let match;
|
|
19
|
+
while ((match = linkRegex.exec(html)) !== null) {
|
|
20
|
+
const tag = match[0];
|
|
21
|
+
const hrefMatch = hrefRegex.exec(tag);
|
|
22
|
+
if (hrefMatch) {
|
|
23
|
+
let href = hrefMatch[1];
|
|
24
|
+
try {
|
|
25
|
+
href = new URL(href, baseUrl).toString();
|
|
26
|
+
candidateUrls.add(href);
|
|
27
|
+
}
|
|
28
|
+
catch { }
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (candidateUrls.size === 0) {
|
|
33
|
+
for (const path of COMMON_PATHS) {
|
|
34
|
+
try {
|
|
35
|
+
const url = new URL(path, baseUrl).toString();
|
|
36
|
+
candidateUrls.add(url);
|
|
37
|
+
}
|
|
38
|
+
catch { }
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const client = createClient({ timeout: 8000 });
|
|
42
|
+
await Promise.all(Array.from(candidateUrls).map(async (url) => {
|
|
43
|
+
try {
|
|
44
|
+
const response = await client.get(url);
|
|
45
|
+
if (response.status !== 200)
|
|
46
|
+
return;
|
|
47
|
+
const contentType = response.headers.get('content-type') || '';
|
|
48
|
+
const text = await response.text();
|
|
49
|
+
let type = 'unknown';
|
|
50
|
+
let isValid = false;
|
|
51
|
+
let itemCount = 0;
|
|
52
|
+
let title;
|
|
53
|
+
let description;
|
|
54
|
+
let lastBuildDate;
|
|
55
|
+
if (text.includes('<rss') && text.includes('version="2.0"')) {
|
|
56
|
+
type = 'rss';
|
|
57
|
+
isValid = true;
|
|
58
|
+
itemCount = (text.match(/<item>/g) || []).length;
|
|
59
|
+
const titleMatch = text.match(/<channel>[\s\S]*?<title>(.*?)<\/title>/);
|
|
60
|
+
if (titleMatch)
|
|
61
|
+
title = titleMatch[1].replace(/<!\[CDATA\[(.*?)\]\]>/g, '$1').trim();
|
|
62
|
+
const descMatch = text.match(/<channel>[\s\S]*?<description>(.*?)<\/description>/);
|
|
63
|
+
if (descMatch)
|
|
64
|
+
description = descMatch[1].replace(/<!\[CDATA\[(.*?)\]\]>/g, '$1').trim();
|
|
65
|
+
const dateMatch = text.match(/<lastBuildDate>(.*?)<\/lastBuildDate>/);
|
|
66
|
+
if (dateMatch)
|
|
67
|
+
lastBuildDate = dateMatch[1];
|
|
68
|
+
}
|
|
69
|
+
else if (text.includes('<feed') && text.includes('xmlns="http://www.w3.org/2005/Atom"')) {
|
|
70
|
+
type = 'atom';
|
|
71
|
+
isValid = true;
|
|
72
|
+
itemCount = (text.match(/<entry>/g) || []).length;
|
|
73
|
+
const titleMatch = text.match(/<title>(.*?)<\/title>/);
|
|
74
|
+
if (titleMatch)
|
|
75
|
+
title = titleMatch[1].trim();
|
|
76
|
+
}
|
|
77
|
+
if (isValid) {
|
|
78
|
+
feeds.push({
|
|
79
|
+
url,
|
|
80
|
+
type,
|
|
81
|
+
isValid,
|
|
82
|
+
title,
|
|
83
|
+
description,
|
|
84
|
+
itemCount,
|
|
85
|
+
lastBuildDate
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
}
|
|
91
|
+
}));
|
|
92
|
+
return feeds;
|
|
93
|
+
}
|