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
@@ -0,0 +1,199 @@
1
+ import { createResult } from './types.js';
2
+ export const redirectRules = [
3
+ {
4
+ id: 'redirect-chain-length',
5
+ name: 'Redirect Chain Length',
6
+ category: 'technical',
7
+ severity: 'warning',
8
+ description: 'Redirect chains should not exceed 3 hops',
9
+ check: (ctx) => {
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
+ }
13
+ const chainLength = ctx.redirectChain.length;
14
+ if (chainLength === 0) {
15
+ return createResult({ id: 'redirect-chain-length', name: 'Redirect Chain Length', category: 'technical', severity: 'warning' }, 'pass', 'No redirects detected');
16
+ }
17
+ if (chainLength > 5) {
18
+ return createResult({ id: 'redirect-chain-length', name: 'Redirect Chain Length', category: 'technical', severity: 'warning' }, 'fail', `Redirect chain has ${chainLength} hops (excessive)`, {
19
+ value: chainLength,
20
+ recommendation: 'Consolidate redirects to point directly to final URL',
21
+ evidence: {
22
+ found: ctx.redirectChain.map(r => `${r.status} ${r.from} → ${r.to}`),
23
+ expected: 'Direct link to final destination',
24
+ impact: 'Long redirect chains waste crawl budget and increase latency'
25
+ }
26
+ });
27
+ }
28
+ if (chainLength > 3) {
29
+ return createResult({ id: 'redirect-chain-length', name: 'Redirect Chain Length', category: 'technical', severity: 'warning' }, 'warn', `Redirect chain has ${chainLength} hops`, {
30
+ value: chainLength,
31
+ recommendation: 'Reduce redirect chain by updating original links',
32
+ evidence: {
33
+ found: ctx.redirectChain.map(r => `${r.status} ${r.from} → ${r.to}`),
34
+ expected: 'Maximum 3 redirects',
35
+ impact: 'Excessive redirects slow page load and lose link equity'
36
+ }
37
+ });
38
+ }
39
+ return createResult({ id: 'redirect-chain-length', name: 'Redirect Chain Length', category: 'technical', severity: 'warning' }, 'info', `Page reached after ${chainLength} redirect(s)`, {
40
+ value: chainLength,
41
+ evidence: {
42
+ found: ctx.redirectChain.map(r => `${r.status} ${r.from} → ${r.to}`)
43
+ }
44
+ });
45
+ },
46
+ },
47
+ {
48
+ id: 'redirect-loop',
49
+ name: 'Redirect Loop',
50
+ category: 'technical',
51
+ severity: 'error',
52
+ description: 'Pages should not create redirect loops',
53
+ check: (ctx) => {
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
+ }
57
+ const urls = ctx.redirectChain.map(r => r.from);
58
+ urls.push(ctx.redirectChain[ctx.redirectChain.length - 1].to);
59
+ const seen = new Set();
60
+ let loopUrl;
61
+ for (const url of urls) {
62
+ const normalized = normalizeUrl(url);
63
+ if (seen.has(normalized)) {
64
+ loopUrl = url;
65
+ break;
66
+ }
67
+ seen.add(normalized);
68
+ }
69
+ if (loopUrl) {
70
+ return createResult({ id: 'redirect-loop', name: 'Redirect Loop', category: 'technical', severity: 'error' }, 'fail', 'Redirect loop detected', {
71
+ recommendation: 'Fix server configuration to break the redirect loop',
72
+ evidence: {
73
+ found: ctx.redirectChain.map(r => `${r.status} ${r.from} → ${r.to}`),
74
+ issue: `Loop at: ${loopUrl}`,
75
+ impact: 'Redirect loops prevent page from being crawled and indexed'
76
+ }
77
+ });
78
+ }
79
+ return createResult({ id: 'redirect-loop', name: 'Redirect Loop', category: 'technical', severity: 'error' }, 'pass', 'No redirect loop detected');
80
+ },
81
+ },
82
+ {
83
+ id: 'redirect-type',
84
+ name: 'Redirect Type',
85
+ category: 'technical',
86
+ severity: 'info',
87
+ description: 'Permanent content moves should use 301 redirects',
88
+ check: (ctx) => {
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
+ }
92
+ const temporary = ctx.redirectChain.filter(r => r.status === 302 || r.status === 307);
93
+ const permanent = ctx.redirectChain.filter(r => r.status === 301 || r.status === 308);
94
+ if (temporary.length > 0 && permanent.length === 0) {
95
+ return createResult({ id: 'redirect-type', name: 'Redirect Type', category: 'technical', severity: 'info' }, 'warn', `Using temporary redirect (${temporary[0].status})`, {
96
+ recommendation: 'Use 301 redirect for permanent URL changes to pass link equity',
97
+ evidence: {
98
+ found: `${temporary[0].status} ${temporary[0].from} → ${temporary[0].to}`,
99
+ expected: '301 for permanent moves, 302 only for temporary changes',
100
+ impact: 'Temporary redirects may not pass full link equity'
101
+ }
102
+ });
103
+ }
104
+ if (permanent.length > 0) {
105
+ return createResult({ id: 'redirect-type', name: 'Redirect Type', category: 'technical', severity: 'info' }, 'pass', 'Using permanent redirect (301)');
106
+ }
107
+ return createResult({ id: 'redirect-type', name: 'Redirect Type', category: 'technical', severity: 'info' }, 'info', 'Unknown redirect type', { recommendation: 'Check redirect status codes' });
108
+ },
109
+ },
110
+ {
111
+ id: 'www-consistency',
112
+ name: 'WWW vs Non-WWW',
113
+ category: 'canonicalization',
114
+ severity: 'warning',
115
+ description: 'Site should redirect consistently to either WWW or non-WWW version',
116
+ check: (ctx) => {
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
+ }
120
+ const { canonicalHasWww, urlHasWww, redirectsToCanonical } = ctx.wwwConsistency;
121
+ if (canonicalHasWww !== urlHasWww && !redirectsToCanonical) {
122
+ return createResult({ id: 'www-consistency', name: 'WWW vs Non-WWW', category: 'canonicalization', severity: 'warning' }, 'warn', 'WWW/non-WWW version accessible without redirect', {
123
+ recommendation: 'Set up 301 redirect from non-canonical version to canonical',
124
+ evidence: {
125
+ found: urlHasWww ? 'www version' : 'non-www version',
126
+ expected: `Redirect to ${canonicalHasWww ? 'www' : 'non-www'} version`,
127
+ impact: 'Duplicate content issues when both versions are accessible'
128
+ }
129
+ });
130
+ }
131
+ return createResult({ id: 'www-consistency', name: 'WWW vs Non-WWW', category: 'canonicalization', severity: 'warning' }, 'pass', 'WWW handling is consistent');
132
+ },
133
+ },
134
+ {
135
+ id: 'http-to-https-redirect',
136
+ name: 'HTTP to HTTPS Redirect',
137
+ category: 'security',
138
+ severity: 'warning',
139
+ description: 'HTTP version should redirect to HTTPS',
140
+ check: (ctx) => {
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
+ }
144
+ if (!ctx.httpRedirectsToHttps) {
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', {
146
+ recommendation: 'Configure server to redirect HTTP traffic to HTTPS',
147
+ evidence: {
148
+ found: 'HTTP accessible without redirect',
149
+ expected: '301 redirect from HTTP to HTTPS',
150
+ impact: 'Users may access insecure version; duplicate content issues',
151
+ learnMore: 'https://web.dev/why-https-matters/'
152
+ }
153
+ });
154
+ }
155
+ return createResult({ id: 'http-to-https-redirect', name: 'HTTP to HTTPS Redirect', category: 'security', severity: 'warning' }, 'pass', 'HTTP properly redirects to HTTPS');
156
+ },
157
+ },
158
+ {
159
+ id: 'cross-domain-redirect',
160
+ name: 'Cross-Domain Redirect',
161
+ category: 'technical',
162
+ severity: 'info',
163
+ description: 'Detects redirects to different domains',
164
+ check: (ctx) => {
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
+ }
168
+ const crossDomainRedirects = ctx.redirectChain.filter(r => {
169
+ try {
170
+ const fromHost = new URL(r.from).hostname;
171
+ const toHost = new URL(r.to).hostname;
172
+ return fromHost !== toHost;
173
+ }
174
+ catch {
175
+ return false;
176
+ }
177
+ });
178
+ if (crossDomainRedirects.length > 0) {
179
+ return createResult({ id: 'cross-domain-redirect', name: 'Cross-Domain Redirect', category: 'technical', severity: 'info' }, 'info', `${crossDomainRedirects.length} cross-domain redirect(s) detected`, {
180
+ value: crossDomainRedirects.length,
181
+ evidence: {
182
+ found: crossDomainRedirects.map(r => `${r.from} → ${r.to}`),
183
+ impact: 'Cross-domain redirects may indicate site migration or tracking'
184
+ }
185
+ });
186
+ }
187
+ return createResult({ id: 'cross-domain-redirect', name: 'Cross-Domain Redirect', category: 'technical', severity: 'info' }, 'pass', 'No cross-domain redirects');
188
+ },
189
+ },
190
+ ];
191
+ function normalizeUrl(url) {
192
+ try {
193
+ const u = new URL(url);
194
+ return `${u.protocol}//${u.hostname}${u.pathname.replace(/\/$/, '')}${u.search}`;
195
+ }
196
+ catch {
197
+ return url.toLowerCase();
198
+ }
199
+ }
@@ -0,0 +1,2 @@
1
+ import { SeoRule } from './types.js';
2
+ export declare const resourceRules: SeoRule[];
@@ -0,0 +1,390 @@
1
+ import { createResult } from './types.js';
2
+ export const resourceRules = [
3
+ {
4
+ id: 'resources-js-files-count',
5
+ name: 'JavaScript File Count',
6
+ category: 'resources',
7
+ severity: 'warning',
8
+ description: 'Too many JavaScript files increase HTTP requests and slow page load',
9
+ check: (ctx) => {
10
+ if (ctx.jsFilesCount === undefined) {
11
+ return createResult({ id: 'resources-js-files-count', name: 'JavaScript File Count', category: 'resources', severity: 'warning' }, 'info', 'Not applicable (JavaScript file count unavailable)', { recommendation: 'This rule checks JavaScript file count when resource information is available' });
12
+ }
13
+ const max = 15;
14
+ if (ctx.jsFilesCount > max) {
15
+ return createResult({ id: 'resources-js-files-count', name: 'JavaScript File Count', category: 'resources', severity: 'warning' }, 'warn', `Too many JS files (${ctx.jsFilesCount})`, {
16
+ value: ctx.jsFilesCount,
17
+ recommendation: `Bundle JavaScript files to reduce HTTP requests (max ${max})`,
18
+ evidence: {
19
+ found: ctx.jsFilesCount,
20
+ expected: `${max} or fewer`,
21
+ impact: 'Each HTTP request adds latency and slows page load',
22
+ },
23
+ });
24
+ }
25
+ return createResult({ id: 'resources-js-files-count', name: 'JavaScript File Count', category: 'resources', severity: 'warning' }, 'info', 'Not applicable (JavaScript file count is within limits or unavailable)', { recommendation: 'This rule checks JavaScript file count when resource information is available' });
26
+ },
27
+ },
28
+ {
29
+ id: 'resources-js-total-size',
30
+ name: 'JavaScript Total Size',
31
+ category: 'resources',
32
+ severity: 'warning',
33
+ description: 'Large JavaScript bundles slow page load and increase bandwidth',
34
+ check: (ctx) => {
35
+ if (ctx.jsTotalSize === undefined) {
36
+ return createResult({ id: 'resources-js-total-size', name: 'JavaScript Total Size', category: 'resources', severity: 'warning' }, 'info', 'Not applicable (JavaScript size information unavailable)', { recommendation: 'This rule checks JavaScript total size when resource size information is available' });
37
+ }
38
+ const maxKB = 500;
39
+ const sizeKB = Math.round(ctx.jsTotalSize / 1024);
40
+ if (sizeKB > maxKB) {
41
+ return createResult({ id: 'resources-js-total-size', name: 'JavaScript Total Size', category: 'resources', severity: 'warning' }, 'warn', `Large JS bundle (${sizeKB}KB)`, {
42
+ value: sizeKB,
43
+ recommendation: `Reduce JavaScript size to under ${maxKB}KB`,
44
+ evidence: {
45
+ found: `${sizeKB}KB`,
46
+ expected: `${maxKB}KB or less`,
47
+ impact: 'Large JS bundles delay page interactivity',
48
+ },
49
+ });
50
+ }
51
+ return createResult({ id: 'resources-js-total-size', name: 'JavaScript Total Size', category: 'resources', severity: 'warning' }, 'info', 'Not applicable (JavaScript size is within limits)', { recommendation: 'This rule checks for large JavaScript bundles that slow page load' });
52
+ },
53
+ },
54
+ {
55
+ id: 'resources-js-render-blocking',
56
+ name: 'Render-Blocking JavaScript',
57
+ category: 'resources',
58
+ severity: 'warning',
59
+ description: 'Render-blocking JS in <head> delays page rendering',
60
+ check: (ctx) => {
61
+ if (ctx.renderBlockingJs === undefined) {
62
+ return createResult({ id: 'resources-js-render-blocking', name: 'Render-Blocking JavaScript', category: 'resources', severity: 'warning' }, 'info', 'Not applicable (render-blocking JavaScript information unavailable)', { recommendation: 'This rule checks for render-blocking JavaScript when resource information is available' });
63
+ }
64
+ if (ctx.renderBlockingJs > 0) {
65
+ return createResult({ id: 'resources-js-render-blocking', name: 'Render-Blocking JavaScript', category: 'resources', severity: 'warning' }, 'warn', `${ctx.renderBlockingJs} render-blocking JS file(s)`, {
66
+ value: ctx.renderBlockingJs,
67
+ recommendation: 'Add async or defer attribute to non-critical scripts',
68
+ evidence: {
69
+ found: `${ctx.renderBlockingJs} scripts without async/defer`,
70
+ expected: 'All scripts with async, defer, or in body',
71
+ example: '<script src="app.js" defer></script>',
72
+ },
73
+ });
74
+ }
75
+ return createResult({ id: 'resources-js-render-blocking', name: 'Render-Blocking JavaScript', category: 'resources', severity: 'warning' }, 'pass', 'No render-blocking JavaScript');
76
+ },
77
+ },
78
+ {
79
+ id: 'resources-css-files-count',
80
+ name: 'CSS File Count',
81
+ category: 'resources',
82
+ severity: 'warning',
83
+ description: 'Too many CSS files increase HTTP requests',
84
+ check: (ctx) => {
85
+ if (ctx.cssFilesCount === undefined) {
86
+ return createResult({ id: 'resources-css-files-count', name: 'CSS File Count', category: 'resources', severity: 'warning' }, 'info', 'Not applicable (CSS file count unavailable)', { recommendation: 'This rule checks CSS file count when resource information is available' });
87
+ }
88
+ const max = 10;
89
+ if (ctx.cssFilesCount > max) {
90
+ return createResult({ id: 'resources-css-files-count', name: 'CSS File Count', category: 'resources', severity: 'warning' }, 'warn', `Too many CSS files (${ctx.cssFilesCount})`, {
91
+ value: ctx.cssFilesCount,
92
+ recommendation: `Bundle CSS files to reduce HTTP requests (max ${max})`,
93
+ evidence: {
94
+ found: ctx.cssFilesCount,
95
+ expected: `${max} or fewer`,
96
+ },
97
+ });
98
+ }
99
+ return createResult({ id: 'resources-css-files-count', name: 'CSS File Count', category: 'resources', severity: 'warning' }, 'info', 'Not applicable (CSS file count is within limits or unavailable)', { recommendation: 'This rule checks CSS file count when resource information is available' });
100
+ },
101
+ },
102
+ {
103
+ id: 'resources-css-total-size',
104
+ name: 'CSS Total Size',
105
+ category: 'resources',
106
+ severity: 'warning',
107
+ description: 'Large CSS files delay page rendering',
108
+ check: (ctx) => {
109
+ if (ctx.cssTotalSize === undefined) {
110
+ return createResult({ id: 'resources-css-total-size', name: 'CSS Total Size', category: 'resources', severity: 'warning' }, 'info', 'Not applicable (CSS size information unavailable)', { recommendation: 'This rule checks CSS total size when resource size information is available' });
111
+ }
112
+ const maxKB = 200;
113
+ const sizeKB = Math.round(ctx.cssTotalSize / 1024);
114
+ if (sizeKB > maxKB) {
115
+ return createResult({ id: 'resources-css-total-size', name: 'CSS Total Size', category: 'resources', severity: 'warning' }, 'warn', `Large CSS bundle (${sizeKB}KB)`, {
116
+ value: sizeKB,
117
+ recommendation: `Reduce CSS size to under ${maxKB}KB`,
118
+ evidence: {
119
+ found: `${sizeKB}KB`,
120
+ expected: `${maxKB}KB or less`,
121
+ impact: 'Large CSS delays first contentful paint',
122
+ },
123
+ });
124
+ }
125
+ return createResult({ id: 'resources-css-total-size', name: 'CSS Total Size', category: 'resources', severity: 'warning' }, 'info', 'Not applicable (CSS size is within limits)', { recommendation: 'This rule checks for large CSS files that delay page rendering' });
126
+ },
127
+ },
128
+ {
129
+ id: 'resources-css-render-blocking',
130
+ name: 'Render-Blocking CSS',
131
+ category: 'resources',
132
+ severity: 'info',
133
+ description: 'Critical CSS should be inlined, non-critical deferred',
134
+ check: (ctx) => {
135
+ if (ctx.cssFilesCount === undefined) {
136
+ return createResult({ id: 'resources-css-render-blocking', name: 'Render-Blocking CSS', category: 'resources', severity: 'info' }, 'info', 'Not applicable (CSS file information unavailable)', { recommendation: 'This rule checks for render-blocking CSS when resource information is available' });
137
+ }
138
+ if (ctx.cssFilesCount > 3 && !ctx.hasCriticalCss) {
139
+ return createResult({ id: 'resources-css-render-blocking', name: 'Render-Blocking CSS', category: 'resources', severity: 'info' }, 'info', `${ctx.cssFilesCount} CSS files blocking render`, {
140
+ value: ctx.cssFilesCount,
141
+ recommendation: 'Consider inlining critical CSS and lazy-loading the rest',
142
+ evidence: {
143
+ impact: 'CSS blocks rendering until fully loaded',
144
+ example: '<link rel="preload" href="style.css" as="style" onload="this.rel=\'stylesheet\'">',
145
+ },
146
+ });
147
+ }
148
+ return createResult({ id: 'resources-css-render-blocking', name: 'Render-Blocking CSS', category: 'resources', severity: 'info' }, 'info', 'Not applicable (CSS file count is low or critical CSS is present)', { recommendation: 'This rule checks for render-blocking CSS that delays page rendering' });
149
+ },
150
+ },
151
+ {
152
+ id: 'resources-image-size-large',
153
+ name: 'Large Image Files',
154
+ category: 'resources',
155
+ severity: 'warning',
156
+ description: 'Images over 200KB should be optimized',
157
+ check: (ctx) => {
158
+ if (!ctx.largeImages || ctx.largeImages.length === 0) {
159
+ return createResult({ id: 'resources-image-size-large', name: 'Large Image Files', category: 'resources', severity: 'warning' }, 'info', 'Not applicable (no large images detected or image data unavailable)', { recommendation: 'This rule checks for images over 200KB when image information is available' });
160
+ }
161
+ const maxKB = 200;
162
+ const largeCount = ctx.largeImages.length;
163
+ return createResult({ id: 'resources-image-size-large', name: 'Large Image Files', category: 'resources', severity: 'warning' }, 'warn', `${largeCount} image(s) over ${maxKB}KB`, {
164
+ value: largeCount,
165
+ recommendation: 'Compress images or use modern formats (WebP, AVIF)',
166
+ evidence: {
167
+ found: ctx.largeImages.slice(0, 5),
168
+ impact: 'Large images significantly slow page load',
169
+ },
170
+ });
171
+ },
172
+ },
173
+ {
174
+ id: 'resources-image-format',
175
+ name: 'Modern Image Formats',
176
+ category: 'resources',
177
+ severity: 'info',
178
+ description: 'Use WebP or AVIF for better compression',
179
+ check: (ctx) => {
180
+ if (ctx.imagesTotal === undefined || ctx.imagesTotal === 0) {
181
+ return createResult({ id: 'resources-image-format', name: 'Modern Image Formats', category: 'resources', severity: 'info' }, 'info', 'Not applicable (no images present or image data unavailable)', { recommendation: 'This rule checks image format usage when images are present on the page' });
182
+ }
183
+ if (ctx.modernFormatImages === undefined) {
184
+ return createResult({ id: 'resources-image-format', name: 'Modern Image Formats', category: 'resources', severity: 'info' }, 'info', 'Not applicable (modern format image data unavailable)', { recommendation: 'This rule checks for modern image formats when image format information is available' });
185
+ }
186
+ const modernPercent = (ctx.modernFormatImages / ctx.imagesTotal) * 100;
187
+ if (modernPercent < 50) {
188
+ return createResult({ id: 'resources-image-format', name: 'Modern Image Formats', category: 'resources', severity: 'info' }, 'info', `Only ${Math.round(modernPercent)}% of images use modern formats`, {
189
+ value: ctx.modernFormatImages,
190
+ recommendation: 'Convert images to WebP or AVIF for 25-50% smaller files',
191
+ evidence: {
192
+ found: `${ctx.modernFormatImages}/${ctx.imagesTotal} modern format images`,
193
+ expected: 'WebP or AVIF for all images',
194
+ impact: 'Modern formats reduce image size by 25-50%',
195
+ },
196
+ });
197
+ }
198
+ return createResult({ id: 'resources-image-format', name: 'Modern Image Formats', category: 'resources', severity: 'info' }, 'pass', `${Math.round(modernPercent)}% of images use modern formats`);
199
+ },
200
+ },
201
+ {
202
+ id: 'resources-image-dimensions',
203
+ name: 'Image Dimensions',
204
+ category: 'resources',
205
+ severity: 'warning',
206
+ description: 'Images should have width and height attributes',
207
+ check: (ctx) => {
208
+ if (ctx.imagesMissingDimensions === undefined) {
209
+ return createResult({ id: 'resources-image-dimensions', name: 'Image Dimensions', category: 'resources', severity: 'warning' }, 'info', 'Not applicable (image dimension information unavailable)', { recommendation: 'This rule checks for image dimensions when image information is available' });
210
+ }
211
+ if (ctx.imagesMissingDimensions > 0) {
212
+ return createResult({ id: 'resources-image-dimensions', name: 'Image Dimensions', category: 'resources', severity: 'warning' }, 'warn', `${ctx.imagesMissingDimensions} image(s) missing width/height`, {
213
+ value: ctx.imagesMissingDimensions,
214
+ recommendation: 'Add width and height attributes to prevent layout shift',
215
+ evidence: {
216
+ impact: 'Missing dimensions cause Cumulative Layout Shift (CLS)',
217
+ example: '<img src="photo.jpg" width="800" height="600" alt="Photo">',
218
+ },
219
+ });
220
+ }
221
+ return createResult({ id: 'resources-image-dimensions', name: 'Image Dimensions', category: 'resources', severity: 'warning' }, 'pass', 'All images have width/height attributes');
222
+ },
223
+ },
224
+ {
225
+ id: 'resources-font-files',
226
+ name: 'Web Font Files',
227
+ category: 'resources',
228
+ severity: 'info',
229
+ description: 'Too many font files can slow page load',
230
+ check: (ctx) => {
231
+ if (ctx.fontFilesCount === undefined) {
232
+ return createResult({ id: 'resources-font-files', name: 'Web Font Files', category: 'resources', severity: 'info' }, 'info', 'Not applicable (font file information unavailable)', { recommendation: 'This rule checks font file count when font information is available' });
233
+ }
234
+ const max = 4;
235
+ if (ctx.fontFilesCount > max) {
236
+ return createResult({ id: 'resources-font-files', name: 'Web Font Files', category: 'resources', severity: 'info' }, 'info', `${ctx.fontFilesCount} font files loaded`, {
237
+ value: ctx.fontFilesCount,
238
+ recommendation: `Limit to ${max} font files or use variable fonts`,
239
+ evidence: {
240
+ found: ctx.fontFilesCount,
241
+ expected: `${max} or fewer`,
242
+ impact: 'Each font file adds to page weight and load time',
243
+ },
244
+ });
245
+ }
246
+ return createResult({ id: 'resources-font-files', name: 'Web Font Files', category: 'resources', severity: 'info' }, 'info', 'Not applicable (font file count is within limits)', { recommendation: 'This rule checks for excessive font files that slow page load' });
247
+ },
248
+ },
249
+ {
250
+ id: 'resources-font-display',
251
+ name: 'Font Display Strategy',
252
+ category: 'resources',
253
+ severity: 'info',
254
+ description: 'Use font-display: swap to prevent invisible text during load',
255
+ check: (ctx) => {
256
+ if (ctx.hasFontDisplaySwap === undefined) {
257
+ return createResult({ id: 'resources-font-display', name: 'Font Display Strategy', category: 'resources', severity: 'info' }, 'info', 'Not applicable (font display strategy information unavailable)', { recommendation: 'This rule checks font display strategy when font information is available' });
258
+ }
259
+ if (!ctx.hasFontDisplaySwap && ctx.fontFilesCount && ctx.fontFilesCount > 0) {
260
+ return createResult({ id: 'resources-font-display', name: 'Font Display Strategy', category: 'resources', severity: 'info' }, 'info', 'font-display: swap not detected', {
261
+ recommendation: 'Add font-display: swap to @font-face rules',
262
+ evidence: {
263
+ expected: '@font-face { font-display: swap; }',
264
+ impact: 'Without swap, text may be invisible during font load (FOIT)',
265
+ },
266
+ });
267
+ }
268
+ return createResult({ id: 'resources-font-display', name: 'Font Display Strategy', category: 'resources', severity: 'info' }, 'info', 'Not applicable (no fonts loaded or font-display is configured)', { recommendation: 'This rule checks for font-display strategy when fonts are loaded' });
269
+ },
270
+ },
271
+ {
272
+ id: 'resources-total-requests',
273
+ name: 'Total HTTP Requests',
274
+ category: 'resources',
275
+ severity: 'warning',
276
+ description: 'Too many HTTP requests slow page load',
277
+ check: (ctx) => {
278
+ if (ctx.totalRequests === undefined) {
279
+ return createResult({ id: 'resources-total-requests', name: 'Total HTTP Requests', category: 'resources', severity: 'warning' }, 'info', 'Not applicable (total request count unavailable)', { recommendation: 'This rule checks total HTTP requests when request information is available' });
280
+ }
281
+ const max = 50;
282
+ if (ctx.totalRequests > max) {
283
+ return createResult({ id: 'resources-total-requests', name: 'Total HTTP Requests', category: 'resources', severity: 'warning' }, 'warn', `Too many HTTP requests (${ctx.totalRequests})`, {
284
+ value: ctx.totalRequests,
285
+ recommendation: `Reduce to under ${max} requests by bundling and optimizing`,
286
+ evidence: {
287
+ found: ctx.totalRequests,
288
+ expected: `${max} or fewer`,
289
+ impact: 'Each HTTP request adds latency',
290
+ },
291
+ });
292
+ }
293
+ return createResult({ id: 'resources-total-requests', name: 'Total HTTP Requests', category: 'resources', severity: 'warning' }, 'pass', `Good HTTP request count (${ctx.totalRequests})`);
294
+ },
295
+ },
296
+ {
297
+ id: 'resources-total-size',
298
+ name: 'Total Page Size',
299
+ category: 'resources',
300
+ severity: 'warning',
301
+ description: 'Total page weight affects load time',
302
+ check: (ctx) => {
303
+ if (ctx.totalPageSize === undefined) {
304
+ return createResult({ id: 'resources-total-size', name: 'Total Page Size', category: 'resources', severity: 'warning' }, 'info', 'Not applicable (total page size information unavailable)', { recommendation: 'This rule checks total page size when page weight information is available' });
305
+ }
306
+ const maxMB = 3;
307
+ const sizeMB = ctx.totalPageSize / (1024 * 1024);
308
+ if (sizeMB > maxMB) {
309
+ return createResult({ id: 'resources-total-size', name: 'Total Page Size', category: 'resources', severity: 'warning' }, 'warn', `Large page size (${sizeMB.toFixed(1)}MB)`, {
310
+ value: Math.round(ctx.totalPageSize / 1024),
311
+ recommendation: `Reduce total page size to under ${maxMB}MB`,
312
+ evidence: {
313
+ found: `${sizeMB.toFixed(1)}MB`,
314
+ expected: `${maxMB}MB or less`,
315
+ impact: 'Large pages are slow on mobile and low-bandwidth connections',
316
+ },
317
+ });
318
+ }
319
+ return createResult({ id: 'resources-total-size', name: 'Total Page Size', category: 'resources', severity: 'warning' }, 'pass', `Good page size (${sizeMB.toFixed(1)}MB)`);
320
+ },
321
+ },
322
+ {
323
+ id: 'resources-compression',
324
+ name: 'Resource Compression',
325
+ category: 'resources',
326
+ severity: 'warning',
327
+ description: 'Text resources should be compressed with gzip or brotli',
328
+ check: (ctx) => {
329
+ if (ctx.uncompressedResources === undefined) {
330
+ return createResult({ id: 'resources-compression', name: 'Resource Compression', category: 'resources', severity: 'warning' }, 'info', 'Not applicable (resource compression information unavailable)', { recommendation: 'This rule checks resource compression when resource information is available' });
331
+ }
332
+ if (ctx.uncompressedResources > 0) {
333
+ return createResult({ id: 'resources-compression', name: 'Resource Compression', category: 'resources', severity: 'warning' }, 'warn', `${ctx.uncompressedResources} uncompressed resource(s)`, {
334
+ value: ctx.uncompressedResources,
335
+ recommendation: 'Enable gzip or Brotli compression on your server',
336
+ evidence: {
337
+ impact: 'Compression typically reduces file size by 60-80%',
338
+ },
339
+ });
340
+ }
341
+ return createResult({ id: 'resources-compression', name: 'Resource Compression', category: 'resources', severity: 'warning' }, 'pass', 'All text resources are compressed');
342
+ },
343
+ },
344
+ {
345
+ id: 'resources-caching',
346
+ name: 'Browser Caching',
347
+ category: 'resources',
348
+ severity: 'info',
349
+ description: 'Static resources should have long cache lifetimes',
350
+ check: (ctx) => {
351
+ if (ctx.resourcesWithoutCaching === undefined) {
352
+ return createResult({ id: 'resources-caching', name: 'Browser Caching', category: 'resources', severity: 'info' }, 'info', 'Not applicable (caching information unavailable)', { recommendation: 'This rule checks browser caching when resource caching information is available' });
353
+ }
354
+ if (ctx.resourcesWithoutCaching > 0) {
355
+ return createResult({ id: 'resources-caching', name: 'Browser Caching', category: 'resources', severity: 'info' }, 'info', `${ctx.resourcesWithoutCaching} resource(s) without proper caching`, {
356
+ value: ctx.resourcesWithoutCaching,
357
+ recommendation: 'Set Cache-Control headers for static resources',
358
+ evidence: {
359
+ expected: 'Cache-Control: max-age=31536000 for static assets',
360
+ impact: 'Proper caching improves repeat visit performance',
361
+ },
362
+ });
363
+ }
364
+ return createResult({ id: 'resources-caching', name: 'Browser Caching', category: 'resources', severity: 'info' }, 'pass', 'Static resources have proper caching');
365
+ },
366
+ },
367
+ {
368
+ id: 'broken-external-resources',
369
+ name: 'Broken External Resources',
370
+ category: 'resources',
371
+ severity: 'warning',
372
+ description: 'External JS/CSS files should be accessible',
373
+ check: (ctx) => {
374
+ if (ctx.brokenExternalResources === undefined) {
375
+ return createResult({ id: 'broken-external-resources', name: 'Broken External Resources', category: 'resources', severity: 'warning' }, 'info', 'Not applicable (external resource status information unavailable)', { recommendation: 'This rule checks for broken external resources when resource information is available' });
376
+ }
377
+ if (ctx.brokenExternalResources > 0) {
378
+ return createResult({ id: 'broken-external-resources', name: 'Broken External Resources', category: 'resources', severity: 'warning' }, 'warn', `${ctx.brokenExternalResources} broken external JS/CSS files`, {
379
+ value: ctx.brokenExternalResources,
380
+ recommendation: 'Fix or remove references to broken external resources',
381
+ evidence: {
382
+ found: ctx.brokenExternalResourceUrls?.slice(0, 5) || [],
383
+ impact: 'Broken resources may cause rendering issues and affect user experience'
384
+ }
385
+ });
386
+ }
387
+ return createResult({ id: 'broken-external-resources', name: 'Broken External Resources', category: 'resources', severity: 'warning' }, 'info', 'Not applicable (no broken external resources detected)', { recommendation: 'This rule checks for broken external JS/CSS files' });
388
+ },
389
+ },
390
+ ];
@@ -0,0 +1,2 @@
1
+ import { SeoRule } from './types.js';
2
+ export declare const schemaRules: SeoRule[];