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.
Files changed (459) hide show
  1. package/README.md +47 -0
  2. package/dist/bin/recker-linux-x64 +0 -0
  3. package/dist/bin/recker-macos-x64 +0 -0
  4. package/dist/bin/recker-win-x64.exe +0 -0
  5. package/dist/bin/rek.cjs +85152 -100207
  6. package/dist/browser/ai/adaptive-timeout.d.ts +50 -0
  7. package/dist/browser/ai/adaptive-timeout.js +208 -0
  8. package/dist/browser/ai/client.d.ts +22 -0
  9. package/dist/browser/ai/client.js +294 -0
  10. package/dist/browser/ai/index.d.ts +14 -0
  11. package/dist/browser/ai/index.js +11 -0
  12. package/dist/browser/ai/providers/anthropic.d.ts +63 -0
  13. package/dist/browser/ai/providers/anthropic.js +370 -0
  14. package/dist/browser/ai/providers/base.d.ts +48 -0
  15. package/dist/browser/ai/providers/base.js +150 -0
  16. package/dist/browser/ai/providers/google.d.ts +59 -0
  17. package/dist/browser/ai/providers/google.js +305 -0
  18. package/dist/browser/ai/providers/ollama.d.ts +44 -0
  19. package/dist/browser/ai/providers/ollama.js +240 -0
  20. package/dist/browser/ai/providers/openai.d.ts +64 -0
  21. package/dist/browser/ai/providers/openai.js +298 -0
  22. package/dist/browser/ai/rate-limiter.d.ts +43 -0
  23. package/dist/browser/ai/rate-limiter.js +215 -0
  24. package/dist/browser/ai/vector/index.d.ts +2 -0
  25. package/dist/browser/ai/vector/index.js +2 -0
  26. package/dist/browser/ai/vector/similarity.d.ts +2 -0
  27. package/dist/browser/ai/vector/similarity.js +27 -0
  28. package/dist/browser/ai/vector/store.d.ts +27 -0
  29. package/dist/browser/ai/vector/store.js +82 -0
  30. package/dist/browser/browser/cache.d.ts +2 -40
  31. package/dist/browser/browser/cache.js +2 -199
  32. package/dist/browser/browser/index.d.ts +8 -0
  33. package/dist/browser/browser/index.js +8 -0
  34. package/dist/browser/browser/recker.d.ts +8 -1
  35. package/dist/browser/browser/recker.js +8 -2
  36. package/dist/browser/cache/indexed-db.d.ts +10 -0
  37. package/dist/browser/cache/indexed-db.js +88 -0
  38. package/dist/browser/cache/service-worker-cache.d.ts +18 -0
  39. package/dist/browser/cache/service-worker-cache.js +103 -0
  40. package/dist/browser/cache.d.ts +2 -40
  41. package/dist/browser/cache.js +2 -199
  42. package/dist/browser/constants/user-agents.d.ts +7 -0
  43. package/dist/browser/constants/user-agents.js +7 -0
  44. package/dist/browser/core/client.d.ts +2 -0
  45. package/dist/browser/core/client.js +19 -1
  46. package/dist/browser/index.d.ts +8 -0
  47. package/dist/browser/index.js +8 -0
  48. package/dist/browser/plugins/har-recorder.d.ts +40 -0
  49. package/dist/browser/plugins/har-recorder.js +120 -0
  50. package/dist/browser/plugins/network-simulation.d.ts +7 -0
  51. package/dist/browser/plugins/network-simulation.js +13 -0
  52. package/dist/browser/presets/android.d.ts +2 -0
  53. package/dist/browser/presets/android.js +16 -0
  54. package/dist/browser/presets/anthropic.d.ts +8 -0
  55. package/dist/browser/presets/anthropic.js +27 -0
  56. package/dist/browser/presets/aws.d.ts +19 -0
  57. package/dist/browser/presets/aws.js +68 -0
  58. package/dist/browser/presets/azure-openai.d.ts +10 -0
  59. package/dist/browser/presets/azure-openai.js +35 -0
  60. package/dist/browser/presets/azure.d.ts +41 -0
  61. package/dist/browser/presets/azure.js +104 -0
  62. package/dist/browser/presets/chaturbate.d.ts +2 -0
  63. package/dist/browser/presets/chaturbate.js +17 -0
  64. package/dist/browser/presets/cloudflare.d.ts +12 -0
  65. package/dist/browser/presets/cloudflare.js +39 -0
  66. package/dist/browser/presets/cohere.d.ts +7 -0
  67. package/dist/browser/presets/cohere.js +22 -0
  68. package/dist/browser/presets/deepseek.d.ts +7 -0
  69. package/dist/browser/presets/deepseek.js +22 -0
  70. package/dist/browser/presets/digitalocean.d.ts +5 -0
  71. package/dist/browser/presets/digitalocean.js +16 -0
  72. package/dist/browser/presets/discord.d.ts +6 -0
  73. package/dist/browser/presets/discord.js +17 -0
  74. package/dist/browser/presets/elevenlabs.d.ts +6 -0
  75. package/dist/browser/presets/elevenlabs.js +20 -0
  76. package/dist/browser/presets/enhancers.d.ts +20 -0
  77. package/dist/browser/presets/enhancers.js +85 -0
  78. package/dist/browser/presets/fireworks.d.ts +7 -0
  79. package/dist/browser/presets/fireworks.js +22 -0
  80. package/dist/browser/presets/gcp.d.ts +34 -0
  81. package/dist/browser/presets/gcp.js +91 -0
  82. package/dist/browser/presets/gemini.d.ts +7 -0
  83. package/dist/browser/presets/gemini.js +23 -0
  84. package/dist/browser/presets/github.d.ts +6 -0
  85. package/dist/browser/presets/github.js +17 -0
  86. package/dist/browser/presets/gitlab.d.ts +6 -0
  87. package/dist/browser/presets/gitlab.js +16 -0
  88. package/dist/browser/presets/groq.d.ts +7 -0
  89. package/dist/browser/presets/groq.js +22 -0
  90. package/dist/browser/presets/hubspot.d.ts +9 -0
  91. package/dist/browser/presets/hubspot.js +28 -0
  92. package/dist/browser/presets/huggingface.d.ts +7 -0
  93. package/dist/browser/presets/huggingface.js +23 -0
  94. package/dist/browser/presets/index.d.ts +47 -0
  95. package/dist/browser/presets/index.js +47 -0
  96. package/dist/browser/presets/ios.d.ts +2 -0
  97. package/dist/browser/presets/ios.js +13 -0
  98. package/dist/browser/presets/linear.d.ts +5 -0
  99. package/dist/browser/presets/linear.js +16 -0
  100. package/dist/browser/presets/mailgun.d.ts +7 -0
  101. package/dist/browser/presets/mailgun.js +20 -0
  102. package/dist/browser/presets/meta.d.ts +10 -0
  103. package/dist/browser/presets/meta.js +33 -0
  104. package/dist/browser/presets/mistral.d.ts +7 -0
  105. package/dist/browser/presets/mistral.js +22 -0
  106. package/dist/browser/presets/notion.d.ts +6 -0
  107. package/dist/browser/presets/notion.js +17 -0
  108. package/dist/browser/presets/openai.d.ts +9 -0
  109. package/dist/browser/presets/openai.js +30 -0
  110. package/dist/browser/presets/oracle.d.ts +19 -0
  111. package/dist/browser/presets/oracle.js +117 -0
  112. package/dist/browser/presets/perplexity.d.ts +7 -0
  113. package/dist/browser/presets/perplexity.js +22 -0
  114. package/dist/browser/presets/pinecone.d.ts +8 -0
  115. package/dist/browser/presets/pinecone.js +42 -0
  116. package/dist/browser/presets/registry.d.ts +23 -0
  117. package/dist/browser/presets/registry.js +519 -0
  118. package/dist/browser/presets/replicate.d.ts +7 -0
  119. package/dist/browser/presets/replicate.js +23 -0
  120. package/dist/browser/presets/sendgrid.d.ts +6 -0
  121. package/dist/browser/presets/sendgrid.js +20 -0
  122. package/dist/browser/presets/sentry.d.ts +11 -0
  123. package/dist/browser/presets/sentry.js +48 -0
  124. package/dist/browser/presets/sinch.d.ts +9 -0
  125. package/dist/browser/presets/sinch.js +39 -0
  126. package/dist/browser/presets/slack.d.ts +5 -0
  127. package/dist/browser/presets/slack.js +16 -0
  128. package/dist/browser/presets/square.d.ts +10 -0
  129. package/dist/browser/presets/square.js +33 -0
  130. package/dist/browser/presets/stripe.d.ts +7 -0
  131. package/dist/browser/presets/stripe.js +23 -0
  132. package/dist/browser/presets/supabase.d.ts +6 -0
  133. package/dist/browser/presets/supabase.js +18 -0
  134. package/dist/browser/presets/tiktok.d.ts +10 -0
  135. package/dist/browser/presets/tiktok.js +38 -0
  136. package/dist/browser/presets/together.d.ts +7 -0
  137. package/dist/browser/presets/together.js +22 -0
  138. package/dist/browser/presets/twilio.d.ts +6 -0
  139. package/dist/browser/presets/twilio.js +17 -0
  140. package/dist/browser/presets/vercel.d.ts +6 -0
  141. package/dist/browser/presets/vercel.js +23 -0
  142. package/dist/browser/presets/vultr.d.ts +5 -0
  143. package/dist/browser/presets/vultr.js +16 -0
  144. package/dist/browser/presets/xai.d.ts +8 -0
  145. package/dist/browser/presets/xai.js +23 -0
  146. package/dist/browser/presets/youtube.d.ts +5 -0
  147. package/dist/browser/presets/youtube.js +20 -0
  148. package/dist/browser/recker.d.ts +8 -1
  149. package/dist/browser/recker.js +8 -2
  150. package/dist/browser/scrape/document.d.ts +5 -4
  151. package/dist/browser/scrape/document.js +89 -76
  152. package/dist/browser/scrape/element.d.ts +10 -8
  153. package/dist/browser/scrape/element.js +295 -81
  154. package/dist/browser/scrape/extractors.d.ts +11 -11
  155. package/dist/browser/scrape/extractors.js +145 -113
  156. package/dist/browser/scrape/parser/back.d.ts +1 -0
  157. package/dist/browser/scrape/parser/back.js +3 -0
  158. package/dist/browser/scrape/parser/index.d.ts +20 -0
  159. package/dist/browser/scrape/parser/index.js +19 -0
  160. package/dist/browser/scrape/parser/matcher.d.ts +30 -0
  161. package/dist/browser/scrape/parser/matcher.js +99 -0
  162. package/dist/browser/scrape/parser/nodes/comment.d.ts +12 -0
  163. package/dist/browser/scrape/parser/nodes/comment.js +21 -0
  164. package/dist/browser/scrape/parser/nodes/html.d.ts +110 -0
  165. package/dist/browser/scrape/parser/nodes/html.js +978 -0
  166. package/dist/browser/scrape/parser/nodes/node.d.ts +18 -0
  167. package/dist/browser/scrape/parser/nodes/node.js +31 -0
  168. package/dist/browser/scrape/parser/nodes/text.d.ts +14 -0
  169. package/dist/browser/scrape/parser/nodes/text.js +30 -0
  170. package/dist/browser/scrape/parser/nodes/type.d.ts +6 -0
  171. package/dist/browser/scrape/parser/nodes/type.js +7 -0
  172. package/dist/browser/scrape/parser/parse.d.ts +1 -0
  173. package/dist/browser/scrape/parser/parse.js +1 -0
  174. package/dist/browser/scrape/parser/valid.d.ts +2 -0
  175. package/dist/browser/scrape/parser/valid.js +5 -0
  176. package/dist/browser/scrape/parser/void-tag.d.ts +7 -0
  177. package/dist/browser/scrape/parser/void-tag.js +43 -0
  178. package/dist/browser/scrape/types.d.ts +7 -0
  179. package/dist/browser/seo/analyzer.d.ts +59 -0
  180. package/dist/browser/seo/analyzer.js +1399 -0
  181. package/dist/browser/seo/keywords.d.ts +16 -0
  182. package/dist/browser/seo/keywords.js +55 -0
  183. package/dist/browser/seo/rules/accessibility.d.ts +2 -0
  184. package/dist/browser/seo/rules/accessibility.js +733 -0
  185. package/dist/browser/seo/rules/ai-search.d.ts +2 -0
  186. package/dist/browser/seo/rules/ai-search.js +436 -0
  187. package/dist/browser/seo/rules/analytics.d.ts +2 -0
  188. package/dist/browser/seo/rules/analytics.js +306 -0
  189. package/dist/browser/seo/rules/best-practices.d.ts +2 -0
  190. package/dist/browser/seo/rules/best-practices.js +195 -0
  191. package/dist/browser/seo/rules/canonical.d.ts +12 -0
  192. package/dist/browser/seo/rules/canonical.js +270 -0
  193. package/dist/browser/seo/rules/content.d.ts +2 -0
  194. package/dist/browser/seo/rules/content.js +522 -0
  195. package/dist/browser/seo/rules/crawl.d.ts +2 -0
  196. package/dist/browser/seo/rules/crawl.js +435 -0
  197. package/dist/browser/seo/rules/cwv.d.ts +2 -0
  198. package/dist/browser/seo/rules/cwv.js +248 -0
  199. package/dist/browser/seo/rules/ecommerce.d.ts +2 -0
  200. package/dist/browser/seo/rules/ecommerce.js +312 -0
  201. package/dist/browser/seo/rules/i18n.d.ts +2 -0
  202. package/dist/browser/seo/rules/i18n.js +288 -0
  203. package/dist/browser/seo/rules/images.d.ts +2 -0
  204. package/dist/browser/seo/rules/images.js +255 -0
  205. package/dist/browser/seo/rules/index.d.ts +52 -0
  206. package/dist/browser/seo/rules/index.js +159 -0
  207. package/dist/browser/seo/rules/internal-linking.d.ts +2 -0
  208. package/dist/browser/seo/rules/internal-linking.js +394 -0
  209. package/dist/browser/seo/rules/links.d.ts +2 -0
  210. package/dist/browser/seo/rules/links.js +498 -0
  211. package/dist/browser/seo/rules/local.d.ts +2 -0
  212. package/dist/browser/seo/rules/local.js +289 -0
  213. package/dist/browser/seo/rules/meta.d.ts +2 -0
  214. package/dist/browser/seo/rules/meta.js +805 -0
  215. package/dist/browser/seo/rules/mobile.d.ts +2 -0
  216. package/dist/browser/seo/rules/mobile.js +161 -0
  217. package/dist/browser/seo/rules/performance.d.ts +2 -0
  218. package/dist/browser/seo/rules/performance.js +738 -0
  219. package/dist/browser/seo/rules/pwa.d.ts +2 -0
  220. package/dist/browser/seo/rules/pwa.js +299 -0
  221. package/dist/browser/seo/rules/readability.d.ts +2 -0
  222. package/dist/browser/seo/rules/readability.js +264 -0
  223. package/dist/browser/seo/rules/redirects.d.ts +16 -0
  224. package/dist/browser/seo/rules/redirects.js +199 -0
  225. package/dist/browser/seo/rules/resources.d.ts +2 -0
  226. package/dist/browser/seo/rules/resources.js +390 -0
  227. package/dist/browser/seo/rules/schema.d.ts +2 -0
  228. package/dist/browser/seo/rules/schema.js +379 -0
  229. package/dist/browser/seo/rules/security.d.ts +2 -0
  230. package/dist/browser/seo/rules/security.js +877 -0
  231. package/dist/browser/seo/rules/social.d.ts +2 -0
  232. package/dist/browser/seo/rules/social.js +603 -0
  233. package/dist/browser/seo/rules/structural.d.ts +2 -0
  234. package/dist/browser/seo/rules/structural.js +223 -0
  235. package/dist/browser/seo/rules/technical-advanced.d.ts +10 -0
  236. package/dist/browser/seo/rules/technical-advanced.js +289 -0
  237. package/dist/browser/seo/rules/technical.d.ts +2 -0
  238. package/dist/browser/seo/rules/technical.js +480 -0
  239. package/dist/browser/seo/rules/thresholds.d.ts +196 -0
  240. package/dist/browser/seo/rules/thresholds.js +118 -0
  241. package/dist/browser/seo/rules/types.d.ts +498 -0
  242. package/dist/browser/seo/rules/types.js +11 -0
  243. package/dist/browser/seo/types.d.ts +211 -0
  244. package/dist/browser/seo/types.js +1 -0
  245. package/dist/browser/transport/curl.d.ts +4 -0
  246. package/dist/browser/transport/curl.js +101 -0
  247. package/dist/browser/transport/undici.js +1 -2
  248. package/dist/browser/transport/worker.d.ts +18 -0
  249. package/dist/browser/transport/worker.js +278 -0
  250. package/dist/browser/types/index.d.ts +4 -1
  251. package/dist/browser/utils/binary-manager.d.ts +4 -0
  252. package/dist/browser/utils/binary-manager.js +72 -0
  253. package/dist/browser/utils/user-agent.js +2 -13
  254. package/dist/cache/indexed-db.d.ts +10 -0
  255. package/dist/cache/indexed-db.js +88 -0
  256. package/dist/cache/service-worker-cache.d.ts +18 -0
  257. package/dist/cache/service-worker-cache.js +103 -0
  258. package/dist/cli/commands/ai.d.ts +2 -0
  259. package/dist/cli/commands/ai.js +162 -0
  260. package/dist/cli/commands/bench.d.ts +2 -0
  261. package/dist/cli/commands/bench.js +51 -0
  262. package/dist/cli/commands/dns.d.ts +2 -0
  263. package/dist/cli/commands/dns.js +295 -0
  264. package/dist/cli/commands/har.d.ts +2 -0
  265. package/dist/cli/commands/har.js +171 -0
  266. package/dist/cli/commands/hls.d.ts +2 -0
  267. package/dist/cli/commands/hls.js +192 -0
  268. package/dist/cli/commands/network.d.ts +2 -0
  269. package/dist/cli/commands/network.js +288 -0
  270. package/dist/cli/commands/protocols.d.ts +2 -0
  271. package/dist/cli/commands/protocols.js +344 -0
  272. package/dist/cli/commands/scrape.d.ts +2 -0
  273. package/dist/cli/commands/scrape.js +176 -0
  274. package/dist/cli/commands/security.d.ts +2 -0
  275. package/dist/cli/commands/security.js +57 -0
  276. package/dist/cli/commands/seo.d.ts +2 -0
  277. package/dist/cli/commands/seo.js +125 -0
  278. package/dist/cli/commands/serve.d.ts +2 -0
  279. package/dist/cli/commands/serve.js +531 -0
  280. package/dist/cli/commands/spider.d.ts +3 -0
  281. package/dist/cli/commands/spider.js +456 -0
  282. package/dist/cli/commands/utils.d.ts +2 -0
  283. package/dist/cli/commands/utils.js +176 -0
  284. package/dist/cli/commands/vector.d.ts +2 -0
  285. package/dist/cli/commands/vector.js +158 -0
  286. package/dist/cli/handler.d.ts +2 -2
  287. package/dist/cli/handler.js +6 -6
  288. package/dist/cli/helpers.d.ts +7 -0
  289. package/dist/cli/helpers.js +128 -0
  290. package/dist/cli/index.js +96 -5228
  291. package/dist/cli/parser/help.d.ts +2 -0
  292. package/dist/cli/parser/help.js +52 -0
  293. package/dist/cli/parser/index.d.ts +3 -0
  294. package/dist/cli/parser/index.js +3 -0
  295. package/dist/cli/parser/parser.d.ts +4 -0
  296. package/dist/cli/parser/parser.js +146 -0
  297. package/dist/cli/parser/types.d.ts +41 -0
  298. package/dist/cli/parser/types.js +1 -0
  299. package/dist/cli/presets.d.ts +1 -1
  300. package/dist/cli/presets.js +1 -1
  301. package/dist/cli/router.d.ts +36 -0
  302. package/dist/cli/router.js +195 -0
  303. package/dist/cli/tui/ai-chat.js +1 -1
  304. package/dist/cli/tui/commands/context.d.ts +9 -0
  305. package/dist/cli/tui/commands/context.js +1 -0
  306. package/dist/cli/tui/commands/dns.d.ts +10 -0
  307. package/dist/cli/tui/commands/dns.js +461 -0
  308. package/dist/cli/tui/commands/hls.d.ts +2 -0
  309. package/dist/cli/tui/commands/hls.js +162 -0
  310. package/dist/cli/tui/commands/ip.d.ts +2 -0
  311. package/dist/cli/tui/commands/ip.js +45 -0
  312. package/dist/cli/tui/commands/network.d.ts +3 -0
  313. package/dist/cli/tui/commands/network.js +81 -0
  314. package/dist/cli/tui/commands/protocols.d.ts +6 -0
  315. package/dist/cli/tui/commands/protocols.js +531 -0
  316. package/dist/cli/tui/commands/security.d.ts +2 -0
  317. package/dist/cli/tui/commands/security.js +48 -0
  318. package/dist/cli/tui/commands/seo.d.ts +2 -0
  319. package/dist/cli/tui/commands/seo.js +74 -0
  320. package/dist/cli/tui/context.d.ts +12 -0
  321. package/dist/cli/tui/context.js +1 -0
  322. package/dist/cli/tui/shell.d.ts +11 -20
  323. package/dist/cli/tui/shell.js +216 -1873
  324. package/dist/constants/user-agents.d.ts +7 -0
  325. package/dist/constants/user-agents.js +7 -0
  326. package/dist/core/client.d.ts +2 -0
  327. package/dist/core/client.js +19 -1
  328. package/dist/index.d.ts +1 -0
  329. package/dist/index.js +1 -0
  330. package/dist/mcp/cli.js +2 -3
  331. package/dist/mcp/data/embeddings.json +1 -1
  332. package/dist/mcp/tools/network.js +298 -158
  333. package/dist/plugins/har-player.d.ts +23 -0
  334. package/dist/plugins/har-player.js +49 -0
  335. package/dist/plugins/har-recorder.d.ts +37 -3
  336. package/dist/plugins/har-recorder.js +116 -63
  337. package/dist/plugins/network-simulation.d.ts +7 -0
  338. package/dist/plugins/network-simulation.js +13 -0
  339. package/dist/presets/android.d.ts +2 -0
  340. package/dist/presets/android.js +16 -0
  341. package/dist/presets/chaturbate.d.ts +2 -0
  342. package/dist/presets/chaturbate.js +17 -0
  343. package/dist/presets/elevenlabs.d.ts +6 -0
  344. package/dist/presets/elevenlabs.js +20 -0
  345. package/dist/presets/enhancers.d.ts +20 -0
  346. package/dist/presets/enhancers.js +85 -0
  347. package/dist/presets/hubspot.d.ts +9 -0
  348. package/dist/presets/hubspot.js +28 -0
  349. package/dist/presets/index.d.ts +10 -0
  350. package/dist/presets/index.js +10 -0
  351. package/dist/presets/ios.d.ts +2 -0
  352. package/dist/presets/ios.js +13 -0
  353. package/dist/presets/pinecone.d.ts +8 -0
  354. package/dist/presets/pinecone.js +42 -0
  355. package/dist/presets/registry.js +60 -0
  356. package/dist/presets/sendgrid.d.ts +6 -0
  357. package/dist/presets/sendgrid.js +20 -0
  358. package/dist/presets/sentry.d.ts +11 -0
  359. package/dist/presets/sentry.js +48 -0
  360. package/dist/presets/square.d.ts +10 -0
  361. package/dist/presets/square.js +33 -0
  362. package/dist/recker.d.ts +3 -0
  363. package/dist/recker.js +4 -0
  364. package/dist/scrape/document.d.ts +5 -4
  365. package/dist/scrape/document.js +89 -76
  366. package/dist/scrape/element.d.ts +10 -8
  367. package/dist/scrape/element.js +295 -81
  368. package/dist/scrape/extractors.d.ts +11 -11
  369. package/dist/scrape/extractors.js +145 -113
  370. package/dist/scrape/index.d.ts +2 -0
  371. package/dist/scrape/index.js +1 -0
  372. package/dist/scrape/parser/back.d.ts +1 -0
  373. package/dist/scrape/parser/back.js +3 -0
  374. package/dist/scrape/parser/index.d.ts +20 -0
  375. package/dist/scrape/parser/index.js +19 -0
  376. package/dist/scrape/parser/matcher.d.ts +30 -0
  377. package/dist/scrape/parser/matcher.js +99 -0
  378. package/dist/scrape/parser/nodes/comment.d.ts +12 -0
  379. package/dist/scrape/parser/nodes/comment.js +21 -0
  380. package/dist/scrape/parser/nodes/html.d.ts +110 -0
  381. package/dist/scrape/parser/nodes/html.js +978 -0
  382. package/dist/scrape/parser/nodes/node.d.ts +18 -0
  383. package/dist/scrape/parser/nodes/node.js +31 -0
  384. package/dist/scrape/parser/nodes/text.d.ts +14 -0
  385. package/dist/scrape/parser/nodes/text.js +30 -0
  386. package/dist/scrape/parser/nodes/type.d.ts +6 -0
  387. package/dist/scrape/parser/nodes/type.js +7 -0
  388. package/dist/scrape/parser/parse.d.ts +1 -0
  389. package/dist/scrape/parser/parse.js +1 -0
  390. package/dist/scrape/parser/valid.d.ts +2 -0
  391. package/dist/scrape/parser/valid.js +5 -0
  392. package/dist/scrape/parser/void-tag.d.ts +7 -0
  393. package/dist/scrape/parser/void-tag.js +43 -0
  394. package/dist/scrape/spider.d.ts +19 -0
  395. package/dist/scrape/spider.js +28 -3
  396. package/dist/scrape/types.d.ts +7 -0
  397. package/dist/seo/analyzer.d.ts +15 -5
  398. package/dist/seo/analyzer.js +636 -175
  399. package/dist/seo/formatter.d.ts +16 -0
  400. package/dist/seo/formatter.js +228 -0
  401. package/dist/seo/index.d.ts +2 -0
  402. package/dist/seo/index.js +1 -0
  403. package/dist/seo/keywords.d.ts +16 -0
  404. package/dist/seo/keywords.js +55 -0
  405. package/dist/seo/rules/accessibility.js +96 -57
  406. package/dist/seo/rules/ai-search.js +44 -31
  407. package/dist/seo/rules/analytics.d.ts +2 -0
  408. package/dist/seo/rules/analytics.js +306 -0
  409. package/dist/seo/rules/best-practices.js +21 -14
  410. package/dist/seo/rules/canonical.js +53 -32
  411. package/dist/seo/rules/content.js +317 -31
  412. package/dist/seo/rules/crawl.js +55 -40
  413. package/dist/seo/rules/cwv.js +21 -15
  414. package/dist/seo/rules/ecommerce.js +82 -22
  415. package/dist/seo/rules/i18n.js +75 -36
  416. package/dist/seo/rules/images.js +109 -30
  417. package/dist/seo/rules/index.js +2 -0
  418. package/dist/seo/rules/internal-linking.js +58 -39
  419. package/dist/seo/rules/links.js +79 -52
  420. package/dist/seo/rules/local.js +49 -25
  421. package/dist/seo/rules/meta.js +339 -81
  422. package/dist/seo/rules/mobile.js +112 -2
  423. package/dist/seo/rules/performance.js +434 -66
  424. package/dist/seo/rules/pwa.js +36 -39
  425. package/dist/seo/rules/readability.js +31 -22
  426. package/dist/seo/rules/redirects.js +21 -15
  427. package/dist/seo/rules/resources.js +59 -42
  428. package/dist/seo/rules/schema.js +333 -8
  429. package/dist/seo/rules/security.js +142 -80
  430. package/dist/seo/rules/social.js +277 -47
  431. package/dist/seo/rules/structural.js +87 -19
  432. package/dist/seo/rules/technical-advanced.js +30 -24
  433. package/dist/seo/rules/technical.js +243 -42
  434. package/dist/seo/rules/types.d.ts +53 -1
  435. package/dist/seo/seo-spider.d.ts +22 -0
  436. package/dist/seo/seo-spider.js +77 -13
  437. package/dist/seo/types.d.ts +8 -1
  438. package/dist/seo/validators/llms-txt.js +19 -0
  439. package/dist/seo/validators/rss.d.ts +11 -0
  440. package/dist/seo/validators/rss.js +93 -0
  441. package/dist/seo/validators/sitemap.js +36 -26
  442. package/dist/transport/curl.d.ts +4 -0
  443. package/dist/transport/curl.js +101 -0
  444. package/dist/transport/udp.js +0 -1
  445. package/dist/transport/undici.js +1 -2
  446. package/dist/transport/worker.d.ts +18 -0
  447. package/dist/transport/worker.js +278 -0
  448. package/dist/types/index.d.ts +4 -1
  449. package/dist/utils/binary-manager.d.ts +4 -0
  450. package/dist/utils/binary-manager.js +72 -0
  451. package/dist/utils/optional-require.d.ts +7 -8
  452. package/dist/utils/optional-require.js +2 -21
  453. package/dist/utils/upload.d.ts +6 -0
  454. package/dist/utils/upload.js +11 -0
  455. package/dist/utils/user-agent.js +2 -13
  456. package/dist/version.js +1 -1
  457. package/package.json +12 -6
  458. package/dist/browser/utils/optional-require.d.ts +0 -19
  459. package/dist/browser/utils/optional-require.js +0 -105
@@ -7,8 +7,6 @@ export const pwaRules = [
7
7
  severity: 'info',
8
8
  description: 'Pages should link to a web app manifest for PWA support',
9
9
  check: (ctx) => {
10
- if (ctx.hasManifest === undefined)
11
- return null;
12
10
  if (!ctx.hasManifest) {
13
11
  return createResult({ id: 'pwa-manifest-link', name: 'Web App Manifest', category: 'technical', severity: 'info' }, 'info', 'No web app manifest linked', {
14
12
  recommendation: 'Add a manifest.json for PWA installability',
@@ -29,8 +27,6 @@ export const pwaRules = [
29
27
  severity: 'info',
30
28
  description: 'Pages should define a theme color for browser UI',
31
29
  check: (ctx) => {
32
- if (ctx.themeColor === undefined)
33
- return null;
34
30
  if (!ctx.themeColor) {
35
31
  return createResult({ id: 'pwa-theme-color', name: 'Theme Color', category: 'mobile', severity: 'info' }, 'info', 'No theme-color meta tag', {
36
32
  recommendation: 'Add theme-color for browser UI customization',
@@ -50,8 +46,6 @@ export const pwaRules = [
50
46
  severity: 'info',
51
47
  description: 'iOS devices need apple-touch-icon for home screen',
52
48
  check: (ctx) => {
53
- if (ctx.hasAppleTouchIcon === undefined)
54
- return null;
55
49
  if (!ctx.hasAppleTouchIcon) {
56
50
  return createResult({ id: 'pwa-apple-touch-icon', name: 'Apple Touch Icon', category: 'mobile', severity: 'info' }, 'info', 'No apple-touch-icon found', {
57
51
  recommendation: 'Add apple-touch-icon for iOS home screen',
@@ -72,8 +66,6 @@ export const pwaRules = [
72
66
  severity: 'info',
73
67
  description: 'Enable standalone mode on iOS devices',
74
68
  check: (ctx) => {
75
- if (ctx.hasAppleMobileWebAppCapable === undefined)
76
- return null;
77
69
  if (!ctx.hasAppleMobileWebAppCapable) {
78
70
  return createResult({ id: 'pwa-apple-mobile-capable', name: 'Apple Mobile Web App', category: 'mobile', severity: 'info' }, 'info', 'No apple-mobile-web-app-capable meta tag', {
79
71
  recommendation: 'Add for full-screen iOS experience',
@@ -93,10 +85,11 @@ export const pwaRules = [
93
85
  severity: 'info',
94
86
  description: 'Configure iOS status bar appearance',
95
87
  check: (ctx) => {
96
- if (!ctx.hasAppleMobileWebAppCapable)
97
- return null;
98
- if (ctx.appleStatusBarStyle === undefined)
99
- return null;
88
+ if (!ctx.hasAppleMobileWebAppCapable) {
89
+ return createResult({ id: 'pwa-apple-status-bar', name: 'Apple Status Bar Style', category: 'mobile', severity: 'info' }, 'info', 'Not applicable (apple-mobile-web-app-capable not set)', {
90
+ recommendation: 'First enable apple-mobile-web-app-capable, then configure status bar',
91
+ });
92
+ }
100
93
  if (!ctx.appleStatusBarStyle) {
101
94
  return createResult({ id: 'pwa-apple-status-bar', name: 'Apple Status Bar Style', category: 'mobile', severity: 'info' }, 'info', 'No apple-mobile-web-app-status-bar-style defined', {
102
95
  recommendation: 'Define status bar style for iOS',
@@ -116,8 +109,6 @@ export const pwaRules = [
116
109
  severity: 'info',
117
110
  description: 'Manifest should include maskable icons for Android',
118
111
  check: (ctx) => {
119
- if (ctx.hasMaskableIcon === undefined)
120
- return null;
121
112
  if (!ctx.hasMaskableIcon) {
122
113
  return createResult({ id: 'pwa-maskable-icon', name: 'Maskable Icon', category: 'mobile', severity: 'info' }, 'info', 'No maskable icon in manifest', {
123
114
  recommendation: 'Add maskable icon for adaptive icons on Android',
@@ -145,10 +136,11 @@ export const pwaRules = [
145
136
  severity: 'info',
146
137
  description: 'Manifest should define a start_url',
147
138
  check: (ctx) => {
148
- if (!ctx.hasManifest)
149
- return null;
150
- if (ctx.manifestStartUrl === undefined)
151
- return null;
139
+ if (!ctx.hasManifest) {
140
+ return createResult({ id: 'pwa-start-url', name: 'Start URL', category: 'technical', severity: 'info' }, 'info', 'Not applicable (no manifest)', {
141
+ recommendation: 'First add a web app manifest, then define start_url',
142
+ });
143
+ }
152
144
  if (!ctx.manifestStartUrl) {
153
145
  return createResult({ id: 'pwa-start-url', name: 'Start URL', category: 'technical', severity: 'info' }, 'info', 'Manifest missing start_url', {
154
146
  recommendation: 'Define start_url in manifest for PWA launch',
@@ -168,10 +160,11 @@ export const pwaRules = [
168
160
  severity: 'info',
169
161
  description: 'Manifest should define display mode',
170
162
  check: (ctx) => {
171
- if (!ctx.hasManifest)
172
- return null;
173
- if (ctx.manifestDisplay === undefined)
174
- return null;
163
+ if (!ctx.hasManifest) {
164
+ return createResult({ id: 'pwa-display-mode', name: 'Display Mode', category: 'technical', severity: 'info' }, 'info', 'Not applicable (no manifest)', {
165
+ recommendation: 'First add a web app manifest, then define display mode',
166
+ });
167
+ }
175
168
  const display = ctx.manifestDisplay;
176
169
  if (!display) {
177
170
  return createResult({ id: 'pwa-display-mode', name: 'Display Mode', category: 'technical', severity: 'info' }, 'info', 'Manifest missing display mode', {
@@ -198,10 +191,11 @@ export const pwaRules = [
198
191
  severity: 'info',
199
192
  description: 'Manifest should define navigation scope',
200
193
  check: (ctx) => {
201
- if (!ctx.hasManifest)
202
- return null;
203
- if (ctx.manifestScope === undefined)
204
- return null;
194
+ if (!ctx.hasManifest) {
195
+ return createResult({ id: 'pwa-scope', name: 'Navigation Scope', category: 'technical', severity: 'info' }, 'info', 'Not applicable (no manifest)', {
196
+ recommendation: 'First add a web app manifest, then define scope',
197
+ });
198
+ }
205
199
  if (!ctx.manifestScope) {
206
200
  return createResult({ id: 'pwa-scope', name: 'Navigation Scope', category: 'technical', severity: 'info' }, 'info', 'Manifest missing scope', {
207
201
  recommendation: 'Define scope to control PWA navigation boundaries',
@@ -221,11 +215,12 @@ export const pwaRules = [
221
215
  severity: 'info',
222
216
  description: 'Manifest should include multiple icon sizes',
223
217
  check: (ctx) => {
224
- if (!ctx.hasManifest)
225
- return null;
226
- if (ctx.manifestIconSizes === undefined)
227
- return null;
228
- const sizes = ctx.manifestIconSizes;
218
+ if (!ctx.hasManifest) {
219
+ return createResult({ id: 'pwa-icons-sizes', name: 'Icon Sizes', category: 'mobile', severity: 'info' }, 'info', 'Not applicable (no manifest)', {
220
+ recommendation: 'First add a web app manifest with icons',
221
+ });
222
+ }
223
+ const sizes = ctx.manifestIconSizes || [];
229
224
  const requiredSizes = [192, 512];
230
225
  const missingSizes = requiredSizes.filter(s => !sizes.includes(s));
231
226
  if (missingSizes.length > 0) {
@@ -248,10 +243,11 @@ export const pwaRules = [
248
243
  severity: 'info',
249
244
  description: 'Manifest should have a short_name for home screen',
250
245
  check: (ctx) => {
251
- if (!ctx.hasManifest)
252
- return null;
253
- if (ctx.manifestShortName === undefined && ctx.manifestName === undefined)
254
- return null;
246
+ if (!ctx.hasManifest) {
247
+ return createResult({ id: 'pwa-short-name', name: 'Short Name', category: 'technical', severity: 'info' }, 'info', 'Not applicable (no manifest)', {
248
+ recommendation: 'First add a web app manifest with name/short_name',
249
+ });
250
+ }
255
251
  const shortName = ctx.manifestShortName;
256
252
  const name = ctx.manifestName;
257
253
  if (!shortName && !name) {
@@ -283,10 +279,11 @@ export const pwaRules = [
283
279
  severity: 'info',
284
280
  description: 'Manifest should define background_color for splash screen',
285
281
  check: (ctx) => {
286
- if (!ctx.hasManifest)
287
- return null;
288
- if (ctx.manifestBackgroundColor === undefined)
289
- return null;
282
+ if (!ctx.hasManifest) {
283
+ return createResult({ id: 'pwa-background-color', name: 'Background Color', category: 'mobile', severity: 'info' }, 'info', 'Not applicable (no manifest)', {
284
+ recommendation: 'First add a web app manifest, then define background_color',
285
+ });
286
+ }
290
287
  if (!ctx.manifestBackgroundColor) {
291
288
  return createResult({ id: 'pwa-background-color', name: 'Background Color', category: 'mobile', severity: 'info' }, 'info', 'Manifest missing background_color', {
292
289
  recommendation: 'Define background_color for splash screen',
@@ -7,8 +7,9 @@ export const readabilityRules = [
7
7
  severity: 'info',
8
8
  description: 'Content should be easy to read for the target audience',
9
9
  check: (ctx) => {
10
- if (ctx.fleschReadingEase === undefined)
11
- return null;
10
+ if (ctx.fleschReadingEase === undefined) {
11
+ return createResult({ id: 'readability-flesch-score', name: 'Flesch Reading Ease', category: 'content', severity: 'info' }, 'info', 'Not applicable (Flesch Reading Ease data unavailable)', { recommendation: 'This rule analyzes content readability using the Flesch Reading Ease formula' });
12
+ }
12
13
  const score = ctx.fleschReadingEase;
13
14
  let level;
14
15
  let status;
@@ -49,8 +50,9 @@ export const readabilityRules = [
49
50
  severity: 'info',
50
51
  description: 'Sentences should be concise for better readability',
51
52
  check: (ctx) => {
52
- if (ctx.avgSentenceLength === undefined)
53
- return null;
53
+ if (ctx.avgSentenceLength === undefined) {
54
+ return createResult({ id: 'readability-sentence-length', name: 'Sentence Length', category: 'content', severity: 'info' }, 'info', 'Not applicable (sentence length data unavailable)', { recommendation: 'This rule checks average sentence length for optimal readability' });
55
+ }
54
56
  const avgLength = ctx.avgSentenceLength;
55
57
  if (avgLength > 30) {
56
58
  return createResult({ id: 'readability-sentence-length', name: 'Sentence Length', category: 'content', severity: 'info' }, 'warn', `Average sentence length: ${Math.round(avgLength)} words (too long)`, {
@@ -81,8 +83,9 @@ export const readabilityRules = [
81
83
  severity: 'info',
82
84
  description: 'Paragraphs should be short for web readability',
83
85
  check: (ctx) => {
84
- if (ctx.avgParagraphLength === undefined)
85
- return null;
86
+ if (ctx.avgParagraphLength === undefined) {
87
+ return createResult({ id: 'readability-paragraph-length', name: 'Paragraph Length', category: 'content', severity: 'info' }, 'info', 'Not applicable (paragraph length data unavailable)', { recommendation: 'This rule checks average paragraph length for web readability' });
88
+ }
86
89
  const avgLength = ctx.avgParagraphLength;
87
90
  if (avgLength > 150) {
88
91
  return createResult({ id: 'readability-paragraph-length', name: 'Paragraph Length', category: 'content', severity: 'info' }, 'warn', `Average paragraph length: ${Math.round(avgLength)} words (too long)`, {
@@ -109,8 +112,9 @@ export const readabilityRules = [
109
112
  severity: 'info',
110
113
  description: 'Limit passive voice for clearer writing',
111
114
  check: (ctx) => {
112
- if (ctx.passiveVoicePercentage === undefined)
113
- return null;
115
+ if (ctx.passiveVoicePercentage === undefined) {
116
+ return createResult({ id: 'readability-passive-voice', name: 'Passive Voice', category: 'content', severity: 'info' }, 'info', 'Not applicable (passive voice data unavailable)', { recommendation: 'This rule checks passive voice usage for clearer writing' });
117
+ }
114
118
  const percentage = ctx.passiveVoicePercentage;
115
119
  if (percentage > 20) {
116
120
  return createResult({ id: 'readability-passive-voice', name: 'Passive Voice', category: 'content', severity: 'info' }, 'warn', `Passive voice: ${Math.round(percentage)}% of sentences`, {
@@ -127,7 +131,7 @@ export const readabilityRules = [
127
131
  recommendation: 'Consider reducing passive voice usage',
128
132
  });
129
133
  }
130
- return null;
134
+ return createResult({ id: 'readability-passive-voice', name: 'Passive Voice', category: 'content', severity: 'info' }, 'pass', `Passive voice usage acceptable (${Math.round(percentage)}%)`);
131
135
  },
132
136
  },
133
137
  {
@@ -137,8 +141,9 @@ export const readabilityRules = [
137
141
  severity: 'info',
138
142
  description: 'Use transition words for better flow',
139
143
  check: (ctx) => {
140
- if (ctx.transitionWordPercentage === undefined)
141
- return null;
144
+ if (ctx.transitionWordPercentage === undefined) {
145
+ return createResult({ id: 'readability-transition-words', name: 'Transition Words', category: 'content', severity: 'info' }, 'info', 'Not applicable (transition word data unavailable)', { recommendation: 'This rule checks for transition words that improve content flow' });
146
+ }
142
147
  const percentage = ctx.transitionWordPercentage;
143
148
  if (percentage < 20) {
144
149
  return createResult({ id: 'readability-transition-words', name: 'Transition Words', category: 'content', severity: 'info' }, 'info', `Transition words: ${Math.round(percentage)}% of sentences`, {
@@ -161,8 +166,9 @@ export const readabilityRules = [
161
166
  severity: 'info',
162
167
  description: 'Break content with subheadings every 300 words',
163
168
  check: (ctx) => {
164
- if (!ctx.wordCount || !ctx.h2Count)
165
- return null;
169
+ if (!ctx.wordCount || !ctx.h2Count) {
170
+ return createResult({ id: 'readability-subheading-distribution', name: 'Subheading Distribution', category: 'content', severity: 'info' }, 'info', 'Not applicable (word count or heading data unavailable)', { recommendation: 'This rule checks for proper subheading distribution to break up content' });
171
+ }
166
172
  const wordsPerSubheading = ctx.wordCount / (ctx.h2Count + 1);
167
173
  if (ctx.wordCount > 500 && wordsPerSubheading > 400) {
168
174
  return createResult({ id: 'readability-subheading-distribution', name: 'Subheading Distribution', category: 'content', severity: 'info' }, 'warn', `${Math.round(wordsPerSubheading)} words between subheadings (too many)`, {
@@ -189,8 +195,9 @@ export const readabilityRules = [
189
195
  severity: 'info',
190
196
  description: 'Use varied sentence structures and word choices',
191
197
  check: (ctx) => {
192
- if (ctx.consecutiveSentenceStarts === undefined)
193
- return null;
198
+ if (ctx.consecutiveSentenceStarts === undefined) {
199
+ return createResult({ id: 'readability-text-variety', name: 'Text Variety', category: 'content', severity: 'info' }, 'info', 'Not applicable (sentence variety data unavailable)', { recommendation: 'This rule checks for varied sentence structures and beginnings' });
200
+ }
194
201
  if (ctx.consecutiveSentenceStarts > 3) {
195
202
  return createResult({ id: 'readability-text-variety', name: 'Text Variety', category: 'content', severity: 'info' }, 'info', `${ctx.consecutiveSentenceStarts} consecutive sentences start similarly`, {
196
203
  recommendation: 'Vary sentence beginnings for better flow',
@@ -199,7 +206,7 @@ export const readabilityRules = [
199
206
  },
200
207
  });
201
208
  }
202
- return null;
209
+ return createResult({ id: 'readability-text-variety', name: 'Text Variety', category: 'content', severity: 'info' }, 'pass', 'Good sentence variety');
203
210
  },
204
211
  },
205
212
  {
@@ -209,8 +216,9 @@ export const readabilityRules = [
209
216
  severity: 'info',
210
217
  description: 'Avoid overly complex vocabulary',
211
218
  check: (ctx) => {
212
- if (ctx.complexWordPercentage === undefined)
213
- return null;
219
+ if (ctx.complexWordPercentage === undefined) {
220
+ return createResult({ id: 'readability-word-complexity', name: 'Word Complexity', category: 'content', severity: 'info' }, 'info', 'Not applicable (word complexity data unavailable)', { recommendation: 'This rule checks for complex vocabulary that may reduce readability' });
221
+ }
214
222
  if (ctx.complexWordPercentage > 15) {
215
223
  return createResult({ id: 'readability-word-complexity', name: 'Word Complexity', category: 'content', severity: 'info' }, 'warn', `Complex words: ${Math.round(ctx.complexWordPercentage)}%`, {
216
224
  recommendation: 'Use simpler vocabulary where possible',
@@ -224,7 +232,7 @@ export const readabilityRules = [
224
232
  if (ctx.complexWordPercentage > 10) {
225
233
  return createResult({ id: 'readability-word-complexity', name: 'Word Complexity', category: 'content', severity: 'info' }, 'info', `Complex words: ${Math.round(ctx.complexWordPercentage)}%`);
226
234
  }
227
- return null;
235
+ return createResult({ id: 'readability-word-complexity', name: 'Word Complexity', category: 'content', severity: 'info' }, 'pass', `Word complexity acceptable (${Math.round(ctx.complexWordPercentage)}%)`);
228
236
  },
229
237
  },
230
238
  {
@@ -234,8 +242,9 @@ export const readabilityRules = [
234
242
  severity: 'info',
235
243
  description: 'Use lists to improve scannability',
236
244
  check: (ctx) => {
237
- if (!ctx.wordCount || ctx.listCount === undefined)
238
- return null;
245
+ if (!ctx.wordCount || ctx.listCount === undefined) {
246
+ return createResult({ id: 'readability-list-usage', name: 'List Usage', category: 'content', severity: 'info' }, 'info', 'Not applicable (word count or list data unavailable)', { recommendation: 'This rule checks for proper use of lists to improve scannability' });
247
+ }
239
248
  if (ctx.wordCount > 500 && ctx.listCount === 0) {
240
249
  return createResult({ id: 'readability-list-usage', name: 'List Usage', category: 'content', severity: 'info' }, 'info', 'No lists found in long-form content', {
241
250
  recommendation: 'Consider using bullet points or numbered lists',
@@ -249,7 +258,7 @@ export const readabilityRules = [
249
258
  if (ctx.listCount > 0) {
250
259
  return createResult({ id: 'readability-list-usage', name: 'List Usage', category: 'content', severity: 'info' }, 'pass', `${ctx.listCount} list(s) found`);
251
260
  }
252
- return null;
261
+ return createResult({ id: 'readability-list-usage', name: 'List Usage', category: 'content', severity: 'info' }, 'pass', 'Content length does not require lists');
253
262
  },
254
263
  },
255
264
  ];
@@ -7,8 +7,9 @@ export const redirectRules = [
7
7
  severity: 'warning',
8
8
  description: 'Redirect chains should not exceed 3 hops',
9
9
  check: (ctx) => {
10
- if (ctx.redirectChain === undefined)
11
- return null;
10
+ if (ctx.redirectChain === undefined) {
11
+ return createResult({ id: 'redirect-chain-length', name: 'Redirect Chain Length', category: 'technical', severity: 'warning' }, 'info', 'Unable to check redirect chain (data unavailable)', { recommendation: 'Ensure redirect analysis completed' });
12
+ }
12
13
  const chainLength = ctx.redirectChain.length;
13
14
  if (chainLength === 0) {
14
15
  return createResult({ id: 'redirect-chain-length', name: 'Redirect Chain Length', category: 'technical', severity: 'warning' }, 'pass', 'No redirects detected');
@@ -50,8 +51,9 @@ export const redirectRules = [
50
51
  severity: 'error',
51
52
  description: 'Pages should not create redirect loops',
52
53
  check: (ctx) => {
53
- if (!ctx.redirectChain || ctx.redirectChain.length === 0)
54
- return null;
54
+ if (!ctx.redirectChain || ctx.redirectChain.length === 0) {
55
+ return createResult({ id: 'redirect-loop', name: 'Redirect Loop', category: 'technical', severity: 'error' }, 'pass', 'No redirect chain to check for loops');
56
+ }
55
57
  const urls = ctx.redirectChain.map(r => r.from);
56
58
  urls.push(ctx.redirectChain[ctx.redirectChain.length - 1].to);
57
59
  const seen = new Set();
@@ -74,7 +76,7 @@ export const redirectRules = [
74
76
  }
75
77
  });
76
78
  }
77
- return null;
79
+ return createResult({ id: 'redirect-loop', name: 'Redirect Loop', category: 'technical', severity: 'error' }, 'pass', 'No redirect loop detected');
78
80
  },
79
81
  },
80
82
  {
@@ -84,8 +86,9 @@ export const redirectRules = [
84
86
  severity: 'info',
85
87
  description: 'Permanent content moves should use 301 redirects',
86
88
  check: (ctx) => {
87
- if (!ctx.redirectChain || ctx.redirectChain.length === 0)
88
- return null;
89
+ if (!ctx.redirectChain || ctx.redirectChain.length === 0) {
90
+ return createResult({ id: 'redirect-type', name: 'Redirect Type', category: 'technical', severity: 'info' }, 'info', 'No redirects to analyze', { recommendation: 'Page loaded without redirects' });
91
+ }
89
92
  const temporary = ctx.redirectChain.filter(r => r.status === 302 || r.status === 307);
90
93
  const permanent = ctx.redirectChain.filter(r => r.status === 301 || r.status === 308);
91
94
  if (temporary.length > 0 && permanent.length === 0) {
@@ -101,7 +104,7 @@ export const redirectRules = [
101
104
  if (permanent.length > 0) {
102
105
  return createResult({ id: 'redirect-type', name: 'Redirect Type', category: 'technical', severity: 'info' }, 'pass', 'Using permanent redirect (301)');
103
106
  }
104
- return null;
107
+ return createResult({ id: 'redirect-type', name: 'Redirect Type', category: 'technical', severity: 'info' }, 'info', 'Unknown redirect type', { recommendation: 'Check redirect status codes' });
105
108
  },
106
109
  },
107
110
  {
@@ -111,8 +114,9 @@ export const redirectRules = [
111
114
  severity: 'warning',
112
115
  description: 'Site should redirect consistently to either WWW or non-WWW version',
113
116
  check: (ctx) => {
114
- if (ctx.wwwConsistency === undefined)
115
- return null;
117
+ if (ctx.wwwConsistency === undefined) {
118
+ return createResult({ id: 'www-consistency', name: 'WWW vs Non-WWW', category: 'canonicalization', severity: 'warning' }, 'info', 'Unable to check WWW consistency (data unavailable)', { recommendation: 'Ensure WWW/non-WWW analysis completed' });
119
+ }
116
120
  const { canonicalHasWww, urlHasWww, redirectsToCanonical } = ctx.wwwConsistency;
117
121
  if (canonicalHasWww !== urlHasWww && !redirectsToCanonical) {
118
122
  return createResult({ id: 'www-consistency', name: 'WWW vs Non-WWW', category: 'canonicalization', severity: 'warning' }, 'warn', 'WWW/non-WWW version accessible without redirect', {
@@ -134,8 +138,9 @@ export const redirectRules = [
134
138
  severity: 'warning',
135
139
  description: 'HTTP version should redirect to HTTPS',
136
140
  check: (ctx) => {
137
- if (ctx.httpRedirectsToHttps === undefined)
138
- return null;
141
+ if (ctx.httpRedirectsToHttps === undefined) {
142
+ return createResult({ id: 'http-to-https-redirect', name: 'HTTP to HTTPS Redirect', category: 'security', severity: 'warning' }, 'info', 'Unable to check HTTP to HTTPS redirect (data unavailable)', { recommendation: 'Ensure HTTPS redirect analysis completed' });
143
+ }
139
144
  if (!ctx.httpRedirectsToHttps) {
140
145
  return createResult({ id: 'http-to-https-redirect', name: 'HTTP to HTTPS Redirect', category: 'security', severity: 'warning' }, 'warn', 'HTTP version does not redirect to HTTPS', {
141
146
  recommendation: 'Configure server to redirect HTTP traffic to HTTPS',
@@ -157,8 +162,9 @@ export const redirectRules = [
157
162
  severity: 'info',
158
163
  description: 'Detects redirects to different domains',
159
164
  check: (ctx) => {
160
- if (!ctx.redirectChain || ctx.redirectChain.length === 0)
161
- return null;
165
+ if (!ctx.redirectChain || ctx.redirectChain.length === 0) {
166
+ return createResult({ id: 'cross-domain-redirect', name: 'Cross-Domain Redirect', category: 'technical', severity: 'info' }, 'info', 'No redirect chain to check', { recommendation: 'Page loaded without redirects' });
167
+ }
162
168
  const crossDomainRedirects = ctx.redirectChain.filter(r => {
163
169
  try {
164
170
  const fromHost = new URL(r.from).hostname;
@@ -178,7 +184,7 @@ export const redirectRules = [
178
184
  }
179
185
  });
180
186
  }
181
- return null;
187
+ return createResult({ id: 'cross-domain-redirect', name: 'Cross-Domain Redirect', category: 'technical', severity: 'info' }, 'pass', 'No cross-domain redirects');
182
188
  },
183
189
  },
184
190
  ];