@pseolint/core 0.4.1 → 0.5.3

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 (321) hide show
  1. package/README.md +264 -169
  2. package/dist/ai/manifest/diff.d.ts +78 -0
  3. package/dist/ai/manifest/diff.d.ts.map +1 -0
  4. package/dist/ai/manifest/diff.js +139 -0
  5. package/dist/ai/manifest/diff.js.map +1 -0
  6. package/dist/ai/manifest/index.d.ts +18 -0
  7. package/dist/ai/manifest/index.d.ts.map +1 -0
  8. package/dist/ai/manifest/index.js +15 -0
  9. package/dist/ai/manifest/index.js.map +1 -0
  10. package/dist/ai/manifest/validate-manifest.d.ts +37 -0
  11. package/dist/ai/manifest/validate-manifest.d.ts.map +1 -0
  12. package/dist/ai/manifest/validate-manifest.js +67 -0
  13. package/dist/ai/manifest/validate-manifest.js.map +1 -0
  14. package/dist/ai/manifest/validators/domain-patches.d.ts +15 -0
  15. package/dist/ai/manifest/validators/domain-patches.d.ts.map +1 -0
  16. package/dist/ai/manifest/validators/domain-patches.js +110 -0
  17. package/dist/ai/manifest/validators/domain-patches.js.map +1 -0
  18. package/dist/ai/manifest/validators/index.d.ts +5 -0
  19. package/dist/ai/manifest/validators/index.d.ts.map +1 -0
  20. package/dist/ai/manifest/validators/index.js +4 -0
  21. package/dist/ai/manifest/validators/index.js.map +1 -0
  22. package/dist/ai/manifest/validators/page-changes.d.ts +36 -0
  23. package/dist/ai/manifest/validators/page-changes.d.ts.map +1 -0
  24. package/dist/ai/manifest/validators/page-changes.js +221 -0
  25. package/dist/ai/manifest/validators/page-changes.js.map +1 -0
  26. package/dist/ai/manifest/validators/types.d.ts +17 -0
  27. package/dist/ai/manifest/validators/types.d.ts.map +1 -0
  28. package/dist/ai/manifest/validators/types.js +5 -0
  29. package/dist/ai/manifest/validators/types.js.map +1 -0
  30. package/dist/ai/orchestrate.d.ts +74 -0
  31. package/dist/ai/orchestrate.d.ts.map +1 -0
  32. package/dist/ai/orchestrate.js +54 -0
  33. package/dist/ai/orchestrate.js.map +1 -0
  34. package/dist/ai/orchestrator/budget.d.ts +57 -0
  35. package/dist/ai/orchestrator/budget.d.ts.map +1 -0
  36. package/dist/ai/orchestrator/budget.js +114 -0
  37. package/dist/ai/orchestrator/budget.js.map +1 -0
  38. package/dist/ai/orchestrator/finish-tool.d.ts +568 -0
  39. package/dist/ai/orchestrator/finish-tool.d.ts.map +1 -0
  40. package/dist/ai/orchestrator/finish-tool.js +114 -0
  41. package/dist/ai/orchestrator/finish-tool.js.map +1 -0
  42. package/dist/ai/orchestrator/index.d.ts +25 -0
  43. package/dist/ai/orchestrator/index.d.ts.map +1 -0
  44. package/dist/ai/orchestrator/index.js +21 -0
  45. package/dist/ai/orchestrator/index.js.map +1 -0
  46. package/dist/ai/orchestrator/log.d.ts +24 -0
  47. package/dist/ai/orchestrator/log.d.ts.map +1 -0
  48. package/dist/ai/orchestrator/log.js +48 -0
  49. package/dist/ai/orchestrator/log.js.map +1 -0
  50. package/dist/ai/orchestrator/page-cache.d.ts +64 -0
  51. package/dist/ai/orchestrator/page-cache.d.ts.map +1 -0
  52. package/dist/ai/orchestrator/page-cache.js +127 -0
  53. package/dist/ai/orchestrator/page-cache.js.map +1 -0
  54. package/dist/ai/orchestrator/prompt.d.ts +16 -0
  55. package/dist/ai/orchestrator/prompt.d.ts.map +1 -0
  56. package/dist/ai/orchestrator/prompt.js +52 -0
  57. package/dist/ai/orchestrator/prompt.js.map +1 -0
  58. package/dist/ai/orchestrator/runner.d.ts +65 -0
  59. package/dist/ai/orchestrator/runner.d.ts.map +1 -0
  60. package/dist/ai/orchestrator/runner.js +223 -0
  61. package/dist/ai/orchestrator/runner.js.map +1 -0
  62. package/dist/ai/orchestrator/session.d.ts +44 -0
  63. package/dist/ai/orchestrator/session.d.ts.map +1 -0
  64. package/dist/ai/orchestrator/session.js +64 -0
  65. package/dist/ai/orchestrator/session.js.map +1 -0
  66. package/dist/ai/orchestrator/types.d.ts +99 -0
  67. package/dist/ai/orchestrator/types.d.ts.map +1 -0
  68. package/dist/ai/orchestrator/types.js +8 -0
  69. package/dist/ai/orchestrator/types.js.map +1 -0
  70. package/dist/ai/probes/cache.d.ts +12 -0
  71. package/dist/ai/probes/cache.d.ts.map +1 -0
  72. package/dist/ai/probes/cache.js +46 -0
  73. package/dist/ai/probes/cache.js.map +1 -0
  74. package/dist/ai/tools/ask-ai-engine.d.ts +77 -0
  75. package/dist/ai/tools/ask-ai-engine.d.ts.map +1 -0
  76. package/dist/ai/tools/ask-ai-engine.js +253 -0
  77. package/dist/ai/tools/ask-ai-engine.js.map +1 -0
  78. package/dist/ai/tools/check-domain-crawler-access.d.ts +71 -0
  79. package/dist/ai/tools/check-domain-crawler-access.d.ts.map +1 -0
  80. package/dist/ai/tools/check-domain-crawler-access.js +76 -0
  81. package/dist/ai/tools/check-domain-crawler-access.js.map +1 -0
  82. package/dist/ai/tools/check-domain-llms-txt.d.ts +70 -0
  83. package/dist/ai/tools/check-domain-llms-txt.d.ts.map +1 -0
  84. package/dist/ai/tools/check-domain-llms-txt.js +75 -0
  85. package/dist/ai/tools/check-domain-llms-txt.js.map +1 -0
  86. package/dist/ai/tools/check-indexability.d.ts +58 -0
  87. package/dist/ai/tools/check-indexability.d.ts.map +1 -0
  88. package/dist/ai/tools/check-indexability.js +64 -0
  89. package/dist/ai/tools/check-indexability.js.map +1 -0
  90. package/dist/ai/tools/check-robots.d.ts +68 -0
  91. package/dist/ai/tools/check-robots.d.ts.map +1 -0
  92. package/dist/ai/tools/check-robots.js +90 -0
  93. package/dist/ai/tools/check-robots.js.map +1 -0
  94. package/dist/ai/tools/check-rule-answer-first.d.ts +54 -0
  95. package/dist/ai/tools/check-rule-answer-first.d.ts.map +1 -0
  96. package/dist/ai/tools/check-rule-answer-first.js +50 -0
  97. package/dist/ai/tools/check-rule-answer-first.js.map +1 -0
  98. package/dist/ai/tools/check-rule-canonical-consistency.d.ts +66 -0
  99. package/dist/ai/tools/check-rule-canonical-consistency.d.ts.map +1 -0
  100. package/dist/ai/tools/check-rule-canonical-consistency.js +51 -0
  101. package/dist/ai/tools/check-rule-canonical-consistency.js.map +1 -0
  102. package/dist/ai/tools/check-rule-citable-facts.d.ts +58 -0
  103. package/dist/ai/tools/check-rule-citable-facts.d.ts.map +1 -0
  104. package/dist/ai/tools/check-rule-citable-facts.js +41 -0
  105. package/dist/ai/tools/check-rule-citable-facts.js.map +1 -0
  106. package/dist/ai/tools/check-rule-content-modularity.d.ts +58 -0
  107. package/dist/ai/tools/check-rule-content-modularity.d.ts.map +1 -0
  108. package/dist/ai/tools/check-rule-content-modularity.js +45 -0
  109. package/dist/ai/tools/check-rule-content-modularity.js.map +1 -0
  110. package/dist/ai/tools/check-rule-faq-coverage.d.ts +54 -0
  111. package/dist/ai/tools/check-rule-faq-coverage.d.ts.map +1 -0
  112. package/dist/ai/tools/check-rule-faq-coverage.js +39 -0
  113. package/dist/ai/tools/check-rule-faq-coverage.js.map +1 -0
  114. package/dist/ai/tools/check-rule-freshness-signals.d.ts +54 -0
  115. package/dist/ai/tools/check-rule-freshness-signals.d.ts.map +1 -0
  116. package/dist/ai/tools/check-rule-freshness-signals.js +45 -0
  117. package/dist/ai/tools/check-rule-freshness-signals.js.map +1 -0
  118. package/dist/ai/tools/check-rule-json-ld-valid.d.ts +54 -0
  119. package/dist/ai/tools/check-rule-json-ld-valid.d.ts.map +1 -0
  120. package/dist/ai/tools/check-rule-json-ld-valid.js +44 -0
  121. package/dist/ai/tools/check-rule-json-ld-valid.js.map +1 -0
  122. package/dist/ai/tools/check-rule-missing-author.d.ts +54 -0
  123. package/dist/ai/tools/check-rule-missing-author.d.ts.map +1 -0
  124. package/dist/ai/tools/check-rule-missing-author.js +45 -0
  125. package/dist/ai/tools/check-rule-missing-author.js.map +1 -0
  126. package/dist/ai/tools/check-rule-near-duplicate.d.ts +82 -0
  127. package/dist/ai/tools/check-rule-near-duplicate.d.ts.map +1 -0
  128. package/dist/ai/tools/check-rule-near-duplicate.js +63 -0
  129. package/dist/ai/tools/check-rule-near-duplicate.js.map +1 -0
  130. package/dist/ai/tools/check-rule-required-fields.d.ts +50 -0
  131. package/dist/ai/tools/check-rule-required-fields.d.ts.map +1 -0
  132. package/dist/ai/tools/check-rule-required-fields.js +38 -0
  133. package/dist/ai/tools/check-rule-required-fields.js.map +1 -0
  134. package/dist/ai/tools/check-rule-schema-consistency.d.ts +54 -0
  135. package/dist/ai/tools/check-rule-schema-consistency.d.ts.map +1 -0
  136. package/dist/ai/tools/check-rule-schema-consistency.js +44 -0
  137. package/dist/ai/tools/check-rule-schema-consistency.js.map +1 -0
  138. package/dist/ai/tools/check-rule-summary-bait.d.ts +54 -0
  139. package/dist/ai/tools/check-rule-summary-bait.d.ts.map +1 -0
  140. package/dist/ai/tools/check-rule-summary-bait.js +39 -0
  141. package/dist/ai/tools/check-rule-summary-bait.js.map +1 -0
  142. package/dist/ai/tools/check-rule-thin-content.d.ts +66 -0
  143. package/dist/ai/tools/check-rule-thin-content.d.ts.map +1 -0
  144. package/dist/ai/tools/check-rule-thin-content.js +58 -0
  145. package/dist/ai/tools/check-rule-thin-content.js.map +1 -0
  146. package/dist/ai/tools/detect-templates.d.ts +60 -0
  147. package/dist/ai/tools/detect-templates.d.ts.map +1 -0
  148. package/dist/ai/tools/detect-templates.js +43 -0
  149. package/dist/ai/tools/detect-templates.js.map +1 -0
  150. package/dist/ai/tools/fetch-page.d.ts +70 -0
  151. package/dist/ai/tools/fetch-page.d.ts.map +1 -0
  152. package/dist/ai/tools/fetch-page.js +93 -0
  153. package/dist/ai/tools/fetch-page.js.map +1 -0
  154. package/dist/ai/tools/fetch-sitemap.d.ts +60 -0
  155. package/dist/ai/tools/fetch-sitemap.d.ts.map +1 -0
  156. package/dist/ai/tools/fetch-sitemap.js +116 -0
  157. package/dist/ai/tools/fetch-sitemap.js.map +1 -0
  158. package/dist/ai/tools/index.d.ts +1555 -0
  159. package/dist/ai/tools/index.d.ts.map +1 -0
  160. package/dist/ai/tools/index.js +119 -0
  161. package/dist/ai/tools/index.js.map +1 -0
  162. package/dist/ai/tools/parse-page.d.ts +94 -0
  163. package/dist/ai/tools/parse-page.d.ts.map +1 -0
  164. package/dist/ai/tools/parse-page.js +108 -0
  165. package/dist/ai/tools/parse-page.js.map +1 -0
  166. package/dist/ai/tools/query-serp.d.ts +113 -0
  167. package/dist/ai/tools/query-serp.d.ts.map +1 -0
  168. package/dist/ai/tools/query-serp.js +131 -0
  169. package/dist/ai/tools/query-serp.js.map +1 -0
  170. package/dist/ai/tools/sample-template.d.ts +67 -0
  171. package/dist/ai/tools/sample-template.d.ts.map +1 -0
  172. package/dist/ai/tools/sample-template.js +75 -0
  173. package/dist/ai/tools/sample-template.js.map +1 -0
  174. package/dist/ai/tools/types.d.ts +73 -0
  175. package/dist/ai/tools/types.d.ts.map +1 -0
  176. package/dist/ai/tools/types.js +64 -0
  177. package/dist/ai/tools/types.js.map +1 -0
  178. package/dist/ai/tools/validate-jsonld.d.ts +62 -0
  179. package/dist/ai/tools/validate-jsonld.d.ts.map +1 -0
  180. package/dist/ai/tools/validate-jsonld.js +84 -0
  181. package/dist/ai/tools/validate-jsonld.js.map +1 -0
  182. package/dist/auditor.d.ts +16 -1
  183. package/dist/auditor.d.ts.map +1 -1
  184. package/dist/auditor.js +862 -88
  185. package/dist/auditor.js.map +1 -1
  186. package/dist/backpressure.d.ts.map +1 -1
  187. package/dist/backpressure.js +10 -3
  188. package/dist/backpressure.js.map +1 -1
  189. package/dist/enrich-findings.d.ts.map +1 -1
  190. package/dist/enrich-findings.js +15 -1
  191. package/dist/enrich-findings.js.map +1 -1
  192. package/dist/formatters/bucket-findings.d.ts +43 -0
  193. package/dist/formatters/bucket-findings.d.ts.map +1 -0
  194. package/dist/formatters/bucket-findings.js +110 -0
  195. package/dist/formatters/bucket-findings.js.map +1 -0
  196. package/dist/formatters/console.d.ts.map +1 -1
  197. package/dist/formatters/console.js +116 -34
  198. package/dist/formatters/console.js.map +1 -1
  199. package/dist/formatters/fixplan.d.ts +13 -0
  200. package/dist/formatters/fixplan.d.ts.map +1 -0
  201. package/dist/formatters/fixplan.js +328 -0
  202. package/dist/formatters/fixplan.js.map +1 -0
  203. package/dist/formatters/html.d.ts.map +1 -1
  204. package/dist/formatters/html.js +27 -0
  205. package/dist/formatters/html.js.map +1 -1
  206. package/dist/formatters/index.d.ts +2 -0
  207. package/dist/formatters/index.d.ts.map +1 -1
  208. package/dist/formatters/index.js +1 -0
  209. package/dist/formatters/index.js.map +1 -1
  210. package/dist/formatters/markdown.d.ts.map +1 -1
  211. package/dist/formatters/markdown.js +97 -9
  212. package/dist/formatters/markdown.js.map +1 -1
  213. package/dist/index.d.ts +12 -1
  214. package/dist/index.d.ts.map +1 -1
  215. package/dist/index.js +8 -0
  216. package/dist/index.js.map +1 -1
  217. package/dist/page-filter.d.ts +64 -6
  218. package/dist/page-filter.d.ts.map +1 -1
  219. package/dist/page-filter.js +124 -3
  220. package/dist/page-filter.js.map +1 -1
  221. package/dist/rule-references.d.ts.map +1 -1
  222. package/dist/rule-references.js +5 -0
  223. package/dist/rule-references.js.map +1 -1
  224. package/dist/rules/aeo/answer-first.d.ts.map +1 -1
  225. package/dist/rules/aeo/answer-first.js +17 -3
  226. package/dist/rules/aeo/answer-first.js.map +1 -1
  227. package/dist/rules/aeo/citable-facts.d.ts.map +1 -1
  228. package/dist/rules/aeo/citable-facts.js +12 -1
  229. package/dist/rules/aeo/citable-facts.js.map +1 -1
  230. package/dist/rules/aeo/content-modularity.d.ts.map +1 -1
  231. package/dist/rules/aeo/content-modularity.js +3 -0
  232. package/dist/rules/aeo/content-modularity.js.map +1 -1
  233. package/dist/rules/aeo/crawler-access.d.ts.map +1 -1
  234. package/dist/rules/aeo/crawler-access.js +6 -0
  235. package/dist/rules/aeo/crawler-access.js.map +1 -1
  236. package/dist/rules/aeo/faq-coverage.d.ts.map +1 -1
  237. package/dist/rules/aeo/faq-coverage.js +4 -0
  238. package/dist/rules/aeo/faq-coverage.js.map +1 -1
  239. package/dist/rules/aeo/freshness-signals.d.ts.map +1 -1
  240. package/dist/rules/aeo/freshness-signals.js +9 -2
  241. package/dist/rules/aeo/freshness-signals.js.map +1 -1
  242. package/dist/rules/aeo/llms-txt.d.ts.map +1 -1
  243. package/dist/rules/aeo/llms-txt.js +6 -1
  244. package/dist/rules/aeo/llms-txt.js.map +1 -1
  245. package/dist/rules/aeo/summary-bait.d.ts.map +1 -1
  246. package/dist/rules/aeo/summary-bait.js +5 -2
  247. package/dist/rules/aeo/summary-bait.js.map +1 -1
  248. package/dist/rules/content/heading-structure.d.ts +21 -0
  249. package/dist/rules/content/heading-structure.d.ts.map +1 -0
  250. package/dist/rules/content/heading-structure.js +56 -0
  251. package/dist/rules/content/heading-structure.js.map +1 -0
  252. package/dist/rules/content/image-alt-text.d.ts +18 -0
  253. package/dist/rules/content/image-alt-text.d.ts.map +1 -0
  254. package/dist/rules/content/image-alt-text.js +77 -0
  255. package/dist/rules/content/image-alt-text.js.map +1 -0
  256. package/dist/rules/content/missing-author.d.ts.map +1 -1
  257. package/dist/rules/content/missing-author.js +10 -2
  258. package/dist/rules/content/missing-author.js.map +1 -1
  259. package/dist/rules/content/title-uniqueness.d.ts +18 -0
  260. package/dist/rules/content/title-uniqueness.d.ts.map +1 -0
  261. package/dist/rules/content/title-uniqueness.js +70 -0
  262. package/dist/rules/content/title-uniqueness.js.map +1 -0
  263. package/dist/rules/links/host-section-divergence.d.ts +3 -0
  264. package/dist/rules/links/host-section-divergence.d.ts.map +1 -0
  265. package/dist/rules/links/host-section-divergence.js +158 -0
  266. package/dist/rules/links/host-section-divergence.js.map +1 -0
  267. package/dist/rules/links/link-depth.d.ts +12 -1
  268. package/dist/rules/links/link-depth.d.ts.map +1 -1
  269. package/dist/rules/links/link-depth.js +25 -12
  270. package/dist/rules/links/link-depth.js.map +1 -1
  271. package/dist/rules/scope.d.ts.map +1 -1
  272. package/dist/rules/scope.js +5 -0
  273. package/dist/rules/scope.js.map +1 -1
  274. package/dist/rules/spam/doorway-pattern.d.ts.map +1 -1
  275. package/dist/rules/spam/doorway-pattern.js +27 -4
  276. package/dist/rules/spam/doorway-pattern.js.map +1 -1
  277. package/dist/rules/spam/publication-velocity.d.ts +1 -1
  278. package/dist/rules/spam/publication-velocity.d.ts.map +1 -1
  279. package/dist/rules/spam/publication-velocity.js +9 -4
  280. package/dist/rules/spam/publication-velocity.js.map +1 -1
  281. package/dist/rules/spam/template-coverage.js +1 -1
  282. package/dist/rules/spam/template-coverage.js.map +1 -1
  283. package/dist/rules/spam/template-diversity.js +1 -1
  284. package/dist/rules/spam/template-diversity.js.map +1 -1
  285. package/dist/rules/spam/thin-content.d.ts.map +1 -1
  286. package/dist/rules/spam/thin-content.js +9 -1
  287. package/dist/rules/spam/thin-content.js.map +1 -1
  288. package/dist/rules/tech/hreflang-consistency.d.ts.map +1 -1
  289. package/dist/rules/tech/hreflang-consistency.js +33 -4
  290. package/dist/rules/tech/hreflang-consistency.js.map +1 -1
  291. package/dist/rules/tech/og-completeness.d.ts +11 -0
  292. package/dist/rules/tech/og-completeness.d.ts.map +1 -1
  293. package/dist/rules/tech/og-completeness.js +22 -23
  294. package/dist/rules/tech/og-completeness.js.map +1 -1
  295. package/dist/ruleset-version.d.ts +8 -0
  296. package/dist/ruleset-version.d.ts.map +1 -0
  297. package/dist/ruleset-version.js +8 -0
  298. package/dist/ruleset-version.js.map +1 -0
  299. package/dist/scrape-strategy.d.ts +42 -0
  300. package/dist/scrape-strategy.d.ts.map +1 -0
  301. package/dist/scrape-strategy.js +101 -0
  302. package/dist/scrape-strategy.js.map +1 -0
  303. package/dist/site-classifier.d.ts +1 -1
  304. package/dist/site-classifier.d.ts.map +1 -1
  305. package/dist/site-classifier.js +217 -0
  306. package/dist/site-classifier.js.map +1 -1
  307. package/dist/state.d.ts +36 -1
  308. package/dist/state.d.ts.map +1 -1
  309. package/dist/state.js +3 -1
  310. package/dist/state.js.map +1 -1
  311. package/dist/stratified-sample.d.ts +9 -1
  312. package/dist/stratified-sample.d.ts.map +1 -1
  313. package/dist/stratified-sample.js +23 -6
  314. package/dist/stratified-sample.js.map +1 -1
  315. package/dist/types.d.ts +179 -2
  316. package/dist/types.d.ts.map +1 -1
  317. package/dist/types.js.map +1 -1
  318. package/dist/url-normalize.d.ts.map +1 -1
  319. package/dist/url-normalize.js +13 -1
  320. package/dist/url-normalize.js.map +1 -1
  321. package/package.json +90 -90
@@ -1,5 +1,16 @@
1
+ /**
2
+ * tech/og-completeness — flags pages missing the core Open Graph metadata
3
+ * that drives social-share previews and is increasingly used by AI Overviews
4
+ * as a fallback summary signal.
5
+ *
6
+ * Required: og:title, og:description, og:image.
7
+ *
8
+ * The rule was referenced in the v0.4.x README without ever shipping. The
9
+ * 2026-05-03 blind-spot audit surfaced it as a tier-1 gap; this is the
10
+ * v0.5.2 fix.
11
+ */
1
12
  export function ogCompletenessRule(pages) {
2
- const incomplete = [];
13
+ const findings = [];
3
14
  for (const page of pages) {
4
15
  const missing = [];
5
16
  if (!page.og.title)
@@ -8,28 +19,16 @@ export function ogCompletenessRule(pages) {
8
19
  missing.push("og:description");
9
20
  if (!page.og.image)
10
21
  missing.push("og:image");
11
- if (missing.length > 0) {
12
- incomplete.push({ url: page.url, missing });
13
- }
22
+ if (missing.length === 0)
23
+ continue;
24
+ findings.push({
25
+ ruleId: "tech/og-completeness",
26
+ severity: "warning",
27
+ message: `${page.url} is missing Open Graph tags: ${missing.join(", ")}.`,
28
+ pageUrl: page.url,
29
+ fix: `Add the missing meta tags inside <head>: ${missing.map((tag) => `<meta property="${tag}" content="...">`).join(" ")}.`,
30
+ });
14
31
  }
15
- if (incomplete.length === 0)
16
- return [];
17
- if (incomplete.length === pages.length && pages.length > 3) {
18
- const allMissing = new Set(incomplete.flatMap((i) => i.missing));
19
- return [{
20
- ruleId: "tech/og-completeness",
21
- severity: "warning",
22
- message: `All ${incomplete.length} pages are missing Open Graph tags (${Array.from(allMissing).join(", ")}).`,
23
- fix: `Add Open Graph tags site-wide: ${Array.from(allMissing).join(", ")}.`,
24
- relatedUrls: incomplete.map((i) => i.url).sort()
25
- }];
26
- }
27
- return incomplete.map((item) => ({
28
- ruleId: "tech/og-completeness",
29
- severity: "warning",
30
- message: `${item.url} is missing ${item.missing.join(", ")}.`,
31
- pageUrl: item.url,
32
- fix: `Add the missing Open Graph tags: ${item.missing.join(", ")}.`
33
- }));
32
+ return findings;
34
33
  }
35
34
  //# sourceMappingURL=og-completeness.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"og-completeness.js","sourceRoot":"","sources":["../../../src/rules/tech/og-completeness.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,kBAAkB,CAAC,KAAmB;IACpD,MAAM,UAAU,GAA8C,EAAE,CAAC;IAEjE,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK;YAAE,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC7C,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,WAAW;YAAE,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QACzD,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK;YAAE,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC7C,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,UAAU,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;IAED,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEvC,IAAI,UAAU,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3D,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QACjE,OAAO,CAAC;gBACN,MAAM,EAAE,sBAAsB;gBAC9B,QAAQ,EAAE,SAAS;gBACnB,OAAO,EAAE,OAAO,UAAU,CAAC,MAAM,uCAAuC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;gBAC7G,GAAG,EAAE,kCAAkC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;gBAC3E,WAAW,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE;aACjD,CAAC,CAAC;IACL,CAAC;IAED,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC/B,MAAM,EAAE,sBAA+B;QACvC,QAAQ,EAAE,SAAkB;QAC5B,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,eAAe,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;QAC7D,OAAO,EAAE,IAAI,CAAC,GAAG;QACjB,GAAG,EAAE,oCAAoC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;KACpE,CAAC,CAAC,CAAC;AACN,CAAC"}
1
+ {"version":3,"file":"og-completeness.js","sourceRoot":"","sources":["../../../src/rules/tech/og-completeness.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAmB;IACpD,MAAM,QAAQ,GAAiB,EAAE,CAAC;IAClC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK;YAAE,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC7C,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,WAAW;YAAE,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QACzD,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK;YAAE,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC7C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QACnC,QAAQ,CAAC,IAAI,CAAC;YACZ,MAAM,EAAE,sBAAsB;YAC9B,QAAQ,EAAE,SAAS;YACnB,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,gCAAgC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;YACzE,OAAO,EAAE,IAAI,CAAC,GAAG;YACjB,GAAG,EAAE,4CAA4C,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,mBAAmB,GAAG,kBAAkB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG;SAC7H,CAAC,CAAC;IACL,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Bump when adding a rule, materially changing rule logic, or changing a default
3
+ * threshold in a way that would change findings on previously-audited pages.
4
+ * Pure refactor → don't bump. Used by change-driven monitoring to invalidate
5
+ * skips when a new rule wouldn't otherwise run on prior-state-only URLs.
6
+ */
7
+ export declare const CORE_RULESET_VERSION = "15";
8
+ //# sourceMappingURL=ruleset-version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ruleset-version.d.ts","sourceRoot":"","sources":["../src/ruleset-version.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,OAAO,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Bump when adding a rule, materially changing rule logic, or changing a default
3
+ * threshold in a way that would change findings on previously-audited pages.
4
+ * Pure refactor → don't bump. Used by change-driven monitoring to invalidate
5
+ * skips when a new rule wouldn't otherwise run on prior-state-only URLs.
6
+ */
7
+ export const CORE_RULESET_VERSION = "15";
8
+ //# sourceMappingURL=ruleset-version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ruleset-version.js","sourceRoot":"","sources":["../src/ruleset-version.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,CAAC"}
@@ -0,0 +1,42 @@
1
+ import type { RunState } from "./state.js";
2
+ /** v0.5: shared default age-floor for monitoring. Single source of truth. */
3
+ export declare const DEFAULT_AGE_FLOOR_DAYS = 7;
4
+ export type RefetchReason = "new" | "age" | "ruleset" | "recheck" | "lastmod" | "gsc" | "no-signal" | "watched";
5
+ export type SkipReason = "unchanged";
6
+ export interface ScrapePlan {
7
+ refetch: Map<string, RefetchReason>;
8
+ skip: Map<string, SkipReason>;
9
+ }
10
+ export interface GscDelta {
11
+ impressionsDelta: number;
12
+ clicksDelta: number;
13
+ }
14
+ export interface GscThresholds {
15
+ impressionsPct: number;
16
+ clicksAbsolute: number;
17
+ }
18
+ export interface ScrapeStrategyInputs {
19
+ candidateUrls: readonly string[];
20
+ priorState: RunState | null;
21
+ sitemapLastmodByUrl: ReadonlyMap<string, string>;
22
+ gscDeltasByUrl?: ReadonlyMap<string, GscDelta>;
23
+ gscThresholds?: GscThresholds;
24
+ currentRulesetVersion: string;
25
+ ageFloorDays: number;
26
+ now: Date;
27
+ /**
28
+ * v0.5.3 — caller-supplied "watched pages" list. Any URL appearing here is
29
+ * marked refetch with reason `"watched"` and short-circuits the rest of the
30
+ * matrix (age, ruleset, lastmod, etc.). Watched URLs that aren't already in
31
+ * `candidateUrls` are still added to the audit set — the caller may
32
+ * legitimately watch a page that has been removed from the sitemap and we
33
+ * should still audit it so they find out it's gone.
34
+ *
35
+ * Owned by the caller (e.g. the web app's per-domain DB-backed list); the
36
+ * engine treats it as a transient input override and never persists it on
37
+ * `RunState`.
38
+ */
39
+ forceRefetchUrls?: ReadonlyArray<string>;
40
+ }
41
+ export declare function planScrapeStrategy(inputs: ScrapeStrategyInputs): ScrapePlan;
42
+ //# sourceMappingURL=scrape-strategy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scrape-strategy.d.ts","sourceRoot":"","sources":["../src/scrape-strategy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,6EAA6E;AAC7E,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAaxC,MAAM,MAAM,aAAa,GACrB,KAAK,GACL,KAAK,GACL,SAAS,GACT,SAAS,GACT,SAAS,GACT,KAAK,GACL,WAAW,GACX,SAAS,CAAC;AAEd,MAAM,MAAM,UAAU,GAAG,WAAW,CAAC;AAErC,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACpC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,QAAQ;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,oBAAoB;IACnC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,UAAU,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC5B,mBAAmB,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjD,cAAc,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC/C,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,GAAG,EAAE,IAAI,CAAC;IACV;;;;;;;;;;;OAWG;IACH,gBAAgB,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAC1C;AAyBD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,oBAAoB,GAAG,UAAU,CAgF3E"}
@@ -0,0 +1,101 @@
1
+ /** v0.5: shared default age-floor for monitoring. Single source of truth. */
2
+ export const DEFAULT_AGE_FLOOR_DAYS = 7;
3
+ /**
4
+ * v0.5: severities that trigger an unconditional recheck under monitoring mode.
5
+ * Open blockers and warnings are re-verified every run; informational findings
6
+ * are carried forward without re-fetching the page. Without this gate the
7
+ * carry-forward path would be dead code on any site with any prior finding,
8
+ * because every URL with findings would round-trip through `recheck` and be
9
+ * fetched anyway. Pre-v0.5 had no recheck distinction (no monitoring); the
10
+ * gate is the load-bearing semantic difference.
11
+ */
12
+ const RECHECK_SEVERITIES = new Set(["error", "critical", "warning", "warn"]);
13
+ const MS_PER_DAY = 24 * 60 * 60 * 1000;
14
+ function gscExceedsThreshold(delta, thresholds) {
15
+ return Math.abs(delta.impressionsDelta) >= thresholds.impressionsPct
16
+ || Math.abs(delta.clicksDelta) >= thresholds.clicksAbsolute;
17
+ }
18
+ /**
19
+ * Returns true when the URL has at least one prior finding whose severity is
20
+ * in `RECHECK_SEVERITIES`. Informational findings alone do NOT trigger
21
+ * recheck — they're carried forward. Pre-v0.5 state files (or carriers from
22
+ * older runs) only have `findingIds`; for those we can't tell severity, so we
23
+ * assume worst-case and recheck. New runs persist full Finding records, so
24
+ * the severity-gated path applies on the very next monitoring run.
25
+ */
26
+ function priorFindingsTriggerRecheck(prior) {
27
+ if (prior.findings.length > 0) {
28
+ return prior.findings.some((f) => RECHECK_SEVERITIES.has(f.severity));
29
+ }
30
+ // Fallback: legacy entries with findingIds but no full records — be safe.
31
+ return prior.findingIds.length > 0;
32
+ }
33
+ export function planScrapeStrategy(inputs) {
34
+ const refetch = new Map();
35
+ const skip = new Map();
36
+ // v0.5.3: caller-curated watched pages. A watched URL is always refetched
37
+ // and short-circuits the rest of the matrix so the dashboard can attribute
38
+ // the refetch to the user's explicit request rather than to "new"/"age"/etc.
39
+ // Watched URLs absent from `candidateUrls` (e.g. removed from the sitemap)
40
+ // are also included so the user finds out the page is gone.
41
+ const watchedSet = inputs.forceRefetchUrls && inputs.forceRefetchUrls.length > 0
42
+ ? new Set(inputs.forceRefetchUrls)
43
+ : null;
44
+ const visited = new Set();
45
+ const evalOrder = [];
46
+ if (watchedSet) {
47
+ for (const url of watchedSet) {
48
+ if (!visited.has(url)) {
49
+ visited.add(url);
50
+ evalOrder.push(url);
51
+ }
52
+ }
53
+ }
54
+ for (const url of inputs.candidateUrls) {
55
+ if (!visited.has(url)) {
56
+ visited.add(url);
57
+ evalOrder.push(url);
58
+ }
59
+ }
60
+ for (const url of evalOrder) {
61
+ if (watchedSet && watchedSet.has(url)) {
62
+ refetch.set(url, "watched");
63
+ continue;
64
+ }
65
+ const prior = inputs.priorState?.urls[url];
66
+ if (!prior) {
67
+ refetch.set(url, "new");
68
+ continue;
69
+ }
70
+ const ageMs = inputs.now.getTime() - Date.parse(prior.fetchedAt);
71
+ if (Number.isFinite(ageMs) && ageMs > inputs.ageFloorDays * MS_PER_DAY) {
72
+ refetch.set(url, "age");
73
+ continue;
74
+ }
75
+ if (prior.rulesetVersion !== inputs.currentRulesetVersion) {
76
+ refetch.set(url, "ruleset");
77
+ continue;
78
+ }
79
+ if (priorFindingsTriggerRecheck(prior)) {
80
+ refetch.set(url, "recheck");
81
+ continue;
82
+ }
83
+ const lastmod = inputs.sitemapLastmodByUrl.get(url);
84
+ if (lastmod && Date.parse(lastmod) > Date.parse(prior.fetchedAt)) {
85
+ refetch.set(url, "lastmod");
86
+ continue;
87
+ }
88
+ const gscDelta = inputs.gscDeltasByUrl?.get(url);
89
+ if (gscDelta && inputs.gscThresholds && gscExceedsThreshold(gscDelta, inputs.gscThresholds)) {
90
+ refetch.set(url, "gsc");
91
+ continue;
92
+ }
93
+ if (!lastmod && !gscDelta) {
94
+ refetch.set(url, "no-signal");
95
+ continue;
96
+ }
97
+ skip.set(url, "unchanged");
98
+ }
99
+ return { refetch, skip };
100
+ }
101
+ //# sourceMappingURL=scrape-strategy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scrape-strategy.js","sourceRoot":"","sources":["../src/scrape-strategy.ts"],"names":[],"mappings":"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"}
@@ -19,7 +19,7 @@
19
19
  * - Per-cluster classification (mixed pSEO + marketing).
20
20
  * - Per-page applicability tagging on findings.
21
21
  */
22
- export type SiteType = "programmatic-directory" | "small-marketing" | "blog" | "ecommerce" | "unclear";
22
+ export type SiteType = "programmatic-directory" | "small-marketing" | "blog" | "ecommerce" | "docs" | "unclear";
23
23
  export type ClassificationSignal = {
24
24
  kind: "sitemap-url-count";
25
25
  value: number;
@@ -1 +1 @@
1
- {"version":3,"file":"site-classifier.d.ts","sourceRoot":"","sources":["../src/site-classifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,MAAM,MAAM,QAAQ,GAChB,wBAAwB,GACxB,iBAAiB,GACjB,MAAM,GACN,WAAW,GACX,SAAS,CAAC;AAEd,MAAM,MAAM,oBAAoB,GAC5B;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC5C;IACE,IAAI,EAAE,8BAA8B,CAAC;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf,GACD;IAAE,IAAI,EAAE,oBAAoB,CAAC;IAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAA;CAAE,CAAC;AAEnF,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,QAAQ,CAAC;IACf,wEAAwE;IACxE,UAAU,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,OAAO,EAAE,oBAAoB,EAAE,CAAC;IAChC;;;;;;OAMG;IACH,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,oEAAoE;AACpE,eAAO,MAAM,kBAAkB,EAAE,SAAS,MAAM,EAK/C,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAiChE;AAiBD,kFAAkF;AAClF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAiB7G;AAED,MAAM,WAAW,iBAAiB;IAChC,yDAAyD;IACzD,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,kEAAkE;IAClE,SAAS,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;CACrD;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,kBAAkB,CA6FzE"}
1
+ {"version":3,"file":"site-classifier.d.ts","sourceRoot":"","sources":["../src/site-classifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,MAAM,MAAM,QAAQ,GAChB,wBAAwB,GACxB,iBAAiB,GACjB,MAAM,GACN,WAAW,GACX,MAAM,GACN,SAAS,CAAC;AAEd,MAAM,MAAM,oBAAoB,GAC5B;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC5C;IACE,IAAI,EAAE,8BAA8B,CAAC;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf,GACD;IAAE,IAAI,EAAE,oBAAoB,CAAC;IAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAA;CAAE,CAAC;AAEnF,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,QAAQ,CAAC;IACf,wEAAwE;IACxE,UAAU,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,OAAO,EAAE,oBAAoB,EAAE,CAAC;IAChC;;;;;;OAMG;IACH,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,oEAAoE;AACpE,eAAO,MAAM,kBAAkB,EAAE,SAAS,MAAM,EAM/C,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAiChE;AA8BD,kFAAkF;AAClF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAiB7G;AAED,MAAM,WAAW,iBAAiB;IAChC,yDAAyD;IACzD,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,kEAAkE;IAClE,SAAS,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;CACrD;AA8KD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,kBAAkB,CA2HzE"}
@@ -25,6 +25,7 @@ export const PSEO_ONLY_RULE_IDS = [
25
25
  "spam/template-diversity",
26
26
  "spam/entity-swap",
27
27
  "cannibal/url-pattern",
28
+ "links/host-section-divergence",
28
29
  ];
29
30
  /**
30
31
  * Normalize a pathname into a hashable template by replacing path segments
@@ -71,6 +72,19 @@ export function normalizePathToTemplate(pathname) {
71
72
  });
72
73
  return "/" + out.join("/");
73
74
  }
75
+ /** Extract a normalized pathname from a URL string (or raw path). */
76
+ function urlToPath(url) {
77
+ try {
78
+ return new URL(url).pathname || "/";
79
+ }
80
+ catch {
81
+ if (typeof url === "string" && url.length > 0) {
82
+ const path = url.split("?")[0].split("#")[0];
83
+ return path.startsWith("/") ? path : `/${path}`;
84
+ }
85
+ return null;
86
+ }
87
+ }
74
88
  /** Convert a URL string to its template path (no host, no query). */
75
89
  function urlToTemplate(url) {
76
90
  try {
@@ -107,6 +121,178 @@ export function clusterUrlTemplates(urls) {
107
121
  entries.sort((a, b) => b.count - a.count || a.template.localeCompare(b.template));
108
122
  return entries;
109
123
  }
124
+ /**
125
+ * v0.4.3 — common docs path prefixes. Match is case-insensitive and exact
126
+ * on the FIRST path segment (so `/docs/...` matches but a stray `/somethingdocs`
127
+ * does not). Tuned to the docs frameworks we see most: Docusaurus, Nextra,
128
+ * GitBook, MkDocs, VuePress, mintlify, fumadocs, Astro Starlight.
129
+ */
130
+ const DOCS_PATH_PREFIXES = [
131
+ "/docs",
132
+ "/doc",
133
+ "/documentation",
134
+ "/api",
135
+ "/api-docs",
136
+ "/api-reference",
137
+ "/reference",
138
+ "/guide",
139
+ "/guides",
140
+ "/learn",
141
+ "/tutorials",
142
+ "/manual",
143
+ "/handbook",
144
+ ];
145
+ /**
146
+ * v0.4.3 — common ecommerce path prefixes. Same matching rules as docs:
147
+ * case-insensitive, anchored at the FIRST path segment.
148
+ */
149
+ const ECOMMERCE_PATH_PREFIXES = [
150
+ "/products",
151
+ "/product",
152
+ "/collections",
153
+ "/collection",
154
+ "/shop",
155
+ "/store",
156
+ "/cart",
157
+ "/checkout",
158
+ "/category",
159
+ "/categories",
160
+ "/p", // Shopify shortlink convention
161
+ ];
162
+ function firstSegmentMatches(pathname, prefixes) {
163
+ const segments = pathname.split("/").filter(Boolean);
164
+ if (segments.length === 0)
165
+ return false;
166
+ const first = `/${segments[0].toLowerCase()}`;
167
+ return prefixes.includes(first);
168
+ }
169
+ /** Ratio of URLs whose first path segment matches one of `prefixes`. */
170
+ function pathPrefixRatio(urls, prefixes) {
171
+ if (urls.length === 0)
172
+ return 0;
173
+ let hits = 0;
174
+ let total = 0;
175
+ for (const u of urls) {
176
+ const path = urlToPath(u);
177
+ if (path === null)
178
+ continue;
179
+ total += 1;
180
+ if (firstSegmentMatches(path, prefixes))
181
+ hits += 1;
182
+ }
183
+ if (total === 0)
184
+ return 0;
185
+ return hits / total;
186
+ }
187
+ /**
188
+ * v0.4.3 — docs site detection. A site is `docs` when its URL distribution
189
+ * is dominated by docs-shaped path prefixes:
190
+ * - 50+ URLs total (docs sites are not tiny — guard against false-positives
191
+ * on a 5-page marketing site with one /docs link)
192
+ * - ≥ 60% of URLs sit under a docs prefix
193
+ * Returns `null` when not enough evidence; otherwise the SiteClassification.
194
+ */
195
+ function tryClassifyDocs(urls, signals) {
196
+ if (urls.length < 50)
197
+ return null;
198
+ const ratio = pathPrefixRatio(urls, DOCS_PATH_PREFIXES);
199
+ if (ratio < 0.6)
200
+ return null;
201
+ let confidence = 0.7;
202
+ if (ratio >= 0.75)
203
+ confidence = 0.85;
204
+ if (ratio >= 0.9)
205
+ confidence = 0.92;
206
+ if (urls.length >= 200)
207
+ confidence = Math.min(0.95, confidence + 0.03);
208
+ return { type: "docs", confidence, signals, suppressedRules: [] };
209
+ }
210
+ /**
211
+ * v0.4.3 — ecommerce site detection. Two paths to a positive classification:
212
+ * - URL count ≥ 50 AND ≥ 70% of URLs match /products/* or /collections/*
213
+ * (high-confidence: this is the canonical Shopify/Woo URL shape)
214
+ * - URL count ≥ 100 AND ≥ 50% match a broader ecommerce-shaped prefix
215
+ * (looser fallback for sites that mix /shop, /store, /category)
216
+ * Returns `null` when not enough evidence.
217
+ */
218
+ function tryClassifyEcommerce(urls, signals) {
219
+ if (urls.length < 50)
220
+ return null;
221
+ const productsRatio = pathPrefixRatio(urls, ["/products", "/collections"]);
222
+ if (productsRatio >= 0.7) {
223
+ const confidence = productsRatio >= 0.85 ? 0.92 : 0.85;
224
+ return { type: "ecommerce", confidence, signals, suppressedRules: [] };
225
+ }
226
+ const broadRatio = pathPrefixRatio(urls, ECOMMERCE_PATH_PREFIXES);
227
+ if (broadRatio >= 0.5 && urls.length >= 100) {
228
+ const confidence = broadRatio >= 0.7 ? 0.85 : 0.75;
229
+ return { type: "ecommerce", confidence, signals, suppressedRules: [] };
230
+ }
231
+ return null;
232
+ }
233
+ /**
234
+ * Locale-code regex for the FIRST path segment. Matches BCP-47-shaped slugs:
235
+ * `/en`, `/de`, `/en-us`, `/zh-hant`, `/pt-br`, etc. Two-letter language code
236
+ * with optional two-letter region. Lowercase only — sites that uppercase
237
+ * locale codes are rare; we don't normalize.
238
+ */
239
+ const LOCALE_SEGMENT_REGEX = /^[a-z]{2}(?:-[a-z]{2})?$/;
240
+ function isLocalizedRoot(pathname) {
241
+ const segments = pathname.split("/").filter(Boolean);
242
+ if (segments.length === 0)
243
+ return false;
244
+ return LOCALE_SEGMENT_REGEX.test(segments[0]);
245
+ }
246
+ /** Ratio of URLs whose first path segment looks like a locale code. */
247
+ function localizedRatio(urls) {
248
+ if (urls.length === 0)
249
+ return 0;
250
+ let hits = 0;
251
+ let total = 0;
252
+ for (const u of urls) {
253
+ const path = urlToPath(u);
254
+ if (path === null)
255
+ continue;
256
+ total += 1;
257
+ if (isLocalizedRoot(path))
258
+ hits += 1;
259
+ }
260
+ if (total === 0)
261
+ return 0;
262
+ return hits / total;
263
+ }
264
+ /**
265
+ * v0.4.3-rc2 — localized-marketing detector. Stripe, Vercel, Linear, Cloudflare
266
+ * etc. publish each marketing page under multiple `/[lang]/` prefixes. Their
267
+ * sitemap looks like a programmatic directory to the size+cluster heuristic
268
+ * (10k+ URLs all matching `/:slug/:slug` after normalization), but they are
269
+ * NOT pSEO sites — they're high-quality marketing sites with i18n.
270
+ *
271
+ * Signal: ≥30% of URLs have a first segment that matches `/[a-z]{2}(-[a-z]{2})?/`.
272
+ * Returns small-marketing with 0.75 confidence (lower than ecommerce/docs
273
+ * because some localized pSEO directories DO exist — we'd rather under-classify
274
+ * here than mis-suppress real spam findings).
275
+ */
276
+ function tryClassifyLocalizedMarketing(urls, signals) {
277
+ const ratio = localizedRatio(urls);
278
+ if (ratio < 0.3)
279
+ return null;
280
+ // Higher confidence when the locale prefix dominates AND the site isn't
281
+ // ALSO matching the docs/ecommerce shape underneath the locale (those
282
+ // would have caught it earlier — by reaching this point we know it's
283
+ // generic marketing).
284
+ let confidence = 0.75;
285
+ if (ratio >= 0.5)
286
+ confidence = 0.82;
287
+ if (ratio >= 0.7)
288
+ confidence = 0.88;
289
+ return {
290
+ type: "small-marketing",
291
+ confidence,
292
+ signals,
293
+ suppressedRules: [...PSEO_ONLY_RULE_IDS],
294
+ };
295
+ }
110
296
  /**
111
297
  * Classify a site from its URL list + framework signal. Pure function.
112
298
  *
@@ -135,9 +321,36 @@ export function classifySite(input) {
135
321
  if (urls.length === 0) {
136
322
  return { type: "unclear", confidence: 0, signals, suppressedRules: [] };
137
323
  }
324
+ // v0.4.3 — try the new high-confidence URL-shape detectors first. These
325
+ // override the legacy size-based heuristics when they fire because URL
326
+ // shape is a stronger signal than raw URL count. Order matters:
327
+ // 1. ecommerce — strongest signal first; /products/* and /collections/*
328
+ // are an extremely specific URL shape that doesn't occur elsewhere.
329
+ // 2. docs — /docs/*, /api/*, /reference/* are nearly as specific.
330
+ // 3. localized-marketing — /[lang]/ first segment indicates i18n marketing
331
+ // (stripe.com, vercel.com, etc.) which v0.4.3-rc1 mis-classified as
332
+ // programmatic-directory because the localized URL count tripped the
333
+ // ≥1000 URL + ≥60% top-3 cluster heuristic.
334
+ // After all three fail, fall through to the legacy size-clustering logic.
335
+ const ecommerce = tryClassifyEcommerce(urls, signals);
336
+ if (ecommerce)
337
+ return ecommerce;
338
+ const docs = tryClassifyDocs(urls, signals);
339
+ if (docs)
340
+ return docs;
341
+ const localized = tryClassifyLocalizedMarketing(urls, signals);
342
+ if (localized)
343
+ return localized;
138
344
  // Step 4: synthesize.
139
345
  let type = "unclear";
140
346
  let confidence = 0;
347
+ // v0.4.3-rc3 — lowered programmatic-directory threshold from 1000 → 500
348
+ // after dogfood showed softschools.com (955 URLs, real pSEO directory)
349
+ // missing the cutoff and classifying as `unclear`. Rebalanced confidence:
350
+ // ≥ 1000 URLs + top3≥60% template ratio → 0.9
351
+ // ≥ 500 URLs + top3≥70% template ratio → 0.78 (slightly lower confidence
352
+ // because the smaller sample is noisier, but still well above the
353
+ // 0.7 profile-application cutoff)
141
354
  if (urls.length >= 1000) {
142
355
  if (top3Ratio >= 0.6) {
143
356
  type = "programmatic-directory";
@@ -150,6 +363,10 @@ export function classifySite(input) {
150
363
  confidence = 0.6;
151
364
  }
152
365
  }
366
+ else if (urls.length >= 500 && top3Ratio >= 0.7) {
367
+ type = "programmatic-directory";
368
+ confidence = 0.78;
369
+ }
153
370
  else if (urls.length < 50) {
154
371
  // Small site. Detect blog separately from generic small-marketing.
155
372
  //
@@ -1 +1 @@
1
- {"version":3,"file":"site-classifier.js","sourceRoot":"","sources":["../src/site-classifier.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"site-classifier.js","sourceRoot":"","sources":["../src/site-classifier.ts"],"names":[],"mappings":"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"}
package/dist/state.d.ts CHANGED
@@ -1,16 +1,51 @@
1
- export declare const STATE_SCHEMA_VERSION = 1;
1
+ export declare const STATE_SCHEMA_VERSION = 2;
2
2
  export type RenderMode = "static" | "rendered";
3
+ /**
4
+ * Permissive snapshot of a finding stored in state for carry-forward across
5
+ * delta runs. The canonical Finding type lives in types.ts; state intentionally
6
+ * accepts a wider shape so we can persist whatever fields the engine emitted at
7
+ * audit time without coupling state IO to the rule-result schema.
8
+ */
9
+ export interface Finding {
10
+ id: string;
11
+ ruleId: string;
12
+ severity: string;
13
+ confidence: string;
14
+ message: string;
15
+ url?: string;
16
+ [key: string]: unknown;
17
+ }
3
18
  export interface UrlStateEntry {
4
19
  contentHash: string;
5
20
  fetchedAt: string;
6
21
  status: number;
22
+ /** Kept for back-compat within v2; derived from `findings`. */
7
23
  findingIds: string[];
24
+ /** Full finding records persisted so unchanged URLs can carry findings forward. */
25
+ findings: Finding[];
26
+ /** Ruleset signature at the time this URL was last fetched. */
27
+ rulesetVersion: string;
28
+ /** HTTP `Last-Modified` response header captured at fetch. */
29
+ lastModified?: string;
30
+ /** HTTP `ETag` response header captured at fetch. */
31
+ etag?: string;
32
+ /** Sitemap `<lastmod>` value associated with this URL at the audit. */
33
+ sitemapLastmodAtAudit?: string;
34
+ gscMetricsAtLastRun?: {
35
+ impressions: number;
36
+ clicks: number;
37
+ period: string;
38
+ };
8
39
  }
9
40
  export interface RunState {
10
41
  version: number;
11
42
  lastRun: string;
43
+ /** ISO timestamp of the last full (non-delta) audit. */
44
+ lastFullAuditAt: string;
12
45
  source: string;
13
46
  renderMode: RenderMode;
47
+ /** Ruleset signature at the time this state file was written. */
48
+ rulesetVersion: string;
14
49
  urls: Record<string, UrlStateEntry>;
15
50
  summary: {
16
51
  score: number;
@@ -1 +1 @@
1
- {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE/C,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,UAAU,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACpC,OAAO,EAAE;QACP,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,EAAE,MAAM,CAAC;QACtB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACpC,CAAC;CACH;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOzD;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGvD;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CA8BtE;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAK7E"}
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE/C;;;;;GAKG;AACH,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,mFAAmF;IACnF,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,+DAA+D;IAC/D,cAAc,EAAE,MAAM,CAAC;IACvB,8DAA8D;IAC9D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qDAAqD;IACrD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,mBAAmB,CAAC,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/E;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,wDAAwD;IACxD,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,UAAU,CAAC;IACvB,iEAAiE;IACjE,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACpC,OAAO,EAAE;QACP,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,EAAE,MAAM,CAAC;QACtB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACpC,CAAC;CACH;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOzD;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGvD;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAgCtE;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAK7E"}
package/dist/state.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { readFile, writeFile, mkdir, rename } from "node:fs/promises";
3
3
  import { dirname } from "node:path";
4
- export const STATE_SCHEMA_VERSION = 1;
4
+ export const STATE_SCHEMA_VERSION = 2;
5
5
  /**
6
6
  * Normalize HTML so content hashing is stable across irrelevant diffs.
7
7
  *
@@ -47,8 +47,10 @@ export async function readState(path) {
47
47
  throw new Error(`unsupported state version ${state.version} at ${path}, expected ${STATE_SCHEMA_VERSION}`);
48
48
  }
49
49
  if (typeof state.lastRun !== "string" ||
50
+ typeof state.lastFullAuditAt !== "string" ||
50
51
  typeof state.source !== "string" ||
51
52
  typeof state.renderMode !== "string" ||
53
+ typeof state.rulesetVersion !== "string" ||
52
54
  !state.urls || typeof state.urls !== "object" ||
53
55
  !state.summary || typeof state.summary !== "object") {
54
56
  throw new Error(`state file at ${path} has malformed shape`);