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
@@ -21,7 +21,7 @@ export const securityRules = [
21
21
  if (ctx.isHttps === true) {
22
22
  return createResult({ id: 'https-required', name: 'HTTPS', category: 'security', severity: 'error' }, 'pass', 'Page is served over HTTPS');
23
23
  }
24
- return null;
24
+ return createResult({ id: 'https-required', name: 'HTTPS', category: 'security', severity: 'error' }, 'info', 'Not applicable (HTTPS status unavailable)', { recommendation: 'This rule checks if the page is served over HTTPS when protocol information is available' });
25
25
  },
26
26
  },
27
27
  {
@@ -42,7 +42,7 @@ export const securityRules = [
42
42
  },
43
43
  });
44
44
  }
45
- return null;
45
+ return createResult({ id: 'mixed-content', name: 'Mixed Content', category: 'security', severity: 'error' }, 'info', 'Not applicable (no mixed content detected or HTTPS not used)', { recommendation: 'This rule checks for HTTP resources on HTTPS pages when mixed content is detected' });
46
46
  },
47
47
  },
48
48
  {
@@ -52,8 +52,9 @@ export const securityRules = [
52
52
  severity: 'warning',
53
53
  description: 'HTTP traffic should redirect to HTTPS',
54
54
  check: (ctx) => {
55
- if (ctx.httpRedirectsToHttps === undefined)
56
- return null;
55
+ if (ctx.httpRedirectsToHttps === undefined) {
56
+ return createResult({ id: 'http-redirect', name: 'HTTP to HTTPS Redirect', category: 'security', severity: 'warning' }, 'info', 'Not applicable (HTTP redirect status unavailable)', { recommendation: 'This rule checks if HTTP traffic redirects to HTTPS when redirect information is available' });
57
+ }
57
58
  if (!ctx.httpRedirectsToHttps) {
58
59
  return createResult({ id: 'http-redirect', name: 'HTTP to HTTPS Redirect', category: 'security', severity: 'warning' }, 'warn', 'HTTP does not redirect to HTTPS', {
59
60
  recommendation: 'Configure server to redirect all HTTP traffic to HTTPS',
@@ -67,6 +68,32 @@ export const securityRules = [
67
68
  return createResult({ id: 'http-redirect', name: 'HTTP to HTTPS Redirect', category: 'security', severity: 'warning' }, 'pass', 'HTTP redirects to HTTPS');
68
69
  },
69
70
  },
71
+ {
72
+ id: 'internal-links-https',
73
+ name: 'Internal Links Use HTTPS',
74
+ category: 'security',
75
+ severity: 'warning',
76
+ description: 'Internal links should use HTTPS to avoid mixed content and redirect chains',
77
+ check: (ctx) => {
78
+ if (ctx.internalHttpLinks === undefined) {
79
+ return createResult({ id: 'internal-links-https', name: 'Internal Links Use HTTPS', category: 'security', severity: 'warning' }, 'info', 'Not applicable (internal link data unavailable)', { recommendation: 'This rule checks internal links for HTTPS usage when link analysis is available' });
80
+ }
81
+ if (ctx.internalHttpLinks > 0) {
82
+ const examples = ctx.internalHttpLinkUrls?.slice(0, 3) || [];
83
+ return createResult({ id: 'internal-links-https', name: 'Internal Links Use HTTPS', category: 'security', severity: 'warning' }, 'warn', `${ctx.internalHttpLinks} internal link(s) use HTTP instead of HTTPS`, {
84
+ value: ctx.internalHttpLinks,
85
+ recommendation: 'Update all internal links to use HTTPS URLs',
86
+ evidence: {
87
+ found: `${ctx.internalHttpLinks} HTTP internal links${examples.length > 0 ? `: ${examples.join(', ')}` : ''}`,
88
+ expected: 'All internal links should use HTTPS',
89
+ impact: 'HTTP links cause unnecessary redirects, slow page loads, and may trigger mixed content warnings. Search engines prefer sites with consistent HTTPS usage.',
90
+ learnMore: 'https://web.dev/why-https-matters/',
91
+ },
92
+ });
93
+ }
94
+ return createResult({ id: 'internal-links-https', name: 'Internal Links Use HTTPS', category: 'security', severity: 'warning' }, 'pass', 'All internal links use HTTPS');
95
+ },
96
+ },
70
97
  {
71
98
  id: 'security-csp-exists',
72
99
  name: 'Content Security Policy (CSP)',
@@ -74,8 +101,9 @@ export const securityRules = [
74
101
  severity: 'warning',
75
102
  description: 'Content Security Policy header should be present to mitigate XSS attacks.',
76
103
  check: (ctx) => {
77
- if (!ctx.responseHeaders)
78
- return null;
104
+ if (!ctx.responseHeaders) {
105
+ return createResult({ id: 'security-csp-exists', name: 'Content Security Policy', category: 'security', severity: 'warning' }, 'info', 'Not applicable (response headers unavailable)', { recommendation: 'This rule checks for CSP headers when HTTP response headers are available' });
106
+ }
79
107
  const cspHeader = ctx.responseHeaders['content-security-policy'] || ctx.responseHeaders['Content-Security-Policy'];
80
108
  if (!cspHeader) {
81
109
  return createResult({ id: 'security-csp-exists', name: 'Content Security Policy', category: 'security', severity: 'warning' }, 'warn', 'Content-Security-Policy header is missing', {
@@ -97,11 +125,13 @@ export const securityRules = [
97
125
  severity: 'warning',
98
126
  description: 'CSP should be effective against XSS attacks',
99
127
  check: (ctx) => {
100
- if (!ctx.responseHeaders)
101
- return null;
128
+ if (!ctx.responseHeaders) {
129
+ return createResult({ id: 'security-csp-xss-effective', name: 'CSP XSS Effectiveness', category: 'security', severity: 'warning' }, 'info', 'Not applicable (response headers unavailable)', { recommendation: 'This rule checks CSP effectiveness when HTTP response headers are available' });
130
+ }
102
131
  const cspHeader = ctx.responseHeaders['content-security-policy'] || ctx.responseHeaders['Content-Security-Policy'];
103
- if (!cspHeader)
104
- return null;
132
+ if (!cspHeader) {
133
+ return createResult({ id: 'security-csp-xss-effective', name: 'CSP XSS Effectiveness', category: 'security', severity: 'warning' }, 'info', 'Not applicable (CSP header not present)', { recommendation: 'This rule checks CSP effectiveness when a Content-Security-Policy header exists' });
134
+ }
105
135
  const csp = String(cspHeader).toLowerCase();
106
136
  const weaknesses = [];
107
137
  if (csp.includes("'unsafe-inline'") && !csp.includes("'strict-dynamic'") && !csp.includes("'nonce-")) {
@@ -137,11 +167,13 @@ export const securityRules = [
137
167
  severity: 'error',
138
168
  description: 'CSP should have script-src and object-src directives to prevent unsafe script execution',
139
169
  check: (ctx) => {
140
- if (!ctx.responseHeaders)
141
- return null;
170
+ if (!ctx.responseHeaders) {
171
+ return createResult({ id: 'security-csp-directives', name: 'CSP Required Directives', category: 'security', severity: 'error' }, 'info', 'Not applicable (response headers unavailable)', { recommendation: 'This rule checks CSP directives when HTTP response headers are available' });
172
+ }
142
173
  const cspHeader = ctx.responseHeaders['content-security-policy'] || ctx.responseHeaders['Content-Security-Policy'];
143
- if (!cspHeader)
144
- return null;
174
+ if (!cspHeader) {
175
+ return createResult({ id: 'security-csp-directives', name: 'CSP Required Directives', category: 'security', severity: 'error' }, 'info', 'Not applicable (CSP header not present)', { recommendation: 'This rule checks CSP directives when a Content-Security-Policy header exists' });
176
+ }
145
177
  const csp = String(cspHeader).toLowerCase();
146
178
  const missingDirectives = [];
147
179
  if (!csp.includes('script-src') && !csp.includes('default-src')) {
@@ -196,8 +228,9 @@ export const securityRules = [
196
228
  severity: 'warning',
197
229
  description: 'HSTS header forces secure connections and improves SEO indirectly.',
198
230
  check: (ctx) => {
199
- if (!ctx.responseHeaders)
200
- return null;
231
+ if (!ctx.responseHeaders) {
232
+ return createResult({ id: 'security-hsts-exists', name: 'HSTS Header', category: 'security', severity: 'warning' }, 'info', 'Not applicable (response headers unavailable)', { recommendation: 'This rule checks for HSTS headers when HTTP response headers are available' });
233
+ }
201
234
  const hstsHeader = ctx.responseHeaders['strict-transport-security'] || ctx.responseHeaders['Strict-Transport-Security'];
202
235
  if (!hstsHeader) {
203
236
  return createResult({ id: 'security-hsts-exists', name: 'HSTS Header', category: 'security', severity: 'warning' }, 'warn', 'Strict-Transport-Security header is missing', {
@@ -219,11 +252,13 @@ export const securityRules = [
219
252
  severity: 'info',
220
253
  description: 'HSTS should have a strong policy with long max-age and includeSubDomains',
221
254
  check: (ctx) => {
222
- if (!ctx.responseHeaders)
223
- return null;
255
+ if (!ctx.responseHeaders) {
256
+ return createResult({ id: 'security-hsts-strong', name: 'Strong HSTS Policy', category: 'security', severity: 'info' }, 'info', 'Not applicable (response headers unavailable)', { recommendation: 'This rule checks HSTS policy strength when HTTP response headers are available' });
257
+ }
224
258
  const hstsHeader = ctx.responseHeaders['strict-transport-security'] || ctx.responseHeaders['Strict-Transport-Security'];
225
- if (!hstsHeader)
226
- return null;
259
+ if (!hstsHeader) {
260
+ return createResult({ id: 'security-hsts-strong', name: 'Strong HSTS Policy', category: 'security', severity: 'info' }, 'info', 'Not applicable (HSTS header not present)', { recommendation: 'This rule checks HSTS policy strength when a Strict-Transport-Security header exists' });
261
+ }
227
262
  const hsts = String(hstsHeader).toLowerCase();
228
263
  const weaknesses = [];
229
264
  const maxAgeMatch = hsts.match(/max-age\s*=\s*(\d+)/);
@@ -258,8 +293,9 @@ export const securityRules = [
258
293
  severity: 'info',
259
294
  description: 'COOP ensures proper origin isolation for security',
260
295
  check: (ctx) => {
261
- if (!ctx.responseHeaders)
262
- return null;
296
+ if (!ctx.responseHeaders) {
297
+ return createResult({ id: 'security-coop', name: 'Cross-Origin-Opener-Policy', category: 'security', severity: 'info' }, 'info', 'Not applicable (response headers unavailable)', { recommendation: 'This rule checks for COOP headers when HTTP response headers are available' });
298
+ }
263
299
  const coopHeader = ctx.responseHeaders['cross-origin-opener-policy'] || ctx.responseHeaders['Cross-Origin-Opener-Policy'];
264
300
  if (!coopHeader) {
265
301
  return createResult({ id: 'security-coop', name: 'Cross-Origin-Opener-Policy', category: 'security', severity: 'info' }, 'info', 'Cross-Origin-Opener-Policy header is missing', {
@@ -289,8 +325,9 @@ export const securityRules = [
289
325
  severity: 'info',
290
326
  description: 'COEP prevents loading cross-origin resources without explicit permission',
291
327
  check: (ctx) => {
292
- if (!ctx.responseHeaders)
293
- return null;
328
+ if (!ctx.responseHeaders) {
329
+ return createResult({ id: 'security-coep', name: 'Cross-Origin-Embedder-Policy', category: 'security', severity: 'info' }, 'info', 'Not applicable (response headers unavailable)', { recommendation: 'This rule checks for COEP headers when HTTP response headers are available' });
330
+ }
294
331
  const coepHeader = ctx.responseHeaders['cross-origin-embedder-policy'] || ctx.responseHeaders['Cross-Origin-Embedder-Policy'];
295
332
  if (!coepHeader) {
296
333
  return createResult({ id: 'security-coep', name: 'Cross-Origin-Embedder-Policy', category: 'security', severity: 'info' }, 'info', 'Cross-Origin-Embedder-Policy header is missing', {
@@ -312,11 +349,13 @@ export const securityRules = [
312
349
  severity: 'info',
313
350
  description: 'Trusted Types help prevent DOM-based XSS attacks',
314
351
  check: (ctx) => {
315
- if (!ctx.responseHeaders)
316
- return null;
352
+ if (!ctx.responseHeaders) {
353
+ return createResult({ id: 'security-trusted-types', name: 'Trusted Types', category: 'security', severity: 'info' }, 'info', 'Not applicable (response headers unavailable)', { recommendation: 'This rule checks for Trusted Types in CSP when HTTP response headers are available' });
354
+ }
317
355
  const cspHeader = ctx.responseHeaders['content-security-policy'] || ctx.responseHeaders['Content-Security-Policy'];
318
- if (!cspHeader)
319
- return null;
356
+ if (!cspHeader) {
357
+ return createResult({ id: 'security-trusted-types', name: 'Trusted Types', category: 'security', severity: 'info' }, 'info', 'Not applicable (CSP header not present)', { recommendation: 'This rule checks for Trusted Types when a Content-Security-Policy header exists' });
358
+ }
320
359
  const csp = String(cspHeader).toLowerCase();
321
360
  if (csp.includes('require-trusted-types-for')) {
322
361
  return createResult({ id: 'security-trusted-types', name: 'Trusted Types', category: 'security', severity: 'info' }, 'pass', 'Trusted Types policy enabled via CSP');
@@ -338,8 +377,9 @@ export const securityRules = [
338
377
  severity: 'warning',
339
378
  description: 'X-Frame-Options header should be present to prevent clickjacking.',
340
379
  check: (ctx) => {
341
- if (!ctx.responseHeaders)
342
- return null;
380
+ if (!ctx.responseHeaders) {
381
+ return createResult({ id: 'security-xfo-exists', name: 'X-Frame-Options', category: 'security', severity: 'warning' }, 'info', 'Not applicable (response headers unavailable)', { recommendation: 'This rule checks for X-Frame-Options headers when HTTP response headers are available' });
382
+ }
343
383
  const xfoHeader = ctx.responseHeaders['x-frame-options'] || ctx.responseHeaders['X-Frame-Options'];
344
384
  if (!xfoHeader) {
345
385
  return createResult({ id: 'security-xfo-exists', name: 'X-Frame-Options', category: 'security', severity: 'warning' }, 'warn', 'X-Frame-Options header is missing', {
@@ -361,12 +401,14 @@ export const securityRules = [
361
401
  severity: 'info',
362
402
  description: 'CSP frame-ancestors is the modern replacement for X-Frame-Options',
363
403
  check: (ctx) => {
364
- if (!ctx.responseHeaders)
365
- return null;
404
+ if (!ctx.responseHeaders) {
405
+ return createResult({ id: 'security-frame-ancestors', name: 'CSP frame-ancestors', category: 'security', severity: 'info' }, 'info', 'Not applicable (response headers unavailable)', { recommendation: 'This rule checks for CSP frame-ancestors when HTTP response headers are available' });
406
+ }
366
407
  const cspHeader = ctx.responseHeaders['content-security-policy'] || ctx.responseHeaders['Content-Security-Policy'];
367
408
  const xfoHeader = ctx.responseHeaders['x-frame-options'] || ctx.responseHeaders['X-Frame-Options'];
368
- if (!cspHeader && !xfoHeader)
369
- return null;
409
+ if (!cspHeader && !xfoHeader) {
410
+ return createResult({ id: 'security-frame-ancestors', name: 'CSP frame-ancestors', category: 'security', severity: 'info' }, 'info', 'Not applicable (neither CSP nor X-Frame-Options present)', { recommendation: 'This rule checks CSP frame-ancestors when clickjacking protection headers are present' });
411
+ }
370
412
  if (cspHeader && String(cspHeader).toLowerCase().includes('frame-ancestors')) {
371
413
  return createResult({ id: 'security-frame-ancestors', name: 'CSP frame-ancestors', category: 'security', severity: 'info' }, 'pass', 'CSP frame-ancestors directive present (modern clickjacking protection)');
372
414
  }
@@ -380,7 +422,7 @@ export const securityRules = [
380
422
  },
381
423
  });
382
424
  }
383
- return null;
425
+ return createResult({ id: 'security-frame-ancestors', name: 'CSP frame-ancestors', category: 'security', severity: 'info' }, 'info', 'Not applicable (clickjacking protection configured without frame-ancestors)', { recommendation: 'This rule checks modern frame-ancestors directive in CSP for enhanced clickjacking protection' });
384
426
  },
385
427
  },
386
428
  {
@@ -390,8 +432,9 @@ export const securityRules = [
390
432
  severity: 'warning',
391
433
  description: 'Review Access-Control-Allow-Origin header for proper CORS configuration.',
392
434
  check: (ctx) => {
393
- if (!ctx.responseHeaders)
394
- return null;
435
+ if (!ctx.responseHeaders) {
436
+ return createResult({ id: 'security-cors-config', name: 'CORS Configuration', category: 'security', severity: 'warning' }, 'info', 'Not applicable (response headers unavailable)', { recommendation: 'This rule checks CORS configuration when HTTP response headers are available' });
437
+ }
395
438
  const acaoHeader = ctx.responseHeaders['access-control-allow-origin'] || ctx.responseHeaders['Access-Control-Allow-Origin'];
396
439
  if (acaoHeader === '*') {
397
440
  return createResult({ id: 'security-cors-config', name: 'CORS Configuration', category: 'security', severity: 'warning' }, 'warn', 'Access-Control-Allow-Origin is set to "*"', {
@@ -416,8 +459,9 @@ export const securityRules = [
416
459
  severity: 'warning',
417
460
  description: 'X-Content-Type-Options header prevents MIME sniffing attacks.',
418
461
  check: (ctx) => {
419
- if (!ctx.responseHeaders)
420
- return null;
462
+ if (!ctx.responseHeaders) {
463
+ return createResult({ id: 'security-xcto-exists', name: 'X-Content-Type-Options', category: 'security', severity: 'warning' }, 'info', 'Not applicable (response headers unavailable)', { recommendation: 'This rule checks for X-Content-Type-Options headers when HTTP response headers are available' });
464
+ }
421
465
  const xctoHeader = ctx.responseHeaders['x-content-type-options'] || ctx.responseHeaders['X-Content-Type-Options'];
422
466
  if (!xctoHeader) {
423
467
  return createResult({ id: 'security-xcto-exists', name: 'X-Content-Type-Options', category: 'security', severity: 'warning' }, 'warn', 'X-Content-Type-Options header is missing', {
@@ -438,8 +482,9 @@ export const securityRules = [
438
482
  severity: 'info',
439
483
  description: 'Referrer-Policy controls how much referrer information is sent with requests.',
440
484
  check: (ctx) => {
441
- if (!ctx.responseHeaders)
442
- return null;
485
+ if (!ctx.responseHeaders) {
486
+ return createResult({ id: 'security-rp-exists', name: 'Referrer-Policy', category: 'security', severity: 'info' }, 'info', 'Not applicable (response headers unavailable)', { recommendation: 'This rule checks for Referrer-Policy headers when HTTP response headers are available' });
487
+ }
443
488
  const rpHeader = ctx.responseHeaders['referrer-policy'] || ctx.responseHeaders['Referrer-Policy'];
444
489
  if (!rpHeader) {
445
490
  return createResult({ id: 'security-rp-exists', name: 'Referrer-Policy', category: 'security', severity: 'info' }, 'info', 'Referrer-Policy header is missing', {
@@ -460,8 +505,9 @@ export const securityRules = [
460
505
  severity: 'info',
461
506
  description: 'Permissions-Policy controls browser features available to the page.',
462
507
  check: (ctx) => {
463
- if (!ctx.responseHeaders)
464
- return null;
508
+ if (!ctx.responseHeaders) {
509
+ return createResult({ id: 'security-pp-exists', name: 'Permissions-Policy', category: 'security', severity: 'info' }, 'info', 'Not applicable (response headers unavailable)', { recommendation: 'This rule checks for Permissions-Policy headers when HTTP response headers are available' });
510
+ }
465
511
  const ppHeader = ctx.responseHeaders['permissions-policy'] || ctx.responseHeaders['Permissions-Policy'];
466
512
  if (!ppHeader) {
467
513
  return createResult({ id: 'security-pp-exists', name: 'Permissions-Policy', category: 'security', severity: 'info' }, 'info', 'Permissions-Policy header is missing', {
@@ -482,8 +528,9 @@ export const securityRules = [
482
528
  severity: 'info',
483
529
  description: 'X-XSS-Protection is deprecated but may still provide protection in older browsers',
484
530
  check: (ctx) => {
485
- if (!ctx.responseHeaders)
486
- return null;
531
+ if (!ctx.responseHeaders) {
532
+ return createResult({ id: 'security-xxss', name: 'X-XSS-Protection', category: 'security', severity: 'info' }, 'info', 'Not applicable (response headers unavailable)', { recommendation: 'This rule checks for deprecated X-XSS-Protection headers when HTTP response headers are available' });
533
+ }
487
534
  const xxssHeader = ctx.responseHeaders['x-xss-protection'] || ctx.responseHeaders['X-XSS-Protection'];
488
535
  if (xxssHeader && String(xxssHeader).includes('1')) {
489
536
  return createResult({ id: 'security-xxss', name: 'X-XSS-Protection', category: 'security', severity: 'info' }, 'info', 'X-XSS-Protection is enabled but deprecated', {
@@ -496,7 +543,7 @@ export const securityRules = [
496
543
  },
497
544
  });
498
545
  }
499
- return null;
546
+ return createResult({ id: 'security-xxss', name: 'X-XSS-Protection', category: 'security', severity: 'info' }, 'info', 'Not applicable (X-XSS-Protection not present or disabled)', { recommendation: 'This rule checks for deprecated X-XSS-Protection headers when present' });
500
547
  },
501
548
  },
502
549
  {
@@ -506,8 +553,9 @@ export const securityRules = [
506
553
  severity: 'info',
507
554
  description: 'CORP restricts which origins can load your resources',
508
555
  check: (ctx) => {
509
- if (!ctx.responseHeaders)
510
- return null;
556
+ if (!ctx.responseHeaders) {
557
+ return createResult({ id: 'security-corp', name: 'Cross-Origin-Resource-Policy', category: 'security', severity: 'info' }, 'info', 'Not applicable (response headers unavailable)', { recommendation: 'This rule checks for CORP headers when HTTP response headers are available' });
558
+ }
511
559
  const corpHeader = ctx.responseHeaders['cross-origin-resource-policy'] || ctx.responseHeaders['Cross-Origin-Resource-Policy'];
512
560
  if (!corpHeader) {
513
561
  return createResult({ id: 'security-corp', name: 'Cross-Origin-Resource-Policy', category: 'security', severity: 'info' }, 'info', 'Cross-Origin-Resource-Policy header is missing', {
@@ -529,8 +577,9 @@ export const securityRules = [
529
577
  severity: 'error',
530
578
  description: 'SSL certificate must be valid and not expired',
531
579
  check: (ctx) => {
532
- if (ctx.sslCertificate === undefined)
533
- return null;
580
+ if (ctx.sslCertificate === undefined) {
581
+ return createResult({ id: 'security-ssl-valid', name: 'SSL Certificate Valid', category: 'security', severity: 'error' }, 'info', 'Not applicable (SSL certificate information unavailable)', { recommendation: 'This rule checks SSL certificate validity when certificate information is available' });
582
+ }
534
583
  if (!ctx.sslCertificate.valid) {
535
584
  return createResult({ id: 'security-ssl-valid', name: 'SSL Certificate Valid', category: 'security', severity: 'error' }, 'fail', 'SSL certificate is invalid', {
536
585
  recommendation: 'Renew or replace the SSL certificate immediately',
@@ -550,8 +599,9 @@ export const securityRules = [
550
599
  severity: 'warning',
551
600
  description: 'SSL certificate should not expire within 30 days',
552
601
  check: (ctx) => {
553
- if (!ctx.sslCertificate?.expiryDate)
554
- return null;
602
+ if (!ctx.sslCertificate?.expiryDate) {
603
+ return createResult({ id: 'security-ssl-expiry', name: 'SSL Certificate Expiry', category: 'security', severity: 'warning' }, 'info', 'Not applicable (SSL certificate expiry date unavailable)', { recommendation: 'This rule checks SSL certificate expiration when certificate expiry information is available' });
604
+ }
555
605
  const expiryDate = new Date(ctx.sslCertificate.expiryDate);
556
606
  const now = new Date();
557
607
  const daysUntilExpiry = Math.floor((expiryDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));
@@ -591,8 +641,9 @@ export const securityRules = [
591
641
  severity: 'error',
592
642
  description: 'SSL certificate CN/SAN must match the domain',
593
643
  check: (ctx) => {
594
- if (ctx.sslCertificate?.nameMismatch === undefined)
595
- return null;
644
+ if (ctx.sslCertificate?.nameMismatch === undefined) {
645
+ return createResult({ id: 'security-ssl-name-match', name: 'SSL Certificate Name Match', category: 'security', severity: 'error' }, 'info', 'Not applicable (SSL certificate name match information unavailable)', { recommendation: 'This rule checks SSL certificate name matching when certificate domain information is available' });
646
+ }
596
647
  if (ctx.sslCertificate.nameMismatch) {
597
648
  return createResult({ id: 'security-ssl-name-match', name: 'SSL Certificate Name Match', category: 'security', severity: 'error' }, 'fail', 'SSL certificate name mismatch', {
598
649
  recommendation: 'Get a certificate that matches your domain name',
@@ -613,8 +664,9 @@ export const securityRules = [
613
664
  severity: 'error',
614
665
  description: 'Server should use TLS 1.2 or higher',
615
666
  check: (ctx) => {
616
- if (!ctx.tlsVersion)
617
- return null;
667
+ if (!ctx.tlsVersion) {
668
+ return createResult({ id: 'security-tls-version', name: 'TLS Version', category: 'security', severity: 'error' }, 'info', 'Not applicable (TLS version information unavailable)', { recommendation: 'This rule checks TLS version when TLS connection information is available' });
669
+ }
618
670
  const version = ctx.tlsVersion;
619
671
  const insecureVersions = ['SSLv2', 'SSLv3', 'TLSv1', 'TLSv1.0', 'TLSv1.1'];
620
672
  if (insecureVersions.includes(version)) {
@@ -637,8 +689,9 @@ export const securityRules = [
637
689
  severity: 'info',
638
690
  description: 'SSL certificate should be from a trusted CA',
639
691
  check: (ctx) => {
640
- if (!ctx.sslCertificate?.issuer)
641
- return null;
692
+ if (!ctx.sslCertificate?.issuer) {
693
+ return createResult({ id: 'security-ssl-issuer', name: 'SSL Certificate Issuer', category: 'security', severity: 'info' }, 'info', 'Not applicable (SSL certificate issuer information unavailable)', { recommendation: 'This rule checks SSL certificate issuer when certificate issuer information is available' });
694
+ }
642
695
  const issuer = ctx.sslCertificate.issuer;
643
696
  const selfSigned = issuer.toLowerCase().includes('self-signed') ||
644
697
  ctx.sslCertificate.selfSigned === true;
@@ -661,8 +714,9 @@ export const securityRules = [
661
714
  severity: 'error',
662
715
  description: 'Password fields must only appear on HTTPS pages',
663
716
  check: (ctx) => {
664
- if (ctx.hasPasswordField === undefined)
665
- return null;
717
+ if (ctx.hasPasswordField === undefined) {
718
+ return createResult({ id: 'security-password-on-http', name: 'Password Fields on HTTP', category: 'security', severity: 'error' }, 'info', 'Not applicable (password field information unavailable)', { recommendation: 'This rule checks for password fields on insecure pages when password field data is available' });
719
+ }
666
720
  if (ctx.hasPasswordField && ctx.isHttps === false) {
667
721
  return createResult({ id: 'security-password-on-http', name: 'Password Fields on HTTP', category: 'security', severity: 'error' }, 'fail', 'Password field on insecure HTTP page', {
668
722
  recommendation: 'Enable HTTPS for all pages with password fields',
@@ -674,7 +728,7 @@ export const securityRules = [
674
728
  if (ctx.hasPasswordField && ctx.isHttps === true) {
675
729
  return createResult({ id: 'security-password-on-http', name: 'Password Fields on HTTP', category: 'security', severity: 'error' }, 'pass', 'Password fields are on HTTPS');
676
730
  }
677
- return null;
731
+ return createResult({ id: 'security-password-on-http', name: 'Password Fields on HTTP', category: 'security', severity: 'error' }, 'info', 'Not applicable (no password fields detected)', { recommendation: 'This rule checks if password fields are only used on HTTPS pages when password fields are present' });
678
732
  },
679
733
  },
680
734
  {
@@ -684,8 +738,9 @@ export const securityRules = [
684
738
  severity: 'warning',
685
739
  description: 'Forms should only submit to HTTPS endpoints',
686
740
  check: (ctx) => {
687
- if (ctx.formsOnHttp === undefined)
688
- return null;
741
+ if (ctx.formsOnHttp === undefined) {
742
+ return createResult({ id: 'security-forms-on-http', name: 'Forms on HTTP', category: 'security', severity: 'warning' }, 'info', 'Not applicable (form submission data unavailable)', { recommendation: 'This rule checks if forms submit to HTTPS endpoints when form data is available' });
743
+ }
689
744
  if (ctx.formsOnHttp > 0) {
690
745
  return createResult({ id: 'security-forms-on-http', name: 'Forms on HTTP', category: 'security', severity: 'warning' }, 'warn', `${ctx.formsOnHttp} form(s) submit to HTTP`, {
691
746
  value: ctx.formsOnHttp,
@@ -705,11 +760,13 @@ export const securityRules = [
705
760
  severity: 'info',
706
761
  description: 'Server header should not reveal detailed version information',
707
762
  check: (ctx) => {
708
- if (!ctx.responseHeaders)
709
- return null;
763
+ if (!ctx.responseHeaders) {
764
+ return createResult({ id: 'security-server-disclosure', name: 'Server Version Disclosure', category: 'security', severity: 'info' }, 'info', 'Not applicable (response headers unavailable)', { recommendation: 'This rule checks for server version disclosure when HTTP response headers are available' });
765
+ }
710
766
  const serverHeader = ctx.responseHeaders['server'] || ctx.responseHeaders['Server'];
711
- if (!serverHeader)
712
- return null;
767
+ if (!serverHeader) {
768
+ return createResult({ id: 'security-server-disclosure', name: 'Server Version Disclosure', category: 'security', severity: 'info' }, 'info', 'Not applicable (Server header not present)', { recommendation: 'This rule checks for server version disclosure when Server header is present' });
769
+ }
713
770
  const server = String(serverHeader);
714
771
  if (/\d+\.\d+/.test(server)) {
715
772
  return createResult({ id: 'security-server-disclosure', name: 'Server Version Disclosure', category: 'security', severity: 'info' }, 'info', `Server header reveals version: ${server}`, {
@@ -720,7 +777,7 @@ export const securityRules = [
720
777
  },
721
778
  });
722
779
  }
723
- return null;
780
+ return createResult({ id: 'security-server-disclosure', name: 'Server Version Disclosure', category: 'security', severity: 'info' }, 'info', 'Not applicable (Server header not present or does not disclose version)', { recommendation: 'This rule checks if the Server header reveals version information' });
724
781
  },
725
782
  },
726
783
  {
@@ -730,8 +787,9 @@ export const securityRules = [
730
787
  severity: 'info',
731
788
  description: 'X-Powered-By header reveals technology stack',
732
789
  check: (ctx) => {
733
- if (!ctx.responseHeaders)
734
- return null;
790
+ if (!ctx.responseHeaders) {
791
+ return createResult({ id: 'security-x-powered-by', name: 'X-Powered-By Header', category: 'security', severity: 'info' }, 'info', 'Not applicable (response headers unavailable)', { recommendation: 'This rule checks for X-Powered-By header disclosure when HTTP response headers are available' });
792
+ }
735
793
  const xPoweredBy = ctx.responseHeaders['x-powered-by'] || ctx.responseHeaders['X-Powered-By'];
736
794
  if (xPoweredBy) {
737
795
  return createResult({ id: 'security-x-powered-by', name: 'X-Powered-By Header', category: 'security', severity: 'info' }, 'info', `X-Powered-By header present: ${xPoweredBy}`, {
@@ -742,7 +800,7 @@ export const securityRules = [
742
800
  },
743
801
  });
744
802
  }
745
- return null;
803
+ return createResult({ id: 'security-x-powered-by', name: 'X-Powered-By Header', category: 'security', severity: 'info' }, 'info', 'Not applicable (X-Powered-By header not present)', { recommendation: 'This rule checks for X-Powered-By header disclosure when the header is present' });
746
804
  },
747
805
  },
748
806
  {
@@ -752,8 +810,9 @@ export const securityRules = [
752
810
  severity: 'info',
753
811
  description: 'Server should support Server Name Indication (SNI)',
754
812
  check: (ctx) => {
755
- if (ctx.sniSupported === undefined)
756
- return null;
813
+ if (ctx.sniSupported === undefined) {
814
+ return createResult({ id: 'ssl-sni-support', name: 'SNI Support', category: 'security', severity: 'info' }, 'info', 'Not applicable (SNI support information unavailable)', { recommendation: 'This rule checks for SNI support when SNI information is available' });
815
+ }
757
816
  if (!ctx.sniSupported) {
758
817
  return createResult({ id: 'ssl-sni-support', name: 'SNI Support', category: 'security', severity: 'info' }, 'info', 'Server may not support SNI', {
759
818
  recommendation: 'Ensure web server supports SNI for proper HTTPS functionality',
@@ -762,7 +821,7 @@ export const securityRules = [
762
821
  }
763
822
  });
764
823
  }
765
- return null;
824
+ return createResult({ id: 'ssl-sni-support', name: 'SNI Support', category: 'security', severity: 'info' }, 'info', 'Not applicable (SNI is supported)', { recommendation: 'This rule checks for SNI support issues when SNI is not supported' });
766
825
  },
767
826
  },
768
827
  {
@@ -772,8 +831,9 @@ export const securityRules = [
772
831
  severity: 'warning',
773
832
  description: 'Sitemap should only contain HTTPS URLs',
774
833
  check: (ctx) => {
775
- if (ctx.sitemapHttpUrls === undefined)
776
- return null;
834
+ if (ctx.sitemapHttpUrls === undefined) {
835
+ return createResult({ id: 'sitemap-https-urls', name: 'HTTPS URLs in Sitemap', category: 'security', severity: 'warning' }, 'info', 'Not applicable (sitemap URL data unavailable)', { recommendation: 'This rule checks sitemap URLs for HTTPS usage when sitemap data is available' });
836
+ }
777
837
  if (ctx.sitemapHttpUrls > 0) {
778
838
  return createResult({ id: 'sitemap-https-urls', name: 'HTTPS URLs in Sitemap', category: 'security', severity: 'warning' }, 'warn', `Sitemap contains ${ctx.sitemapHttpUrls} HTTP URLs`, {
779
839
  value: ctx.sitemapHttpUrls,
@@ -795,10 +855,12 @@ export const securityRules = [
795
855
  severity: 'info',
796
856
  description: 'HTTPS sites should implement HSTS for security',
797
857
  check: (ctx) => {
798
- if (!ctx.isHttps)
799
- return null;
800
- if (ctx.hasHsts === undefined)
801
- return null;
858
+ if (!ctx.isHttps) {
859
+ return createResult({ id: 'security-hsts', name: 'HSTS Header', category: 'security', severity: 'info' }, 'info', 'Not applicable (page is not served over HTTPS)', { recommendation: 'This rule checks HSTS implementation on HTTPS sites only' });
860
+ }
861
+ if (ctx.hasHsts === undefined) {
862
+ return createResult({ id: 'security-hsts', name: 'HSTS Header', category: 'security', severity: 'info' }, 'info', 'Not applicable (HSTS status unavailable)', { recommendation: 'This rule checks for HSTS headers when HSTS information is available' });
863
+ }
802
864
  if (!ctx.hasHsts) {
803
865
  return createResult({ id: 'security-hsts', name: 'HSTS Header', category: 'security', severity: 'info' }, 'info', 'Missing Strict-Transport-Security header', {
804
866
  recommendation: 'Implement HSTS to enforce HTTPS connections',