@pseolint/core 0.7.0 → 0.7.2

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 (116) hide show
  1. package/dist/algorithms/authority/commoncrawl.d.ts +13 -0
  2. package/dist/algorithms/authority/commoncrawl.d.ts.map +1 -0
  3. package/dist/algorithms/authority/commoncrawl.js +17 -0
  4. package/dist/algorithms/authority/commoncrawl.js.map +1 -0
  5. package/dist/algorithms/authority/openpagerank.d.ts +19 -0
  6. package/dist/algorithms/authority/openpagerank.d.ts.map +1 -0
  7. package/dist/algorithms/authority/openpagerank.js +42 -0
  8. package/dist/algorithms/authority/openpagerank.js.map +1 -0
  9. package/dist/algorithms/authority/provider.d.ts +16 -0
  10. package/dist/algorithms/authority/provider.d.ts.map +1 -0
  11. package/dist/algorithms/authority/provider.js +24 -0
  12. package/dist/algorithms/authority/provider.js.map +1 -0
  13. package/dist/algorithms/auto-entity-mask.d.ts +19 -0
  14. package/dist/algorithms/auto-entity-mask.d.ts.map +1 -0
  15. package/dist/algorithms/auto-entity-mask.js +102 -0
  16. package/dist/algorithms/auto-entity-mask.js.map +1 -0
  17. package/dist/algorithms/example-regions.d.ts +22 -0
  18. package/dist/algorithms/example-regions.d.ts.map +1 -0
  19. package/dist/algorithms/example-regions.js +32 -0
  20. package/dist/algorithms/example-regions.js.map +1 -0
  21. package/dist/algorithms/fact-extraction.d.ts.map +1 -1
  22. package/dist/algorithms/fact-extraction.js +6 -0
  23. package/dist/algorithms/fact-extraction.js.map +1 -1
  24. package/dist/auditor.d.ts.map +1 -1
  25. package/dist/auditor.js +39 -9
  26. package/dist/auditor.js.map +1 -1
  27. package/dist/enrich-findings.d.ts.map +1 -1
  28. package/dist/enrich-findings.js +9 -8
  29. package/dist/enrich-findings.js.map +1 -1
  30. package/dist/index.d.ts +7 -0
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +6 -0
  33. package/dist/index.js.map +1 -1
  34. package/dist/rules/aeo/crawler-access.d.ts +14 -0
  35. package/dist/rules/aeo/crawler-access.d.ts.map +1 -1
  36. package/dist/rules/aeo/crawler-access.js +96 -15
  37. package/dist/rules/aeo/crawler-access.js.map +1 -1
  38. package/dist/rules/aeo/summary-bait.d.ts.map +1 -1
  39. package/dist/rules/aeo/summary-bait.js +4 -3
  40. package/dist/rules/aeo/summary-bait.js.map +1 -1
  41. package/dist/rules/content/common-phrase-reuse.d.ts.map +1 -1
  42. package/dist/rules/content/common-phrase-reuse.js +7 -2
  43. package/dist/rules/content/common-phrase-reuse.js.map +1 -1
  44. package/dist/rules/content/eeat-signals.d.ts +13 -0
  45. package/dist/rules/content/eeat-signals.d.ts.map +1 -1
  46. package/dist/rules/content/eeat-signals.js +36 -4
  47. package/dist/rules/content/eeat-signals.js.map +1 -1
  48. package/dist/rules/content/regurgitated-content.d.ts.map +1 -1
  49. package/dist/rules/content/regurgitated-content.js +11 -2
  50. package/dist/rules/content/regurgitated-content.js.map +1 -1
  51. package/dist/rules/content/translation-no-op.d.ts.map +1 -1
  52. package/dist/rules/content/translation-no-op.js +5 -1
  53. package/dist/rules/content/translation-no-op.js.map +1 -1
  54. package/dist/rules/content/unique-value.d.ts +15 -1
  55. package/dist/rules/content/unique-value.d.ts.map +1 -1
  56. package/dist/rules/content/unique-value.js +46 -39
  57. package/dist/rules/content/unique-value.js.map +1 -1
  58. package/dist/rules/content/value-add.d.ts +8 -2
  59. package/dist/rules/content/value-add.d.ts.map +1 -1
  60. package/dist/rules/content/value-add.js +39 -48
  61. package/dist/rules/content/value-add.js.map +1 -1
  62. package/dist/rules/content/wikipedia-paraphrase.d.ts +12 -7
  63. package/dist/rules/content/wikipedia-paraphrase.d.ts.map +1 -1
  64. package/dist/rules/content/wikipedia-paraphrase.js +52 -13
  65. package/dist/rules/content/wikipedia-paraphrase.js.map +1 -1
  66. package/dist/rules/links/cluster-connectivity.d.ts +7 -1
  67. package/dist/rules/links/cluster-connectivity.d.ts.map +1 -1
  68. package/dist/rules/links/cluster-connectivity.js +8 -2
  69. package/dist/rules/links/cluster-connectivity.js.map +1 -1
  70. package/dist/rules/links/orphan-pages.d.ts +8 -1
  71. package/dist/rules/links/orphan-pages.d.ts.map +1 -1
  72. package/dist/rules/links/orphan-pages.js +10 -1
  73. package/dist/rules/links/orphan-pages.js.map +1 -1
  74. package/dist/rules/schema/consistency.d.ts.map +1 -1
  75. package/dist/rules/schema/consistency.js +37 -21
  76. package/dist/rules/schema/consistency.js.map +1 -1
  77. package/dist/rules/schema/json-ld-valid.d.ts.map +1 -1
  78. package/dist/rules/schema/json-ld-valid.js +8 -1
  79. package/dist/rules/schema/json-ld-valid.js.map +1 -1
  80. package/dist/rules/schema/required-fields.d.ts.map +1 -1
  81. package/dist/rules/schema/required-fields.js +47 -1
  82. package/dist/rules/schema/required-fields.js.map +1 -1
  83. package/dist/rules/spam/boilerplate-ratio.d.ts.map +1 -1
  84. package/dist/rules/spam/boilerplate-ratio.js +36 -22
  85. package/dist/rules/spam/boilerplate-ratio.js.map +1 -1
  86. package/dist/rules/spam/entity-swap.d.ts.map +1 -1
  87. package/dist/rules/spam/entity-swap.js +51 -9
  88. package/dist/rules/spam/entity-swap.js.map +1 -1
  89. package/dist/rules/spam/template-diversity.d.ts.map +1 -1
  90. package/dist/rules/spam/template-diversity.js +37 -2
  91. package/dist/rules/spam/template-diversity.js.map +1 -1
  92. package/dist/rules/spam/thin-content.d.ts.map +1 -1
  93. package/dist/rules/spam/thin-content.js +5 -1
  94. package/dist/rules/spam/thin-content.js.map +1 -1
  95. package/dist/rules/tech/canonical-consistency.d.ts.map +1 -1
  96. package/dist/rules/tech/canonical-consistency.js +144 -28
  97. package/dist/rules/tech/canonical-consistency.js.map +1 -1
  98. package/dist/rules/tech/og-completeness.d.ts +8 -3
  99. package/dist/rules/tech/og-completeness.d.ts.map +1 -1
  100. package/dist/rules/tech/og-completeness.js +15 -7
  101. package/dist/rules/tech/og-completeness.js.map +1 -1
  102. package/dist/rules/tech/sitemap-completeness.d.ts +14 -2
  103. package/dist/rules/tech/sitemap-completeness.d.ts.map +1 -1
  104. package/dist/rules/tech/sitemap-completeness.js +21 -5
  105. package/dist/rules/tech/sitemap-completeness.js.map +1 -1
  106. package/dist/rules/tech/soft-404.d.ts +11 -0
  107. package/dist/rules/tech/soft-404.d.ts.map +1 -1
  108. package/dist/rules/tech/soft-404.js +47 -5
  109. package/dist/rules/tech/soft-404.js.map +1 -1
  110. package/dist/template-detection.d.ts +1 -0
  111. package/dist/template-detection.d.ts.map +1 -1
  112. package/dist/template-detection.js +1 -1
  113. package/dist/template-detection.js.map +1 -1
  114. package/dist/types.d.ts +16 -1
  115. package/dist/types.d.ts.map +1 -1
  116. package/package.json +109 -93
@@ -16,8 +16,22 @@ export function resolveCanonicalUrl(canonical, pageUrl, normalizeOpts) {
16
16
  }
17
17
  return normalizeAuditUrl(resolve(dirname(pageUrl), raw), normalizeOpts);
18
18
  }
19
+ /** Extract the hostname from a URL string, or null if unparseable. */
20
+ function extractHost(url) {
21
+ try {
22
+ return new URL(url).hostname;
23
+ }
24
+ catch {
25
+ return null;
26
+ }
27
+ }
19
28
  export function canonicalConsistencyRule(pages, knownUrls, normalizeOpts) {
29
+ // ── Pass 1: collect out-of-scope findings per page ──────────────────────────
30
+ // We separate "out-of-scope" (canonical host ≠ page host, and not in knownUrls)
31
+ // from other findings so we can decide whether to collapse them.
20
32
  const findings = [];
33
+ // Map from canonical-target-host → array of (pageUrl, canonicalUrl) that had that host
34
+ const outOfScopeByTargetHost = new Map();
21
35
  for (const page of pages) {
22
36
  if (!page.canonical) {
23
37
  findings.push({
@@ -40,35 +54,137 @@ export function canonicalConsistencyRule(pages, knownUrls, normalizeOpts) {
40
54
  });
41
55
  continue;
42
56
  }
43
- if (canonicalUrl === page.url)
57
+ if (canonicalUrl === page.url) {
58
+ // Self-canonical: still check HTTP header conflict
59
+ if (page.httpMeta?.linkHeader) {
60
+ const linkCanonicalMatch = page.httpMeta.linkHeader.match(/<([^>]+)>;\s*rel="canonical"/i);
61
+ if (linkCanonicalMatch) {
62
+ const httpCanonical = normalizeAuditUrl(linkCanonicalMatch[1], normalizeOpts);
63
+ const htmlCanonical = resolveCanonicalUrl(page.canonical, page.url, normalizeOpts);
64
+ if (httpCanonical && htmlCanonical && httpCanonical !== htmlCanonical) {
65
+ findings.push({
66
+ ruleId: "tech/canonical-consistency",
67
+ severity: "error",
68
+ message: `${page.url} has conflicting canonical URLs: HTML says ${htmlCanonical}, HTTP Link header says ${httpCanonical}.`,
69
+ pageUrl: page.url,
70
+ relatedUrls: [htmlCanonical, httpCanonical],
71
+ fix: "Ensure the HTML <link rel='canonical'> and HTTP Link header agree on the canonical URL."
72
+ });
73
+ }
74
+ }
75
+ }
44
76
  continue;
45
- findings.push({
46
- ruleId: "tech/canonical-consistency",
47
- severity: knownUrls.has(canonicalUrl) ? "warning" : "info",
48
- message: knownUrls.has(canonicalUrl)
49
- ? `${page.url} canonicalizes to another crawled page (${canonicalUrl}).`
50
- : `${page.url} canonicalizes outside the crawl scope (${canonicalUrl}).`,
51
- pageUrl: page.url,
52
- relatedUrls: [canonicalUrl],
53
- fix: "Verify this canonical target is intentional."
54
- });
55
- // Check HTTP Link header for canonical
56
- if (page.httpMeta?.linkHeader) {
57
- const linkCanonicalMatch = page.httpMeta.linkHeader.match(/<([^>]+)>;\s*rel="canonical"/i);
58
- if (linkCanonicalMatch) {
59
- const httpCanonical = normalizeAuditUrl(linkCanonicalMatch[1], normalizeOpts);
60
- const htmlCanonical = page.canonical
61
- ? resolveCanonicalUrl(page.canonical, page.url, normalizeOpts)
62
- : null;
63
- if (httpCanonical && htmlCanonical && httpCanonical !== htmlCanonical) {
64
- findings.push({
65
- ruleId: "tech/canonical-consistency",
66
- severity: "error",
67
- message: `${page.url} has conflicting canonical URLs: HTML says ${htmlCanonical}, HTTP Link header says ${httpCanonical}.`,
68
- pageUrl: page.url,
69
- relatedUrls: [htmlCanonical, httpCanonical],
70
- fix: "Ensure the HTML <link rel='canonical'> and HTTP Link header agree on the canonical URL."
71
- });
77
+ }
78
+ // canonical differs from page.url
79
+ const pageHost = extractHost(page.url);
80
+ const canonicalHost = extractHost(canonicalUrl);
81
+ const isOutOfScope = !knownUrls.has(canonicalUrl);
82
+ const isCrossHost = pageHost !== null && canonicalHost !== null && canonicalHost !== pageHost;
83
+ if (isOutOfScope && isCrossHost) {
84
+ // Candidate for collapsing — defer into the bucket keyed by target host
85
+ const bucket = outOfScopeByTargetHost.get(canonicalHost) ?? [];
86
+ bucket.push({ pageUrl: page.url, canonicalUrl });
87
+ outOfScopeByTargetHost.set(canonicalHost, bucket);
88
+ }
89
+ else {
90
+ // Either within-scope (warning) or same-host out-of-scope — emit per-page
91
+ findings.push({
92
+ ruleId: "tech/canonical-consistency",
93
+ severity: knownUrls.has(canonicalUrl) ? "warning" : "info",
94
+ message: knownUrls.has(canonicalUrl)
95
+ ? `${page.url} canonicalizes to another crawled page (${canonicalUrl}).`
96
+ : `${page.url} canonicalizes outside the crawl scope (${canonicalUrl}).`,
97
+ pageUrl: page.url,
98
+ relatedUrls: [canonicalUrl],
99
+ fix: "Verify this canonical target is intentional."
100
+ });
101
+ // HTTP header conflict check (only when we haven't already decided to collapse)
102
+ if (page.httpMeta?.linkHeader) {
103
+ const linkCanonicalMatch = page.httpMeta.linkHeader.match(/<([^>]+)>;\s*rel="canonical"/i);
104
+ if (linkCanonicalMatch) {
105
+ const httpCanonical = normalizeAuditUrl(linkCanonicalMatch[1], normalizeOpts);
106
+ const htmlCanonical = resolveCanonicalUrl(page.canonical, page.url, normalizeOpts);
107
+ if (httpCanonical && htmlCanonical && httpCanonical !== htmlCanonical) {
108
+ findings.push({
109
+ ruleId: "tech/canonical-consistency",
110
+ severity: "error",
111
+ message: `${page.url} has conflicting canonical URLs: HTML says ${htmlCanonical}, HTTP Link header says ${httpCanonical}.`,
112
+ pageUrl: page.url,
113
+ relatedUrls: [htmlCanonical, httpCanonical],
114
+ fix: "Ensure the HTML <link rel='canonical'> and HTTP Link header agree on the canonical URL."
115
+ });
116
+ }
117
+ }
118
+ }
119
+ }
120
+ }
121
+ // ── Pass 2: collapse uniform out-of-scope buckets ───────────────────────────
122
+ // Strategy: if ALL pages point to the SAME alternate host (one bucket with all
123
+ // out-of-scope cross-host pages), emit ONE site-level info. If multiple target
124
+ // hosts exist (inconsistent), keep per-page findings.
125
+ const buckets = [...outOfScopeByTargetHost.entries()];
126
+ if (buckets.length === 0) {
127
+ // Nothing to collapse
128
+ }
129
+ else if (buckets.length === 1) {
130
+ // Every cross-host out-of-scope canonical goes to the same alternate host → collapse
131
+ const [targetHost, entries] = buckets[0];
132
+ const count = entries.length;
133
+ // Infer the crawled host from the first page in the bucket
134
+ const crawledHost = extractHost(entries[0].pageUrl) ?? "the crawled host";
135
+ // If there is only ONE page total pointing to the alternate host,
136
+ // still emit a per-page finding (no "site-level" implication with a single page).
137
+ if (count === 1) {
138
+ const { pageUrl, canonicalUrl } = entries[0];
139
+ findings.push({
140
+ ruleId: "tech/canonical-consistency",
141
+ severity: "info",
142
+ message: `${pageUrl} canonicalizes outside the crawl scope (${canonicalUrl}).`,
143
+ pageUrl,
144
+ relatedUrls: [canonicalUrl],
145
+ fix: "Verify this canonical target is intentional."
146
+ });
147
+ }
148
+ else {
149
+ findings.push({
150
+ ruleId: "tech/canonical-consistency",
151
+ severity: "info",
152
+ message: `${count} pages canonicalize to ${targetHost}, outside the crawled host ${crawledHost} — expected if you crawled a staging/preview origin.`,
153
+ relatedUrls: entries.map((e) => e.canonicalUrl).slice(0, 10),
154
+ fix: "If this site is live at the canonical host, the canonicals are correct. If not, verify the canonical URLs."
155
+ });
156
+ }
157
+ }
158
+ else {
159
+ // Multiple target hosts — inconsistent cross-host canonicals → per-page findings
160
+ for (const [, entries] of buckets) {
161
+ for (const { pageUrl, canonicalUrl } of entries) {
162
+ findings.push({
163
+ ruleId: "tech/canonical-consistency",
164
+ severity: "info",
165
+ message: `${pageUrl} canonicalizes outside the crawl scope (${canonicalUrl}).`,
166
+ pageUrl,
167
+ relatedUrls: [canonicalUrl],
168
+ fix: "Verify this canonical target is intentional."
169
+ });
170
+ // HTTP header conflict check for deferred pages
171
+ const pageDef = pages.find((p) => p.url === pageUrl);
172
+ if (pageDef?.httpMeta?.linkHeader) {
173
+ const linkCanonicalMatch = pageDef.httpMeta.linkHeader.match(/<([^>]+)>;\s*rel="canonical"/i);
174
+ if (linkCanonicalMatch) {
175
+ const httpCanonical = normalizeAuditUrl(linkCanonicalMatch[1], normalizeOpts);
176
+ const htmlCanonical = resolveCanonicalUrl(pageDef.canonical, pageDef.url, normalizeOpts);
177
+ if (httpCanonical && htmlCanonical && httpCanonical !== htmlCanonical) {
178
+ findings.push({
179
+ ruleId: "tech/canonical-consistency",
180
+ severity: "error",
181
+ message: `${pageUrl} has conflicting canonical URLs: HTML says ${htmlCanonical}, HTTP Link header says ${httpCanonical}.`,
182
+ pageUrl,
183
+ relatedUrls: [htmlCanonical, httpCanonical],
184
+ fix: "Ensure the HTML <link rel='canonical'> and HTTP Link header agree on the canonical URL."
185
+ });
186
+ }
187
+ }
72
188
  }
73
189
  }
74
190
  }
@@ -1 +1 @@
1
- {"version":3,"file":"canonical-consistency.js","sourceRoot":"","sources":["../../../src/rules/tech/canonical-consistency.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"canonical-consistency.js","sourceRoot":"","sources":["../../../src/rules/tech/canonical-consistency.ts"],"names":[],"mappings":"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"}
@@ -6,9 +6,14 @@ import type { ParsedPage, RuleResult } from "../../types.js";
6
6
  *
7
7
  * Required: og:title, og:description, og:image.
8
8
  *
9
- * The rule was referenced in the v0.4.x README without ever shipping. The
10
- * 2026-05-03 blind-spot audit surfaced it as a tier-1 gap; this is the
11
- * v0.5.2 fix.
9
+ * Severity gradation:
10
+ * - warning: og:title or og:description is missing (core social-card identity
11
+ * fields that affect how a link appears in feeds and AI summaries).
12
+ * - info: only og:image is missing (cosmetic — the card still has a title
13
+ * and description; the missing image is low-priority).
14
+ *
15
+ * Presence check: a field is considered MISSING when it is absent, empty, or
16
+ * whitespace-only (value is trimmed before evaluation).
12
17
  */
13
18
  export declare function ogCompletenessRule(pages: ParsedPage[]): RuleResult[];
14
19
  //# sourceMappingURL=og-completeness.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"og-completeness.d.ts","sourceRoot":"","sources":["../../../src/rules/tech/og-completeness.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE7D;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,UAAU,EAAE,CAiBpE"}
1
+ {"version":3,"file":"og-completeness.d.ts","sourceRoot":"","sources":["../../../src/rules/tech/og-completeness.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE7D;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,UAAU,EAAE,CAsBpE"}
@@ -5,25 +5,33 @@
5
5
  *
6
6
  * Required: og:title, og:description, og:image.
7
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.
8
+ * Severity gradation:
9
+ * - warning: og:title or og:description is missing (core social-card identity
10
+ * fields that affect how a link appears in feeds and AI summaries).
11
+ * - info: only og:image is missing (cosmetic — the card still has a title
12
+ * and description; the missing image is low-priority).
13
+ *
14
+ * Presence check: a field is considered MISSING when it is absent, empty, or
15
+ * whitespace-only (value is trimmed before evaluation).
11
16
  */
12
17
  export function ogCompletenessRule(pages) {
13
18
  const findings = [];
14
19
  for (const page of pages) {
15
20
  const missing = [];
16
- if (!page.og.title)
21
+ if (!page.og.title.trim())
17
22
  missing.push("og:title");
18
- if (!page.og.description)
23
+ if (!page.og.description.trim())
19
24
  missing.push("og:description");
20
- if (!page.og.image)
25
+ if (!page.og.image.trim())
21
26
  missing.push("og:image");
22
27
  if (missing.length === 0)
23
28
  continue;
29
+ const missingCore = missing.some((f) => f === "og:title" || f === "og:description");
30
+ const severity = missingCore ? "warning" : "info";
24
31
  findings.push({
25
32
  ruleId: "tech/og-completeness",
26
- severity: "warning",
33
+ severity,
34
+ confidence: missingCore ? "high" : "medium",
27
35
  message: `${page.url} is missing Open Graph tags: ${missing.join(", ")}.`,
28
36
  pageUrl: page.url,
29
37
  fix: `Add the missing meta tags inside <head>: ${missing.map((tag) => `<meta property="${tag}" content="...">`).join(" ")}.`,
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"og-completeness.js","sourceRoot":"","sources":["../../../src/rules/tech/og-completeness.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;GAeG;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,CAAC,IAAI,EAAE;YAAE,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACpD,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE;YAAE,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAChE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE;YAAE,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACpD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QAEnC,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,UAAU,IAAI,CAAC,KAAK,gBAAgB,CAAC,CAAC;QACpF,MAAM,QAAQ,GAA2B,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC;QAE1E,QAAQ,CAAC,IAAI,CAAC;YACZ,MAAM,EAAE,sBAAsB;YAC9B,QAAQ;YACR,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ;YAC3C,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"}
@@ -1,3 +1,15 @@
1
- import type { ParsedPage, RuleResult } from "../../types.js";
2
- export declare function sitemapCompletenessRule(pages: ParsedPage[], sitemapUrls: Set<string>): RuleResult[];
1
+ import type { NormalizeUrlOptions, ParsedPage, RuleResult } from "../../types.js";
2
+ export interface SitemapCompletenessOptions {
3
+ /**
4
+ * True when the audit ran on a sampled or link-discovery crawl.
5
+ * On sampled crawls it is normal to find pages not listed in the sitemap
6
+ * (they were discovered via links, not sitemap), so the aggregate
7
+ * "missing from sitemap" finding is demoted to `warning`.
8
+ * Wire this from the auditor's `isSampledAudit` flag.
9
+ */
10
+ sampled?: boolean;
11
+ /** URL normalization options that match what the auditor used when building page.url. */
12
+ normalizeUrlOptions?: NormalizeUrlOptions;
13
+ }
14
+ export declare function sitemapCompletenessRule(pages: ParsedPage[], sitemapUrls: Set<string>, options?: SitemapCompletenessOptions): RuleResult[];
3
15
  //# sourceMappingURL=sitemap-completeness.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sitemap-completeness.d.ts","sourceRoot":"","sources":["../../../src/rules/tech/sitemap-completeness.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE7D,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,UAAU,EAAE,EACnB,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,GACvB,UAAU,EAAE,CA0Dd"}
1
+ {"version":3,"file":"sitemap-completeness.d.ts","sourceRoot":"","sources":["../../../src/rules/tech/sitemap-completeness.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAElF,MAAM,WAAW,0BAA0B;IACzC;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yFAAyF;IACzF,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;CAC3C;AAED,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,UAAU,EAAE,EACnB,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,EACxB,OAAO,CAAC,EAAE,0BAA0B,GACnC,UAAU,EAAE,CA6Ed"}
@@ -1,19 +1,30 @@
1
- export function sitemapCompletenessRule(pages, sitemapUrls) {
1
+ import { normalizeAuditUrl } from "../../url-normalize.js";
2
+ export function sitemapCompletenessRule(pages, sitemapUrls, options) {
2
3
  if (sitemapUrls.size === 0)
3
4
  return [];
5
+ const { sampled = false, normalizeUrlOptions } = options ?? {};
6
+ // Normalize sitemap URLs to match the already-normalized page.url values.
7
+ // page.url is produced by normalizeAuditUrl (via parseHtmlPage → parser.ts),
8
+ // so comparing raw sitemap XML URLs against it produces false positives for
9
+ // trailing slashes, query strings, and hash fragments.
10
+ const normalizedSitemapUrls = new Set([...sitemapUrls].map((u) => normalizeAuditUrl(u, normalizeUrlOptions)));
4
11
  const findings = [];
5
- const missingFromSitemap = pages.filter((p) => !sitemapUrls.has(p.url));
12
+ // "missing from sitemap" demoted to `warning` because:
13
+ // • on a sampled crawl many pages were found via link discovery, not sitemap
14
+ // • even on a full crawl this is advisory (Google discovers via crawl too)
15
+ // The genuine hard-error signal is the 4xx/5xx sub-check below.
16
+ const missingFromSitemap = pages.filter((p) => !normalizedSitemapUrls.has(p.url));
6
17
  if (missingFromSitemap.length > 0) {
7
18
  findings.push({
8
19
  ruleId: "tech/sitemap-completeness",
9
- severity: "error",
20
+ severity: "warning",
10
21
  message: `${missingFromSitemap.length} crawlable page(s) not in sitemap.`,
11
22
  fix: "Add these pages to your sitemap.xml to ensure Google discovers them.",
12
23
  relatedUrls: missingFromSitemap.map((p) => p.url).sort()
13
24
  });
14
25
  }
15
26
  for (const page of pages) {
16
- if (!page.httpMeta || !sitemapUrls.has(page.url))
27
+ if (!page.httpMeta || !normalizedSitemapUrls.has(page.url))
17
28
  continue;
18
29
  if (page.httpMeta.statusCode >= 400) {
19
30
  findings.push({
@@ -35,8 +46,13 @@ export function sitemapCompletenessRule(pages, sitemapUrls) {
35
46
  });
36
47
  }
37
48
  }
49
+ // NOTE: the noindex-in-sitemap sub-check below duplicates `robots-noindex-conflict`
50
+ // and `canonical-noindex-conflict`. It is intentionally kept here because
51
+ // sitemap-completeness is the only rule that sees the sitemap set directly and
52
+ // can phrase the finding as "you're signalling two contradictory things to
53
+ // Google via the sitemap". Removing it would lose the sitemap-specific framing.
38
54
  for (const page of pages) {
39
- if (!sitemapUrls.has(page.url))
55
+ if (!normalizedSitemapUrls.has(page.url))
40
56
  continue;
41
57
  const hasNoindex = /noindex/i.test(page.robotsMeta) ||
42
58
  (page.httpMeta?.xRobotsTag != null && /noindex/i.test(page.httpMeta.xRobotsTag));
@@ -1 +1 @@
1
- {"version":3,"file":"sitemap-completeness.js","sourceRoot":"","sources":["../../../src/rules/tech/sitemap-completeness.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,uBAAuB,CACrC,KAAmB,EACnB,WAAwB;IAExB,IAAI,WAAW,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEtC,MAAM,QAAQ,GAAiB,EAAE,CAAC;IAElC,MAAM,kBAAkB,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACxE,IAAI,kBAAkB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClC,QAAQ,CAAC,IAAI,CAAC;YACZ,MAAM,EAAE,2BAA2B;YACnC,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,GAAG,kBAAkB,CAAC,MAAM,oCAAoC;YACzE,GAAG,EAAE,sEAAsE;YAC3E,WAAW,EAAE,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE;SACzD,CAAC,CAAC;IACL,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,SAAS;QAE3D,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,IAAI,GAAG,EAAE,CAAC;YACpC,QAAQ,CAAC,IAAI,CAAC;gBACZ,MAAM,EAAE,2BAA2B;gBACnC,QAAQ,EAAE,OAAO;gBACjB,OAAO,EAAE,eAAe,IAAI,CAAC,GAAG,iBAAiB,IAAI,CAAC,QAAQ,CAAC,UAAU,GAAG;gBAC5E,OAAO,EAAE,IAAI,CAAC,GAAG;gBACjB,GAAG,EAAE,sEAAsE;aAC5E,CAAC,CAAC;QACL,CAAC;QAED,IAAI,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC;YAClF,QAAQ,CAAC,IAAI,CAAC;gBACZ,MAAM,EAAE,2BAA2B;gBACnC,QAAQ,EAAE,SAAS;gBACnB,OAAO,EAAE,eAAe,IAAI,CAAC,GAAG,iBAAiB,IAAI,CAAC,QAAQ,CAAC,QAAQ,GAAG;gBAC1E,OAAO,EAAE,IAAI,CAAC,GAAG;gBACjB,WAAW,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBACrC,GAAG,EAAE,4CAA4C,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE;aAC1E,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,SAAS;QACzC,MAAM,UAAU,GACd,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC;YAChC,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,IAAI,IAAI,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;QACnF,IAAI,UAAU,EAAE,CAAC;YACf,QAAQ,CAAC,IAAI,CAAC;gBACZ,MAAM,EAAE,2BAA2B;gBACnC,QAAQ,EAAE,OAAO;gBACjB,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,iDAAiD;gBACrE,OAAO,EAAE,IAAI,CAAC,GAAG;gBACjB,GAAG,EAAE,oEAAoE;aAC1E,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
1
+ {"version":3,"file":"sitemap-completeness.js","sourceRoot":"","sources":["../../../src/rules/tech/sitemap-completeness.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,14 @@
1
1
  import type { ParsedPage, RuleResult } from "../../types.js";
2
+ /**
3
+ * Confidence model for soft-404 detection:
4
+ *
5
+ * Signal A — thin body (wordCount < THIN_BODY_THRESHOLD)
6
+ * Signal B — soft-404 pattern in title, h1, or opening content
7
+ *
8
+ * A AND B → warning / high (strong: both signals agree)
9
+ * B only → info / low (weak: substantive body, pattern might be
10
+ * editorial, e.g. "How we handle 404 errors")
11
+ * A only → no finding (stub/coming-soon pages are legit)
12
+ */
2
13
  export declare function soft404Rule(pages: ParsedPage[]): RuleResult[];
3
14
  //# sourceMappingURL=soft-404.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"soft-404.d.ts","sourceRoot":"","sources":["../../../src/rules/tech/soft-404.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAI7D,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,UAAU,EAAE,CAsB7D"}
1
+ {"version":3,"file":"soft-404.d.ts","sourceRoot":"","sources":["../../../src/rules/tech/soft-404.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAe7D;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,UAAU,EAAE,CA+C7D"}
@@ -1,4 +1,23 @@
1
- const SOFT_404_PATTERNS = /\b(not\s*found|404|page\s*missing|does\s*not\s*exist|no\s*longer\s*available)\b/i;
1
+ // ponytail: thin-body threshold. Nav/chrome inflate raw word counts; 50 is
2
+ // already generous — pages that are genuinely soft-404s rarely exceed it.
3
+ const THIN_BODY_THRESHOLD = 50;
4
+ // ponytail: how many words of contentText to scan for a pattern signal.
5
+ // Scanning the full body risks matching editorial mentions of "404" deep in an
6
+ // article; the first 120 words (roughly two paragraphs) is enough to catch
7
+ // real error templates.
8
+ const CONTENT_SCAN_WORDS = 120;
9
+ const SOFT_404_PATTERNS = /\b(not\s*found|404|page\s*missing|does\s*not\s*exist|no\s*longer\s*available|page\s*does\s*not\s*exist)\b/i;
10
+ /**
11
+ * Confidence model for soft-404 detection:
12
+ *
13
+ * Signal A — thin body (wordCount < THIN_BODY_THRESHOLD)
14
+ * Signal B — soft-404 pattern in title, h1, or opening content
15
+ *
16
+ * A AND B → warning / high (strong: both signals agree)
17
+ * B only → info / low (weak: substantive body, pattern might be
18
+ * editorial, e.g. "How we handle 404 errors")
19
+ * A only → no finding (stub/coming-soon pages are legit)
20
+ */
2
21
  export function soft404Rule(pages) {
3
22
  const findings = [];
4
23
  for (const page of pages) {
@@ -7,17 +26,40 @@ export function soft404Rule(pages) {
7
26
  if (page.httpMeta.statusCode !== 200)
8
27
  continue;
9
28
  const wordCount = page.contentText.split(/\s+/).filter(Boolean).length;
10
- if (wordCount >= 50)
11
- continue;
12
- if (SOFT_404_PATTERNS.test(page.title)) {
29
+ const signalA = wordCount < THIN_BODY_THRESHOLD;
30
+ // Signal B: check title, first h1, and opening words of contentText
31
+ const h1 = page.headings.h1[0] ?? "";
32
+ const openingText = page.contentText
33
+ .split(/\s+/)
34
+ .filter(Boolean)
35
+ .slice(0, CONTENT_SCAN_WORDS)
36
+ .join(" ");
37
+ const signalB = SOFT_404_PATTERNS.test(page.title) ||
38
+ SOFT_404_PATTERNS.test(h1) ||
39
+ SOFT_404_PATTERNS.test(openingText);
40
+ if (signalA && signalB) {
41
+ // Strong signal: thin body AND pattern match → warning/high
13
42
  findings.push({
14
43
  ruleId: "tech/soft-404",
15
- severity: "error",
44
+ severity: "warning",
45
+ confidence: "high",
16
46
  message: `${page.url} returns HTTP 200 but appears to be an error page (title: "${page.title}", ${wordCount} words).`,
17
47
  pageUrl: page.url,
18
48
  fix: "Return a proper HTTP 404 status code for error pages instead of 200."
19
49
  });
20
50
  }
51
+ else if (signalB) {
52
+ // Weak signal: pattern match only, substantive body → info/low
53
+ findings.push({
54
+ ruleId: "tech/soft-404",
55
+ severity: "info",
56
+ confidence: "low",
57
+ message: `${page.url} returns HTTP 200 and mentions error-page language in its title or headings (title: "${page.title}", ${wordCount} words). Verify this is intentional content, not a misconfigured soft-404.`,
58
+ pageUrl: page.url,
59
+ fix: "If this is an error page, return HTTP 404. If the content is intentional, consider renaming the page title or heading to avoid soft-404 signals."
60
+ });
61
+ }
62
+ // signalA only (thin body, no pattern) → no finding; stub pages are legit
21
63
  }
22
64
  return findings;
23
65
  }
@@ -1 +1 @@
1
- {"version":3,"file":"soft-404.js","sourceRoot":"","sources":["../../../src/rules/tech/soft-404.ts"],"names":[],"mappings":"AAEA,MAAM,iBAAiB,GAAG,kFAAkF,CAAC;AAE7G,MAAM,UAAU,WAAW,CAAC,KAAmB;IAC7C,MAAM,QAAQ,GAAiB,EAAE,CAAC;IAElC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE,SAAS;QAC7B,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,KAAK,GAAG;YAAE,SAAS;QAE/C,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;QACvE,IAAI,SAAS,IAAI,EAAE;YAAE,SAAS;QAE9B,IAAI,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACvC,QAAQ,CAAC,IAAI,CAAC;gBACZ,MAAM,EAAE,eAAe;gBACvB,QAAQ,EAAE,OAAO;gBACjB,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,8DAA8D,IAAI,CAAC,KAAK,MAAM,SAAS,UAAU;gBACrH,OAAO,EAAE,IAAI,CAAC,GAAG;gBACjB,GAAG,EAAE,sEAAsE;aAC5E,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
1
+ {"version":3,"file":"soft-404.js","sourceRoot":"","sources":["../../../src/rules/tech/soft-404.ts"],"names":[],"mappings":"AAEA,2EAA2E;AAC3E,0EAA0E;AAC1E,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAE/B,wEAAwE;AACxE,+EAA+E;AAC/E,2EAA2E;AAC3E,wBAAwB;AACxB,MAAM,kBAAkB,GAAG,GAAG,CAAC;AAE/B,MAAM,iBAAiB,GACrB,4GAA4G,CAAC;AAE/G;;;;;;;;;;GAUG;AACH,MAAM,UAAU,WAAW,CAAC,KAAmB;IAC7C,MAAM,QAAQ,GAAiB,EAAE,CAAC;IAElC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE,SAAS;QAC7B,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,KAAK,GAAG;YAAE,SAAS;QAE/C,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;QACvE,MAAM,OAAO,GAAG,SAAS,GAAG,mBAAmB,CAAC;QAEhD,oEAAoE;QACpE,MAAM,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACrC,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW;aACjC,KAAK,CAAC,KAAK,CAAC;aACZ,MAAM,CAAC,OAAO,CAAC;aACf,KAAK,CAAC,CAAC,EAAE,kBAAkB,CAAC;aAC5B,IAAI,CAAC,GAAG,CAAC,CAAC;QACb,MAAM,OAAO,GACX,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;YAClC,iBAAiB,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1B,iBAAiB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAEtC,IAAI,OAAO,IAAI,OAAO,EAAE,CAAC;YACvB,4DAA4D;YAC5D,QAAQ,CAAC,IAAI,CAAC;gBACZ,MAAM,EAAE,eAAe;gBACvB,QAAQ,EAAE,SAAS;gBACnB,UAAU,EAAE,MAAM;gBAClB,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,8DAA8D,IAAI,CAAC,KAAK,MAAM,SAAS,UAAU;gBACrH,OAAO,EAAE,IAAI,CAAC,GAAG;gBACjB,GAAG,EAAE,sEAAsE;aAC5E,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,OAAO,EAAE,CAAC;YACnB,+DAA+D;YAC/D,QAAQ,CAAC,IAAI,CAAC;gBACZ,MAAM,EAAE,eAAe;gBACvB,QAAQ,EAAE,MAAM;gBAChB,UAAU,EAAE,KAAK;gBACjB,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,wFAAwF,IAAI,CAAC,KAAK,MAAM,SAAS,4EAA4E;gBACjN,OAAO,EAAE,IAAI,CAAC,GAAG;gBACjB,GAAG,EAAE,kJAAkJ;aACxJ,CAAC,CAAC;QACL,CAAC;QACD,0EAA0E;IAC5E,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -48,4 +48,5 @@ export declare function buildUrlToTemplateMap(candidates: TemplateCandidate[]):
48
48
  * Requires ≥ 2 QUALIFYING (non-longtail) templates per §15.3.
49
49
  */
50
50
  export declare function shouldActivateTemplateScoring(candidates: TemplateCandidate[]): boolean;
51
+ export declare function normalizePathToTemplate(pathname: string): string;
51
52
  //# sourceMappingURL=template-detection.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"template-detection.d.ts","sourceRoot":"","sources":["../src/template-detection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAUH,oDAAoD;AACpD,eAAO,MAAM,kBAAkB,cAAc,CAAC;AAE9C,yCAAyC;AACzC,MAAM,WAAW,iBAAiB;IAChC,qDAAqD;IACrD,SAAS,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,qCAAqC;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,iBAAiB,EAAE,CAmDnE;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,iBAAiB,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAQ1F;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,UAAU,EAAE,iBAAiB,EAAE,GAAG,OAAO,CAGtF"}
1
+ {"version":3,"file":"template-detection.d.ts","sourceRoot":"","sources":["../src/template-detection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAUH,oDAAoD;AACpD,eAAO,MAAM,kBAAkB,cAAc,CAAC;AAE9C,yCAAyC;AACzC,MAAM,WAAW,iBAAiB;IAChC,qDAAqD;IACrD,SAAS,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,qCAAqC;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,iBAAiB,EAAE,CAmDnE;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,iBAAiB,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAQ1F;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,UAAU,EAAE,iBAAiB,EAAE,GAAG,OAAO,CAGtF;AAoBD,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAmBhE"}
@@ -113,7 +113,7 @@ function urlToNormalizedTemplate(url) {
113
113
  return null;
114
114
  }
115
115
  }
116
- function normalizePathToTemplate(pathname) {
116
+ export function normalizePathToTemplate(pathname) {
117
117
  let p = pathname || "/";
118
118
  if (p.length > 1 && p.endsWith("/"))
119
119
  p = p.slice(0, -1);
@@ -1 +1 @@
1
- {"version":3,"file":"template-detection.js","sourceRoot":"","sources":["../src/template-detection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"template-detection.js","sourceRoot":"","sources":["../src/template-detection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE3D,yEAAyE;AACzE,MAAM,iBAAiB,GAAG,IAAI,CAAC;AAE/B,kDAAkD;AAClD,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAE5B,oDAAoD;AACpD,MAAM,CAAC,MAAM,kBAAkB,GAAG,WAAW,CAAC;AAc9C;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,eAAe,CAAC,IAAc;IAC5C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEjC,MAAM,QAAQ,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC;IAE1B,iCAAiC;IACjC,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAChC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,iBAAiB,IAAI,CAAC,CAAC,KAAK,IAAI,iBAAiB,CACpE,CAAC;IAEF,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEvC,qEAAqE;IACrE,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;IAExE,4EAA4E;IAC5E,sBAAsB;IACtB,MAAM,eAAe,GAAG,IAAI,GAAG,EAAoB,CAAC;IACpD,KAAK,MAAM,GAAG,IAAI,oBAAoB,EAAE,CAAC;QACvC,eAAe,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAC/B,CAAC;IACD,MAAM,YAAY,GAAa,EAAE,CAAC;IAElC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,GAAG,GAAG,uBAAuB,CAAC,GAAG,CAAC,CAAC;QACzC,IAAI,GAAG,KAAK,IAAI,IAAI,oBAAoB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAClD,eAAe,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACtC,CAAC;aAAM,CAAC;YACN,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAED,MAAM,UAAU,GAAwB,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC7D,SAAS,EAAE,CAAC,CAAC,QAAQ;QACrB,IAAI,EAAE,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE;QAC3C,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,KAAK,EAAE,CAAC,CAAC,KAAK;KACf,CAAC,CAAC,CAAC;IAEJ,yEAAyE;IACzE,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,UAAU,CAAC,IAAI,CAAC;YACd,SAAS,EAAE,kBAAkB;YAC7B,IAAI,EAAE,YAAY;YAClB,KAAK,EAAE,YAAY,CAAC,MAAM;YAC1B,KAAK,EAAE,YAAY,CAAC,MAAM,GAAG,KAAK;SACnC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,UAA+B;IACnE,MAAM,GAAG,GAAG,IAAI,GAAG,EAAkB,CAAC;IACtC,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,KAAK,MAAM,GAAG,IAAI,SAAS,CAAC,IAAI,EAAE,CAAC;YACjC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,6BAA6B,CAAC,UAA+B;IAC3E,MAAM,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,kBAAkB,CAAC,CAAC;IAChF,OAAO,UAAU,CAAC,MAAM,IAAI,CAAC,CAAC;AAChC,CAAC;AAED,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E,oGAAoG;AACpG,SAAS,uBAAuB,CAAC,GAAW;IAC1C,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QACvB,OAAO,uBAAuB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9C,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7C,OAAO,uBAAuB,CAAC,IAAI,CAAC,CAAC;QACvC,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,QAAgB;IACtD,IAAI,CAAC,GAAG,QAAQ,IAAI,GAAG,CAAC;IACxB,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACxD,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC;IAEpC,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACvC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC;QAC3E,OAAO,GAAG,CAAC;IACb,CAAC;IAED,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;QAC/B,IAAI,GAAG,KAAK,EAAE;YAAE,OAAO,EAAE,CAAC;QAC1B,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;QACnC,IAAI,4BAA4B,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,OAAO,OAAO,CAAC;QAC3D,IAAI,GAAG,CAAC,MAAM,IAAI,EAAE,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,OAAO,OAAO,CAAC;QAC7D,OAAO,GAAG,CAAC;IACb,CAAC,CAAC,CAAC;IAEH,OAAO,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC7B,CAAC"}
package/dist/types.d.ts CHANGED
@@ -381,6 +381,11 @@ export interface AuditSummary {
381
381
  * Present only when `truncated` is true.
382
382
  */
383
383
  truncatedKind?: "backpressure" | "coverage";
384
+ /** Resolved domain authority used to moderate the verdict (0–100), with sources. Absent when unavailable. */
385
+ authority?: {
386
+ score: number;
387
+ domain: string;
388
+ };
384
389
  }
385
390
  /**
386
391
  * v0.5+ change-driven monitoring summary surfaced on AuditSummary so dashboards
@@ -438,7 +443,11 @@ export interface AuditOptions {
438
443
  publicationVelocityMaxPerDayCorpusFraction?: number;
439
444
  boilerplateMaxRatio?: number;
440
445
  templateDiversityMinUniqueRatio?: number;
441
- uniqueValueMinWords?: number;
446
+ /** content/unique-value density floors. Default { passBelow: 0.20, errorBelow: 0.12 }. */
447
+ uniqueValueDensity?: {
448
+ passBelow: number;
449
+ errorBelow: number;
450
+ };
442
451
  metaUniquenessMinJaccard?: number;
443
452
  linkDepthMaxClicks?: number;
444
453
  templateCoverageMinPages?: number;
@@ -527,6 +536,8 @@ export interface AuditOptions {
527
536
  pattern: string;
528
537
  flags?: string;
529
538
  }>;
539
+ /** Auto-derive entity-mask patterns from the corpus (default true). Set false to A/B compare. */
540
+ autoEntityMask?: boolean;
530
541
  /** Source data records for data-binding verification. */
531
542
  dataSource?: DataSourceOptions;
532
543
  /** HTTP cache configuration. When omitted, caching is disabled. */
@@ -566,6 +577,10 @@ export interface AuditOptions {
566
577
  * Omitted = no shift (back-compat).
567
578
  */
568
579
  authorityScore?: number;
580
+ /** OpenPageRank API key; enables live authority lookup when authorityScore is not supplied. */
581
+ openPageRankApiKey?: string;
582
+ /** Custom authority provider (overrides the default OPR/CC composite). For tests + offline corpora. */
583
+ authorityProvider?: import("./algorithms/authority/provider.js").AuthorityProvider;
569
584
  /** Run state persistence. When omitted, no state is written. */
570
585
  state?: StateOptions;
571
586
  /**