@houtini/seo-audit-console 0.8.0 → 0.9.1

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 (207) hide show
  1. package/README.md +308 -306
  2. package/dist/audit/checks.d.ts +0 -7
  3. package/dist/audit/checks.d.ts.map +1 -1
  4. package/dist/audit/checks.js +130 -242
  5. package/dist/audit/checks.js.map +1 -1
  6. package/dist/audit/drift.d.ts +0 -12
  7. package/dist/audit/drift.d.ts.map +1 -1
  8. package/dist/audit/drift.js +0 -15
  9. package/dist/audit/drift.js.map +1 -1
  10. package/dist/audit/engine.d.ts +0 -3
  11. package/dist/audit/engine.d.ts.map +1 -1
  12. package/dist/audit/engine.js +7 -42
  13. package/dist/audit/engine.js.map +1 -1
  14. package/dist/audit/keywordList.d.ts +0 -14
  15. package/dist/audit/keywordList.d.ts.map +1 -1
  16. package/dist/audit/keywordList.js +0 -5
  17. package/dist/audit/keywordList.js.map +1 -1
  18. package/dist/audit/opportunities.js +3 -26
  19. package/dist/audit/opportunities.js.map +1 -1
  20. package/dist/audit/recon.d.ts +0 -31
  21. package/dist/audit/recon.d.ts.map +1 -1
  22. package/dist/audit/recon.js +0 -27
  23. package/dist/audit/recon.js.map +1 -1
  24. package/dist/audit/report.d.ts +0 -4
  25. package/dist/audit/report.d.ts.map +1 -1
  26. package/dist/audit/report.js +0 -7
  27. package/dist/audit/report.js.map +1 -1
  28. package/dist/audit/schema-validate.d.ts +0 -26
  29. package/dist/audit/schema-validate.d.ts.map +1 -1
  30. package/dist/audit/schema-validate.js +8 -64
  31. package/dist/audit/schema-validate.js.map +1 -1
  32. package/dist/audit/templates.d.ts +0 -24
  33. package/dist/audit/templates.d.ts.map +1 -1
  34. package/dist/audit/templates.js +1 -21
  35. package/dist/audit/templates.js.map +1 -1
  36. package/dist/audit/topicGaps.d.ts +0 -1
  37. package/dist/audit/topicGaps.d.ts.map +1 -1
  38. package/dist/audit/topicGaps.js +3 -24
  39. package/dist/audit/topicGaps.js.map +1 -1
  40. package/dist/core/AuditDatabase.d.ts +0 -14
  41. package/dist/core/AuditDatabase.d.ts.map +1 -1
  42. package/dist/core/AuditDatabase.js +8 -61
  43. package/dist/core/AuditDatabase.js.map +1 -1
  44. package/dist/core/Backlinks.d.ts +0 -7
  45. package/dist/core/Backlinks.d.ts.map +1 -1
  46. package/dist/core/Backlinks.js +1 -23
  47. package/dist/core/Backlinks.js.map +1 -1
  48. package/dist/core/Crawler.d.ts +0 -1
  49. package/dist/core/Crawler.d.ts.map +1 -1
  50. package/dist/core/Crawler.js +24 -119
  51. package/dist/core/Crawler.js.map +1 -1
  52. package/dist/core/DataForSeoClient.d.ts +0 -74
  53. package/dist/core/DataForSeoClient.d.ts.map +1 -1
  54. package/dist/core/DataForSeoClient.js +1 -72
  55. package/dist/core/DataForSeoClient.js.map +1 -1
  56. package/dist/core/Entities.d.ts +0 -6
  57. package/dist/core/Entities.d.ts.map +1 -1
  58. package/dist/core/Entities.js +0 -8
  59. package/dist/core/Entities.js.map +1 -1
  60. package/dist/core/FirecrawlClient.d.ts +0 -18
  61. package/dist/core/FirecrawlClient.d.ts.map +1 -1
  62. package/dist/core/FirecrawlClient.js +1 -13
  63. package/dist/core/FirecrawlClient.js.map +1 -1
  64. package/dist/core/GscClient.d.ts +0 -7
  65. package/dist/core/GscClient.d.ts.map +1 -1
  66. package/dist/core/GscClient.js +1 -10
  67. package/dist/core/GscClient.js.map +1 -1
  68. package/dist/core/GscSync.d.ts +0 -4
  69. package/dist/core/GscSync.d.ts.map +1 -1
  70. package/dist/core/GscSync.js +2 -33
  71. package/dist/core/GscSync.js.map +1 -1
  72. package/dist/core/JobManager.d.ts +0 -5
  73. package/dist/core/JobManager.d.ts.map +1 -1
  74. package/dist/core/JobManager.js +0 -8
  75. package/dist/core/JobManager.js.map +1 -1
  76. package/dist/core/LinkIntersect.d.ts +0 -1
  77. package/dist/core/LinkIntersect.d.ts.map +1 -1
  78. package/dist/core/LinkIntersect.js +1 -37
  79. package/dist/core/LinkIntersect.js.map +1 -1
  80. package/dist/core/MajesticClient.d.ts +0 -26
  81. package/dist/core/MajesticClient.d.ts.map +1 -1
  82. package/dist/core/MajesticClient.js +0 -15
  83. package/dist/core/MajesticClient.js.map +1 -1
  84. package/dist/core/RankTracker.d.ts +0 -4
  85. package/dist/core/RankTracker.d.ts.map +1 -1
  86. package/dist/core/RankTracker.js +0 -9
  87. package/dist/core/RankTracker.js.map +1 -1
  88. package/dist/core/Refresh.d.ts +0 -6
  89. package/dist/core/Refresh.d.ts.map +1 -1
  90. package/dist/core/Refresh.js +0 -6
  91. package/dist/core/Refresh.js.map +1 -1
  92. package/dist/core/SupadataClient.d.ts +0 -11
  93. package/dist/core/SupadataClient.d.ts.map +1 -1
  94. package/dist/core/SupadataClient.js +0 -5
  95. package/dist/core/SupadataClient.js.map +1 -1
  96. package/dist/core/UrlInspector.d.ts +0 -5
  97. package/dist/core/UrlInspector.d.ts.map +1 -1
  98. package/dist/core/UrlInspector.js +0 -6
  99. package/dist/core/UrlInspector.js.map +1 -1
  100. package/dist/core/WikidataClient.d.ts +0 -2
  101. package/dist/core/WikidataClient.d.ts.map +1 -1
  102. package/dist/core/WikidataClient.js +0 -7
  103. package/dist/core/WikidataClient.js.map +1 -1
  104. package/dist/core/agentReadiness.d.ts +0 -9
  105. package/dist/core/agentReadiness.d.ts.map +1 -1
  106. package/dist/core/agentReadiness.js +0 -13
  107. package/dist/core/agentReadiness.js.map +1 -1
  108. package/dist/core/ctrModel.js +0 -3
  109. package/dist/core/ctrModel.js.map +1 -1
  110. package/dist/core/dashboardData.d.ts +0 -1
  111. package/dist/core/dashboardData.d.ts.map +1 -1
  112. package/dist/core/dashboardData.js +13 -95
  113. package/dist/core/dashboardData.js.map +1 -1
  114. package/dist/core/dataStorage.d.ts +0 -2
  115. package/dist/core/dataStorage.d.ts.map +1 -1
  116. package/dist/core/dataStorage.js +6 -19
  117. package/dist/core/dataStorage.js.map +1 -1
  118. package/dist/core/draftBrief.d.ts +0 -7
  119. package/dist/core/draftBrief.d.ts.map +1 -1
  120. package/dist/core/draftBrief.js +0 -10
  121. package/dist/core/draftBrief.js.map +1 -1
  122. package/dist/core/extract.d.ts +0 -11
  123. package/dist/core/extract.d.ts.map +1 -1
  124. package/dist/core/extract.js +1 -38
  125. package/dist/core/extract.js.map +1 -1
  126. package/dist/core/googleNews.d.ts +0 -11
  127. package/dist/core/googleNews.d.ts.map +1 -1
  128. package/dist/core/googleNews.js +0 -7
  129. package/dist/core/googleNews.js.map +1 -1
  130. package/dist/core/gscFreshness.d.ts +0 -10
  131. package/dist/core/gscFreshness.d.ts.map +1 -1
  132. package/dist/core/gscFreshness.js +0 -11
  133. package/dist/core/gscFreshness.js.map +1 -1
  134. package/dist/core/linkGraph.d.ts +0 -15
  135. package/dist/core/linkGraph.d.ts.map +1 -1
  136. package/dist/core/linkGraph.js +1 -23
  137. package/dist/core/linkGraph.js.map +1 -1
  138. package/dist/core/marketSizing.d.ts +0 -13
  139. package/dist/core/marketSizing.d.ts.map +1 -1
  140. package/dist/core/marketSizing.js +1 -1
  141. package/dist/core/marketSizing.js.map +1 -1
  142. package/dist/core/passageScore.d.ts +0 -13
  143. package/dist/core/passageScore.d.ts.map +1 -1
  144. package/dist/core/passageScore.js +1 -16
  145. package/dist/core/passageScore.js.map +1 -1
  146. package/dist/core/paths.d.ts +0 -3
  147. package/dist/core/paths.d.ts.map +1 -1
  148. package/dist/core/paths.js +0 -0
  149. package/dist/core/paths.js.map +1 -1
  150. package/dist/core/queryData.d.ts +0 -1
  151. package/dist/core/queryData.d.ts.map +1 -1
  152. package/dist/core/queryData.js +0 -18
  153. package/dist/core/queryData.js.map +1 -1
  154. package/dist/core/reconFetch.d.ts +0 -6
  155. package/dist/core/reconFetch.d.ts.map +1 -1
  156. package/dist/core/reconFetch.js +1 -4
  157. package/dist/core/reconFetch.js.map +1 -1
  158. package/dist/core/reconResearch.d.ts +0 -20
  159. package/dist/core/reconResearch.d.ts.map +1 -1
  160. package/dist/core/reconResearch.js +1 -16
  161. package/dist/core/reconResearch.js.map +1 -1
  162. package/dist/core/reranker.d.ts +0 -2
  163. package/dist/core/reranker.d.ts.map +1 -1
  164. package/dist/core/reranker.js +1 -11
  165. package/dist/core/reranker.js.map +1 -1
  166. package/dist/core/robots.d.ts +0 -6
  167. package/dist/core/robots.d.ts.map +1 -1
  168. package/dist/core/robots.js +1 -7
  169. package/dist/core/robots.js.map +1 -1
  170. package/dist/core/serpFootprint.d.ts +0 -12
  171. package/dist/core/serpFootprint.d.ts.map +1 -1
  172. package/dist/core/serpFootprint.js +0 -1
  173. package/dist/core/serpFootprint.js.map +1 -1
  174. package/dist/core/serpRecon.d.ts +0 -14
  175. package/dist/core/serpRecon.d.ts.map +1 -1
  176. package/dist/core/serpRecon.js +1 -13
  177. package/dist/core/serpRecon.js.map +1 -1
  178. package/dist/core/sitemap.js +3 -16
  179. package/dist/core/sitemap.js.map +1 -1
  180. package/dist/core/sql.d.ts +0 -4
  181. package/dist/core/sql.d.ts.map +1 -1
  182. package/dist/core/sql.js +0 -4
  183. package/dist/core/sql.js.map +1 -1
  184. package/dist/core/url-key.d.ts +0 -31
  185. package/dist/core/url-key.d.ts.map +1 -1
  186. package/dist/core/url-key.js +1 -38
  187. package/dist/core/url-key.js.map +1 -1
  188. package/dist/core/webHandlers.d.ts +0 -6
  189. package/dist/core/webHandlers.d.ts.map +1 -1
  190. package/dist/core/webHandlers.js +2 -5
  191. package/dist/core/webHandlers.js.map +1 -1
  192. package/dist/core/webServer.d.ts +0 -16
  193. package/dist/core/webServer.d.ts.map +1 -1
  194. package/dist/core/webServer.js +3 -17
  195. package/dist/core/webServer.js.map +1 -1
  196. package/dist/dashboard.js +0 -23
  197. package/dist/dashboard.js.map +1 -1
  198. package/dist/generators/index.d.ts +0 -7
  199. package/dist/generators/index.d.ts.map +1 -1
  200. package/dist/generators/index.js +1 -26
  201. package/dist/generators/index.js.map +1 -1
  202. package/dist/index.js +0 -2
  203. package/dist/index.js.map +1 -1
  204. package/dist/server.js +16 -166
  205. package/dist/server.js.map +1 -1
  206. package/package.json +2 -2
  207. package/server.json +2 -2
@@ -6,25 +6,18 @@ import { gscFreshness } from '../core/gscFreshness.js';
6
6
  import { brandToken } from '../core/url-key.js';
7
7
  import { snapshotCrawl } from './drift.js';
8
8
  import { CHECKS, expectedCtr } from './checks.js';
9
- const MAX_PER_CHECK = 500; // bound findings/check so a huge site can't balloon the table
10
- // ── 6e priority model: Priority = (T × Y × C) / E — "expected clicks per dev-hour" ──
11
- // Y (yield): expected % traffic uplift from the fix. Default derived from category×severity;
12
- // a check can override via yieldCoef. Indexation/crawlability fixes recover most traffic;
13
- // on-page tweaks are incremental; low-severity = cosmetic.
9
+ const MAX_PER_CHECK = 500;
14
10
  const YIELD_BY_CATEGORY = {
15
11
  indexation: 0.8, crawlability: 0.7, merged: 0.4, security: 0.5,
16
12
  schema: 0.3, content: 0.25, onpage: 0.2, performance: 0.5, 'war-stories': 0.5,
17
13
  };
18
- // Low/info are cosmetic — near-zero real traffic uplift, so they don't ride a high-traffic page up.
19
14
  const SEVERITY_YIELD_ADJ = { crit: 1.1, high: 1, med: 0.7, low: 0.15, info: 0.05 };
20
15
  function yieldOf(chk) {
21
16
  if (chk.yieldCoef != null)
22
17
  return Math.max(0, Math.min(1, chk.yieldCoef));
23
18
  return Math.min(1, (YIELD_BY_CATEGORY[chk.category] ?? 0.25) * SEVERITY_YIELD_ADJ[chk.severity]);
24
19
  }
25
- // Site-wide (null-url) findings: stake = a severity-scaled fraction of total site clicks.
26
20
  const SITEWIDE_FRACTION = { crit: 0.25, high: 0.12, med: 0.04, low: 0.01, info: 0.005 };
27
- // Floor so a high-severity finding on a zero-GSC page (e.g. backlinks-to-404) never scores 0.
28
21
  const SEVERITY_FLOOR = { crit: 5, high: 2, med: 0.5, low: 0.1, info: 0.05 };
29
22
  export function listChecks() {
30
23
  return CHECKS.map(({ run: _run, ...meta }) => meta);
@@ -34,26 +27,20 @@ function effortScale(fixType, n) {
34
27
  return 1;
35
28
  if (fixType === 'automated')
36
29
  return 1.2;
37
- return Math.log10(n + 9); // per-page: sub-linear (bulk workflows)
30
+ return Math.log10(n + 9);
38
31
  }
39
32
  export function runAudit(dataDir, siteUrl, opts = {}) {
40
33
  const t0 = Date.now();
41
34
  const db = new AuditDatabase(dbPathFor(dataDir, siteUrl));
42
35
  try {
43
- // Finalised GSC date (trailing partial days trimmed) — keeps the audit's 28d windows and the
44
- // per-page traffic map consistent with the dashboard, and free of unfinalised-data distortion.
45
36
  const maxDate = gscFreshness(db.db).effectiveMax;
46
37
  const pageCount = db.db.prepare('SELECT COUNT(*) c FROM pages').get().c;
47
- // Self-heal: if the latest crawl's link-graph was left unpopulated (e.g. the crawl process
48
- // was killed before finalizing), recompute inlink_count/iPR/click_depth so graph-dependent
49
- // checks (deep-pages, orphans, iPR-bleed, underlinked-high-demand…) aren't silently no-op.
50
38
  if (pageCount > 0) {
51
39
  const cr = db.db.prepare('SELECT crawl_id FROM pages LIMIT 1').get();
52
40
  if (cr) {
53
41
  const g = db.db.prepare('SELECT MAX(ipr) mi, MAX(inlink_count) mc FROM pages WHERE crawl_id=?').get(cr.crawl_id);
54
42
  if ((g.mi ?? 0) === 0 && (g.mc ?? 0) === 0)
55
43
  finalizeLinkGraph(db.db, cr.crawl_id, null);
56
- // Drift snapshot (idempotent per crawl) so change-detection has cross-crawl history.
57
44
  const at = db.db.prepare(`SELECT COALESCE(finished_at, datetime('now')) at FROM crawl_metadata WHERE crawl_id=?`).get(cr.crawl_id)?.at ?? new Date().toISOString();
58
45
  snapshotCrawl(db.db, cr.crawl_id, at);
59
46
  }
@@ -65,8 +52,6 @@ export function runAudit(dataDir, siteUrl, opts = {}) {
65
52
  checks = checks.filter(c => opts.categories.includes(c.category));
66
53
  if (!opts.includeJudgement)
67
54
  checks = checks.filter(c => c.certainty >= 1);
68
- // Precompute per-page traffic for the window in ONE covering scan, instead of a query per
69
- // finding (which was hundreds of windowed aggregations on a 1.8M-row GSC table — seconds).
70
55
  const trafficMap = new Map();
71
56
  if (maxDate) {
72
57
  for (const row of db.db.prepare(`SELECT page_key, COALESCE(SUM(clicks),0) clicks, COALESCE(SUM(impressions),0) impressions,
@@ -75,7 +60,6 @@ export function runAudit(dataDir, siteUrl, opts = {}) {
75
60
  trafficMap.set(row.page_key, { clicks: row.clicks, impressions: row.impressions, position: row.position });
76
61
  }
77
62
  }
78
- // Total site clicks in the window — the baseline for site-wide (null-url) findings (6e).
79
63
  const siteClicks = maxDate
80
64
  ? db.db.prepare(`SELECT COALESCE(SUM(clicks),0) c FROM search_analytics WHERE date <= '${maxDate}' AND date > date(?, '-28 days')`).get(maxDate).c
81
65
  : 0;
@@ -86,37 +70,24 @@ export function runAudit(dataDir, siteUrl, opts = {}) {
86
70
  let total = 0;
87
71
  const bySeverity = {};
88
72
  const byCategory = {};
89
- const truncated = new Map(); // checkId → true finding count before the cap
73
+ const truncated = new Map();
90
74
  const tx = db.db.transaction(() => {
91
75
  for (const chk of checks) {
92
76
  const all = chk.run(ctx);
93
- // Effort scales with the REAL number of affected pages, not the stored sample. Scaling on
94
- // the capped length understates E (log10(509) vs log10(5009)) and therefore INFLATES
95
- // priority = (T×Y×C)/E on exactly the biggest checks — the cap was reordering the report.
96
77
  const scale = effortScale(chk.fixType, all.length);
97
78
  const findings = all.slice(0, MAX_PER_CHECK);
98
79
  if (all.length > MAX_PER_CHECK)
99
80
  truncated.set(chk.id, all.length);
100
- const E = Math.max(chk.effortBase * scale, 0.0001); // effort in ~hours
81
+ const E = Math.max(chk.effortBase * scale, 0.0001);
101
82
  const Y = yieldOf(chk);
102
83
  for (const f of findings) {
103
84
  const ev = (f.evidence ?? {});
104
85
  const evImpr = Number(ev.impressions) || 0;
105
86
  const evClicks = Number(ev.clicks) || 0;
106
- // `positions` can be a range string ("1.1–15.3", cannibalisation). parseFloat would
107
- // take the BEST position and price the whole query's impressions at its CTR (wildly
108
- // inflating T when the leader sits at #1) — use the midpoint of the range instead.
109
87
  const posNums = String(ev.positions ?? ev.position ?? '').match(/\d+(?:\.\d+)?/g)?.map(Number) ?? [];
110
88
  const evPos = posNums.length ? posNums.reduce((a, b) => a + b, 0) / posNums.length : 0;
111
89
  const hasQuery = ev.query != null && String(ev.query).length > 0;
112
90
  const pageTraf = f.urlKey ? trafficMap.get(f.urlKey) : null;
113
- // Choose the right stake per finding shape:
114
- // • per-query findings (evidence has a `query`: striking-distance, cannibalisation) →
115
- // the query's OWN impressions/clicks (so 2.7k shows 2.7k, not the page's 257k total,
116
- // and multiple per-query findings on one page get DISTINCT priorities);
117
- // • page-level findings on a known URL (ctr-below-expected, missing-meta, …) →
118
- // the page's real GSC traffic (keeps its actual clicks, not 0);
119
- // • null-url findings that still carry evidence traffic → that evidence.
120
91
  let traf;
121
92
  if (hasQuery) {
122
93
  traf = { clicks: evClicks, impressions: evImpr, position: evPos || (pageTraf?.position ?? 0) };
@@ -130,9 +101,6 @@ export function runAudit(dataDir, siteUrl, opts = {}) {
130
101
  else {
131
102
  traf = { clicks: 0, impressions: 0, position: 0 };
132
103
  }
133
- // T = expected monthly clicks at stake: max(current clicks recovered, potential from
134
- // impressions × CTR@position). Site-wide findings with no traffic signal fall back to a
135
- // severity fraction of total site clicks.
136
104
  let T;
137
105
  if (traf.impressions > 0 || traf.clicks > 0) {
138
106
  const potential = traf.impressions > 0 ? traf.impressions * expectedCtr(traf.position || 10) : 0;
@@ -142,10 +110,10 @@ export function runAudit(dataDir, siteUrl, opts = {}) {
142
110
  T = siteClicks * SITEWIDE_FRACTION[chk.severity];
143
111
  }
144
112
  else {
145
- T = 0; // floored below
113
+ T = 0;
146
114
  }
147
- T = Math.max(T, SEVERITY_FLOOR[chk.severity]); // floor so zero-GSC findings aren't lost
148
- const priority = (T * Y * chk.certainty) / E; // expected clicks per dev-hour
115
+ T = Math.max(T, SEVERITY_FLOOR[chk.severity]);
116
+ const priority = (T * Y * chk.certainty) / E;
149
117
  insert.run({
150
118
  run_id: runId, check_id: chk.id, category: chk.category, severity: chk.severity,
151
119
  labels: JSON.stringify(chk.labels), certainty: chk.certainty, url_key: f.urlKey ?? null,
@@ -161,8 +129,6 @@ export function runAudit(dataDir, siteUrl, opts = {}) {
161
129
  });
162
130
  tx();
163
131
  db.db.prepare(`UPDATE audit_runs SET finished_at=datetime('now'), finding_count=? WHERE run_id=?`).run(total, runId);
164
- // Stored counts, annotated with the true total wherever the per-check cap bit — a bare
165
- // "count: 500" reads as "that's all there was" when it may be 5,000.
166
132
  const byCheck = db.db
167
133
  .prepare(`SELECT check_id checkId, category, severity, COUNT(*) count, AVG(priority) priority FROM findings WHERE run_id=? GROUP BY check_id ORDER BY count DESC`)
168
134
  .all(runId)
@@ -188,7 +154,6 @@ export function runAudit(dataDir, siteUrl, opts = {}) {
188
154
  db.close();
189
155
  }
190
156
  }
191
- /** Run one named check and return its findings (no persistence) — for query_audit. */
192
157
  export function runSingleCheck(dataDir, siteUrl, checkId, limit = 100, offset = 0) {
193
158
  const chk = CHECKS.find(c => c.id === checkId);
194
159
  if (!chk)
@@ -1 +1 @@
1
- {"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/audit/engine.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/audit/engine.ts"],"names":[],"mappings":"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"}
@@ -1,18 +1,4 @@
1
1
  import type Database from 'better-sqlite3';
2
- /**
3
- * Demand-first keyword clustering ("list mode") - group a keyword list into topics and
4
- * give each cluster a verdict: OWN / WEAK / ABSENT, with the ranking URL.
5
- *
6
- * Clustering is GSC-bipartite first (free, deterministic, first-party): two keywords
7
- * belong together when Google already answers both with the SAME page - the strongest
8
- * possible clustering signal, and it costs nothing. Keywords the site has no GSC rows
9
- * for are grouped lexically (token Jaccard) - first into the nearest GSC cluster, else
10
- * among themselves.
11
- *
12
- * Verdicts are deterministic from GSC positions: OWN = best position <= 3, WEAK = 4-20,
13
- * ABSENT = no ranking page. Impressions stand in for demand (real, first-party); pass
14
- * the clusters through keyword_volume for market-level volumes when needed.
15
- */
16
2
  export interface KeywordCluster {
17
3
  head: string;
18
4
  verdict: 'own' | 'weak' | 'absent';
@@ -1 +1 @@
1
- {"version":3,"file":"keywordList.d.ts","sourceRoot":"","sources":["../../src/audit/keywordList.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAE3C;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,KAAK,GAAG,MAAM,GAAG,QAAQ,CAAC;IACnC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;CAC/F;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;CAClB;AAYD,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,SAAS,EAAE,IAAI,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,iBAAiB,CAoEvK"}
1
+ {"version":3,"file":"keywordList.d.ts","sourceRoot":"","sources":["../../src/audit/keywordList.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAiB3C,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,KAAK,GAAG,MAAM,GAAG,QAAQ,CAAC;IACnC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;CAC/F;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;CAClB;AAYD,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,SAAS,EAAE,IAAI,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,iBAAiB,CAoEvK"}
@@ -12,12 +12,10 @@ export function clusterKeywordList(db, keywordsIn, opts) {
12
12
  const cap = opts?.maxKeywords ?? 2000;
13
13
  const derived = !keywordsIn?.length;
14
14
  const ub = opts?.maxDate ? `AND date <= '${opts.maxDate}'` : '';
15
- // No list supplied → derive the demand list from the site's own GSC queries (top by impressions).
16
15
  const keywords = derived
17
16
  ? db.prepare(`SELECT query FROM search_analytics WHERE query IS NOT NULL AND date > date('now', '-90 days') ${ub}
18
17
  GROUP BY query ORDER BY SUM(impressions) DESC LIMIT ?`).all(Math.min(cap, 500)).map(r => r.query)
19
18
  : [...new Set(keywordsIn.map(k => k.trim().toLowerCase()).filter(Boolean))].slice(0, cap);
20
- // Best ranking page per keyword (90d, impression-weighted position, top page by impressions).
21
19
  const stmt = db.prepare(`
22
20
  SELECT page_key url, SUM(impressions) imp, SUM(clicks) clk,
23
21
  SUM(position*impressions)*1.0/NULLIF(SUM(impressions),0) pos
@@ -30,7 +28,6 @@ export function clusterKeywordList(db, keywordsIn, opts) {
30
28
  ? { keyword: k, url: r.url, pos: r.pos != null ? Math.round(r.pos * 10) / 10 : null, imp: r.imp, clk: r.clk, inGsc: true }
31
29
  : { keyword: k, url: null, pos: null, imp: 0, clk: 0, inGsc: false };
32
30
  });
33
- // 1) Bipartite: cluster by shared ranking URL.
34
31
  const byUrl = new Map();
35
32
  const orphans = [];
36
33
  for (const r of rows)
@@ -38,8 +35,6 @@ export function clusterKeywordList(db, keywordsIn, opts) {
38
35
  const working = [...byUrl.entries()].map(([url, members]) => ({
39
36
  url, members, tok: new Set(members.flatMap(m => [...tokens(m.keyword)])),
40
37
  }));
41
- // 2) Lexical assignment for non-ranking keywords: nearest GSC cluster at Jaccard >= 0.4,
42
- // else greedy-cluster among themselves (same threshold).
43
38
  const lexical = [];
44
39
  for (const o of orphans) {
45
40
  const tok = tokens(o.keyword);
@@ -1 +1 @@
1
- {"version":3,"file":"keywordList.js","sourceRoot":"","sources":["../../src/audit/keywordList.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"keywordList.js","sourceRoot":"","sources":["../../src/audit/keywordList.ts"],"names":[],"mappings":"AAkCA,MAAM,MAAM,GAAG,CAAC,CAAS,EAAe,EAAE,CACxC,IAAI,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;AAEzE,MAAM,OAAO,GAAG,CAAC,CAAc,EAAE,CAAc,EAAU,EAAE;IACzD,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI;QAAE,OAAO,CAAC,CAAC;IACjC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,CAAC,IAAI,CAAC;QAAE,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAAE,KAAK,EAAE,CAAC;IACzC,OAAO,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC;AAC3C,CAAC,CAAC;AAEF,MAAM,UAAU,kBAAkB,CAAC,EAAqB,EAAE,UAAgC,EAAE,IAAwD;IAClJ,MAAM,GAAG,GAAG,IAAI,EAAE,WAAW,IAAI,IAAI,CAAC;IACtC,MAAM,OAAO,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC;IACpC,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,gBAAgB,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAGhE,MAAM,QAAQ,GAAa,OAAO;QAChC,CAAC,CAAE,EAAE,CAAC,OAAO,CAAC,iGAAiG,EAAE;yEAC5C,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAyB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;QACxI,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,UAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAG7F,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC;;;;kFAIwD,EAAE;gDACpC,CAAC,CAAC;IAGhD,MAAM,IAAI,GAAU,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;QACnC,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAA8E,CAAC;QACnG,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC;YACnB,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;YAC1H,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IACzE,CAAC,CAAC,CAAC;IAGH,MAAM,KAAK,GAAG,IAAI,GAAG,EAAiB,CAAC;IACvC,MAAM,OAAO,GAAU,EAAE,CAAC;IAC1B,KAAK,MAAM,CAAC,IAAI,IAAI;QAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAGzG,MAAM,OAAO,GAAc,CAAC,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;QACvE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;KACzE,CAAC,CAAC,CAAC;IAIJ,MAAM,OAAO,GAAc,EAAE,CAAC;IAC9B,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QAC9B,IAAI,IAAI,GAAmB,IAAI,CAAC;QAAC,IAAI,KAAK,GAAG,CAAC,CAAC;QAC/C,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YAAC,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;YAAC,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC;gBAAC,KAAK,GAAG,CAAC,CAAC;gBAAC,IAAI,GAAG,CAAC,CAAC;YAAC,CAAC;QAAC,CAAC;QACnG,IAAI,IAAI,IAAI,KAAK,IAAI,GAAG,EAAE,CAAC;YAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAAC,KAAK,MAAM,CAAC,IAAI,GAAG;gBAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAAC,SAAS;QAAC,CAAC;QACnG,IAAI,GAAG,GAAmB,IAAI,CAAC;QAAC,IAAI,IAAI,GAAG,CAAC,CAAC;QAC7C,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YAAC,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;YAAC,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC;gBAAC,IAAI,GAAG,CAAC,CAAC;gBAAC,GAAG,GAAG,CAAC,CAAC;YAAC,CAAC;QAAC,CAAC;QAChG,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,EAAE,CAAC;YAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAAC,KAAK,MAAM,CAAC,IAAI,GAAG;gBAAE,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAAC,CAAC;;YAChF,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,MAAM,QAAQ,GAAqB,CAAC,GAAG,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;QAClE,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC/C,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACzF,MAAM,OAAO,GAA8B,OAAO,IAAI,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;QACrJ,MAAM,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;QAC5D,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO;YACvB,OAAO;YACP,GAAG,EAAE,CAAC,CAAC,GAAG;YACV,YAAY,EAAE,OAAO,IAAI,IAAI,IAAI,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;YACnE,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;YACrD,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;YAChD,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC,GAAG,EAAE,WAAW,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;SACzG,CAAC;IACJ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAE1F,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;AACpG,CAAC"}
@@ -1,15 +1,4 @@
1
1
  import { gscFreshness } from '../core/gscFreshness.js';
2
- /**
3
- * New-page opportunity engine (Phase 6c). Grounded in REAL demand (GSC), with ruthless
4
- * subtraction so we never propose a page that already exists. Computable from stored
5
- * GSC + crawl data — no paid calls. (SERP-overlap clustering and search-volume/intent
6
- * scoring are refinements layered on via DataForSEO when available.)
7
- *
8
- * Pipeline: demand universe (queries we get impressions for but rank 11+ → no winning page)
9
- * → subtract (A: we already rank ≤10; B: a page already covers ≥70% of the query's words;
10
- * C: one URL owns >80% of the query's impressions → that's an optimisation, not a new page)
11
- * → cluster surviving queries lexically → score by impressions × intent → attach evidence.
12
- */
13
2
  const STOP = new Set(['the', 'a', 'an', 'and', 'or', 'of', 'for', 'to', 'in', 'on', 'with', 'your', 'you', 'is', 'are', 'best', 'how', 'what', 'vs', 'why', 'can', 'my', 'i', 'it']);
14
3
  const tokenize = (s) => (s || '').toLowerCase().replace(/[^a-z0-9 ]+/g, ' ').split(/\s+/).filter(t => t.length >= 2 && !STOP.has(t));
15
4
  const jaccard = (a, b) => {
@@ -25,13 +14,10 @@ const INTENT_MULT = { transactional: 1, commercial: 0.8, informational: 0.3, nav
25
14
  export function suggestPages(db, opts = {}) {
26
15
  const minImpr = opts.minImpressions ?? 50;
27
16
  const clusterT = opts.clusterThreshold ?? 0.5;
28
- // Window off the FINALISED max date (raw MAX(date) includes 1–3 unfinalised, systematically
29
- // low days that deflate impressions and can shift a query across the rank/dominance thresholds).
30
17
  const maxDate = gscFreshness(db).effectiveMax;
31
18
  if (!maxDate)
32
19
  return { proposals: [], consideredQueries: 0, afterDedup: 0 };
33
20
  const win = `date > date('${maxDate}', '-28 days') AND date <= '${maxDate}'`;
34
- // Demand: per query × page (impression-weighted position).
35
21
  const qp = db.prepare(`SELECT query, page_key, SUM(impressions) impr, SUM(clicks) clicks,
36
22
  SUM(position*impressions)*1.0/NULLIF(SUM(impressions),0) pos
37
23
  FROM search_analytics WHERE query IS NOT NULL AND page_key IS NOT NULL AND ${win}
@@ -48,7 +34,6 @@ export function suggestPages(db, opts = {}) {
48
34
  q.best = Math.min(q.best, r.pos);
49
35
  q.pages.push({ key: r.page_key, impr: r.impr, pos: r.pos });
50
36
  }
51
- // Existing-page vocabulary (title + h1 + url slug) → inverted index for the 70% dedup.
52
37
  const pages = db.prepare(`SELECT url_key, title, h1, url FROM pages WHERE status_code=200`).all();
53
38
  const pageTokens = pages.map(p => {
54
39
  const slug = (() => { try {
@@ -62,30 +47,24 @@ export function suggestPages(db, opts = {}) {
62
47
  const invIndex = new Map();
63
48
  pageTokens.forEach((pt, i) => { for (const t of pt.toks)
64
49
  (invIndex.get(t) ?? invIndex.set(t, []).get(t)).push(i); });
65
- // Persisted intent (optional) for scoring.
66
50
  const intentOf = new Map();
67
51
  try {
68
52
  for (const r of db.prepare('SELECT keyword, intent FROM keyword_intent').all())
69
53
  intentOf.set(r.keyword, r.intent);
70
54
  }
71
- catch { /* table may not exist on very old DBs */ }
72
- // Subtraction.
55
+ catch { }
73
56
  const survivors = [];
74
57
  for (const [query, q] of byQuery) {
75
58
  if (q.totalImpr < minImpr)
76
59
  continue;
77
60
  if (q.best <= 10)
78
- continue; // A: we already rank on page 1
79
- // C: one URL owns >80% of impressions AND is a genuine contender (ranks ≤15) → optimise
80
- // that page, don't spawn a new one. (Gated on rank so an incidental single-page mention at
81
- // position 30 doesn't suppress a real gap — long-tail queries usually surface one page.)
61
+ continue;
82
62
  const dominant = q.pages.reduce((a, b) => (b.impr > a.impr ? b : a));
83
63
  if (dominant.impr / q.totalImpr > 0.8 && dominant.pos <= 15)
84
64
  continue;
85
65
  const qt = tokenize(query);
86
66
  if (!qt.length)
87
67
  continue;
88
- // B: does any existing page already cover ≥70% of the query's words?
89
68
  const hits = new Map();
90
69
  for (const t of qt)
91
70
  for (const i of invIndex.get(t) ?? [])
@@ -97,10 +76,9 @@ export function suggestPages(db, opts = {}) {
97
76
  maxCover = cover;
98
77
  }
99
78
  if (maxCover >= 0.7)
100
- continue; // page exists → optimisation, not a new page
79
+ continue;
101
80
  survivors.push({ query, toks: new Set(qt), impr: q.totalImpr, clicks: q.totalClicks, best: q.best });
102
81
  }
103
- // Cluster survivors lexically (greedy Jaccard); head term = highest-impression query.
104
82
  survivors.sort((a, b) => b.impr - a.impr);
105
83
  const clusters = [];
106
84
  for (const s of survivors) {
@@ -119,7 +97,6 @@ export function suggestPages(db, opts = {}) {
119
97
  const proposals = clusters.map(c => {
120
98
  const intent = intentOf.get(c.headTerm.toLowerCase()) ?? null;
121
99
  const mult = intent ? (INTENT_MULT[intent] ?? 0.5) : 0.5;
122
- // nearest existing page: best partial token coverage of the head term (where to add a link).
123
100
  const ht = tokenize(c.headTerm);
124
101
  let cover = 0, nearestKey = null;
125
102
  const hits = new Map();
@@ -1 +1 @@
1
- {"version":3,"file":"opportunities.js","sourceRoot":"","sources":["../../src/audit/opportunities.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"opportunities.js","sourceRoot":"","sources":["../../src/audit/opportunities.ts"],"names":[],"mappings":"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"}
@@ -1,12 +1,6 @@
1
1
  import type Database from 'better-sqlite3';
2
2
  import type { OwnPageView } from '../core/reconFetch.js';
3
3
  import type { SerpReconResult, ReconVerdict } from '../core/serpRecon.js';
4
- /**
5
- * Content recon core: pick the pages worth a deep competitive look, and turn what we can
6
- * determine WITHOUT judgement (schema gaps, freshness, cannibalisation, SERP format) into
7
- * a trackable to-do ledger. The judgement to-dos (what competitors cover that we don't)
8
- * are written back by the research session via save_recon_todo.
9
- */
10
4
  export interface ReconTarget {
11
5
  urlKey: string;
12
6
  query: string;
@@ -17,16 +11,11 @@ export interface ReconTarget {
17
11
  slipped: boolean;
18
12
  competingUrls: number;
19
13
  }
20
- /** Pick declining / striking-distance pages: high impressions, currently pos 3-15, top query per page. */
21
14
  export declare function selectReconTargets(db: Database.Database, opts: {
22
15
  limit?: number;
23
16
  minImpressions?: number;
24
17
  maxDate: string;
25
18
  }): ReconTarget[];
26
- /** What the CRAWL says about this URL — the reality check the SERP + GSC read has to survive.
27
- * GSC history is legacy: it keeps reporting impressions for a URL that has since been
28
- * canonicalised away or set noindex, and classifying off that alone produced a
29
- * "defend-and-deepen, produce a companion video" verdict for a non-indexable duplicate. */
30
19
  export interface PageState {
31
20
  urlKey: string;
32
21
  crawled: boolean;
@@ -40,7 +29,6 @@ export interface PageState {
40
29
  cannotRank: boolean;
41
30
  }
42
31
  export declare function pageState(db: Database.Database, urlKey: string): PageState;
43
- /** A crawl row that contradicts the SERP/GSC read WINS. Returns null when there's no conflict. */
44
32
  export declare function crawlRealityOverride(v: {
45
33
  verdict: ReconVerdict;
46
34
  note: string;
@@ -55,12 +43,6 @@ export interface DraftTodo {
55
43
  evidence: Record<string, unknown>;
56
44
  priority: number;
57
45
  }
58
- /**
59
- * Priority = OPPORTUNITY, not impressions. Raw impressions ranked a #4 defend-and-deepen page
60
- * above the genuinely broken one, i.e. roughly the wrong working order. This prices the clicks
61
- * actually available: impressions x (CTR at a realistic target position - CTR where we are now),
62
- * damped by the verdict's headroom, then weighted by to-do type.
63
- */
64
46
  export interface PriorityBasis {
65
47
  model: string;
66
48
  impressions: number;
@@ -71,8 +53,6 @@ export interface PriorityBasis {
71
53
  verdictFactor: number;
72
54
  }
73
55
  export declare function opportunityBasis(impressions: number, organicRank: number | null, gscPosition: number, verdict: string): PriorityBasis;
74
- /** The entity-ish terms of a query: the tokens a sibling page would have to share to be competing.
75
- * Minimum length 2 so short but load-bearing terms ("vr", "ai") survive. */
76
56
  export declare function queryCore(query: string): string[];
77
57
  export interface ClusterCannibalisation {
78
58
  core: string[];
@@ -86,16 +66,7 @@ export interface ClusterCannibalisation {
86
66
  }[];
87
67
  cannibalising: boolean;
88
68
  }
89
- /**
90
- * Competition is judged PER QUERY and then unioned across the cluster. Judging it on cluster
91
- * TOTALS is what hid the overlap: the leader accumulates across every query in the topic, so a
92
- * genuine rival page falls under the 10%-of-leader guard and disappears. Here a URL qualifies if
93
- * it competes on any single cluster query (>=10 impressions, >=10% of that query's leader, avg
94
- * position inside 20) — and we separately count the queries where two or more of them collide,
95
- * which is what distinguishes cannibalisation from ordinary topical coverage.
96
- */
97
69
  export declare function clusterCannibalisation(db: Database.Database, query: string, maxDate: string, minImpressions?: number): ClusterCannibalisation;
98
- /** The to-dos we can generate deterministically from our page + the SERP (no judgement). */
99
70
  export declare function deterministicTodos(target: ReconTarget, own: OwnPageView, serp: SerpReconResult, verdict: {
100
71
  verdict: ReconVerdict;
101
72
  note: string;
@@ -118,7 +89,5 @@ export declare function persistReconPage(db: Database.Database, p: {
118
89
  schemaTypes: string[];
119
90
  competitors: unknown;
120
91
  }): void;
121
- /** Insert to-dos for a page. `baseline` snapshots the rank/citation state so outcome is measurable.
122
- * De-dupes on (url_key, action) so a re-run doesn't pile up identical open items. */
123
92
  export declare function insertTodos(db: Database.Database, urlKey: string, query: string, todos: DraftTodo[], baseline: Record<string, unknown>, source: 'auto' | 'research'): number;
124
93
  //# sourceMappingURL=recon.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"recon.d.ts","sourceRoot":"","sources":["../../src/audit/recon.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"recon.d.ts","sourceRoot":"","sources":["../../src/audit/recon.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,4 @@
1
1
  import { expectedCtr } from '../core/ctrModel.js';
2
- /** Pick declining / striking-distance pages: high impressions, currently pos 3-15, top query per page. */
3
2
  export function selectReconTargets(db, opts) {
4
3
  const { limit = 5, minImpressions = 300, maxDate } = opts;
5
4
  const rows = db.prepare(`
@@ -57,7 +56,6 @@ export function pageState(db, urlKey) {
57
56
  cannotRank: indexable === false || canonicalisedAway,
58
57
  };
59
58
  }
60
- /** A crawl row that contradicts the SERP/GSC read WINS. Returns null when there's no conflict. */
61
59
  export function crawlRealityOverride(v, state) {
62
60
  if (!state.cannotRank)
63
61
  return null;
@@ -73,8 +71,6 @@ export function crawlRealityOverride(v, state) {
73
71
  const TYPE_WEIGHT = {
74
72
  'content-gap': 1.0, schema: 0.9, research: 0.8, cannibalisation: 0.7, format: 0.6, freshness: 0.3,
75
73
  };
76
- /** How much headroom the verdict implies. A page already sitting at #4 with nobody editorial
77
- * above it has far less to gain than one losing a competitive gap, even at higher impressions. */
78
74
  const VERDICT_FACTOR = {
79
75
  'competitive-gap': 1.0, 'accuracy-or-freshness': 1.0, 'consolidate-weak-page': 0.9,
80
76
  'page-cannot-rank': 0.8, 'defend-and-deepen': 0.4,
@@ -93,22 +89,10 @@ export function opportunityBasis(impressions, organicRank, gscPosition, verdict)
93
89
  verdictFactor: VERDICT_FACTOR[verdict] ?? 1,
94
90
  };
95
91
  }
96
- /** Cannibalisation at CLUSTER level, not one query at a time. Keying off a single query missed a
97
- * four-URL overlap that only showed up across the whole "<entity>" query family. */
98
92
  const CLUSTER_STOP = new Set(['the', 'and', 'for', 'with', 'best', 'top', 'vs', 'versus', 'review', 'reviews', 'guide', 'how', 'what', 'which', 'why', 'you', 'your', 'cheap', 'budget', 'buy', 'compare', 'comparison', '2024', '2025', '2026', '2027']);
99
- /** The entity-ish terms of a query: the tokens a sibling page would have to share to be competing.
100
- * Minimum length 2 so short but load-bearing terms ("vr", "ai") survive. */
101
93
  export function queryCore(query) {
102
94
  return [...new Set(query.toLowerCase().split(/[^a-z0-9]+/).filter(t => t.length >= 2 && !CLUSTER_STOP.has(t)))].slice(0, 4);
103
95
  }
104
- /**
105
- * Competition is judged PER QUERY and then unioned across the cluster. Judging it on cluster
106
- * TOTALS is what hid the overlap: the leader accumulates across every query in the topic, so a
107
- * genuine rival page falls under the 10%-of-leader guard and disappears. Here a URL qualifies if
108
- * it competes on any single cluster query (>=10 impressions, >=10% of that query's leader, avg
109
- * position inside 20) — and we separately count the queries where two or more of them collide,
110
- * which is what distinguishes cannibalisation from ordinary topical coverage.
111
- */
112
96
  export function clusterCannibalisation(db, query, maxDate, minImpressions = 10) {
113
97
  const core = queryCore(query);
114
98
  const none = { core, clusterQueries: 0, collidingQueries: 0, urls: [], cannibalising: false };
@@ -143,13 +127,10 @@ export function clusterCannibalisation(db, query, maxDate, minImpressions = 10)
143
127
  cannibalising: urls.length >= 2 && colliding >= 1,
144
128
  };
145
129
  }
146
- /** The to-dos we can generate deterministically from our page + the SERP (no judgement). */
147
130
  export function deterministicTodos(target, own, serp, verdict, todayIso, ctx = {}) {
148
131
  const out = [];
149
132
  const basis = opportunityBasis(target.impressions, serp.ourOrganicRank, target.position, verdict.verdict);
150
133
  const w = (type) => Math.max(1, Math.round(basis.opportunityClicks * basis.verdictFactor * (TYPE_WEIGHT[type] ?? 0.5)));
151
- // A URL that cannot rank gets ONE to-do: resolve it. Everything downstream (schema, freshness,
152
- // a companion video) is work against a page Google will not serve.
153
134
  const state = ctx.state;
154
135
  if (state?.cannotRank) {
155
136
  return [{
@@ -165,19 +146,16 @@ export function deterministicTodos(target, own, serp, verdict, todayIso, ctx = {
165
146
  priority: w('cannibalisation'),
166
147
  }];
167
148
  }
168
- // Verdict-driven headline action.
169
149
  if (verdict.verdict === 'accuracy-or-freshness') {
170
150
  out.push({ action: 'Update the facts to current and mark them up so they are machine-liftable', type: 'schema',
171
151
  rationale: `You rank organic #${serp.ourOrganicRank} but the AI Overview does not cite you — a data-accuracy/freshness signal.`,
172
152
  evidence: { organicRank: serp.ourOrganicRank, aioCitesUs: false, aioReferencesChecked: serp.aioReferences.length }, priority: w('schema') + 1 });
173
153
  }
174
- // Schema: commercial/listicle page without Product/Review markup.
175
154
  if (!own.hasProductOrReview && looksCommercial(target.query)) {
176
155
  out.push({ action: 'Add Product + Review/AggregateRating schema per item, with current specs and price',
177
156
  type: 'schema', rationale: 'A comparison/review page with no item-level markup cannot be lifted into rich results or an AI Overview citation cleanly.',
178
157
  evidence: { schemaTypes: own.jsonLdTypes, hasProductOrReview: false }, priority: w('schema') });
179
158
  }
180
- // Freshness: stale dateModified (older than ~90 days from today).
181
159
  if (own.dateModified) {
182
160
  const ageDays = Math.round((Date.parse(todayIso) - Date.parse(own.dateModified)) / 86400000);
183
161
  if (ageDays > 90) {
@@ -186,7 +164,6 @@ export function deterministicTodos(target, own, serp, verdict, todayIso, ctx = {
186
164
  evidence: { dateModified: own.dateModified, ageDays }, priority: w('freshness') });
187
165
  }
188
166
  }
189
- // Cannibalisation — cluster first (the real overlap), falling back to the single query.
190
167
  const cluster = ctx.cluster;
191
168
  if (cluster?.cannibalising) {
192
169
  out.push({ action: `Consolidate cannibalisation — ${cluster.urls.length} of your URLs compete across the "${cluster.core.join(' ')}" cluster, colliding on ${cluster.collidingQueries} quer${cluster.collidingQueries === 1 ? 'y' : 'ies'}`,
@@ -200,14 +177,12 @@ export function deterministicTodos(target, own, serp, verdict, todayIso, ctx = {
200
177
  type: 'cannibalisation', rationale: 'Multiple own URLs competing for one query split signals; pick a canonical winner.',
201
178
  evidence: { competingUrls: target.competingUrls, query: target.query }, priority: w('cannibalisation') });
202
179
  }
203
- // Format: AIO/SERP leans on video.
204
180
  const ytRefs = serp.aioReferences.filter(r => /youtube|youtu\.be/.test(r.domain)).length;
205
181
  if ((serp.videoPresent || ytRefs >= 2)) {
206
182
  out.push({ action: 'Produce a companion video',
207
183
  type: 'format', rationale: `The SERP favours video${ytRefs ? ` and ${ytRefs} of the AI Overview's references are YouTube` : ''}.`,
208
184
  evidence: { videoPresent: serp.videoPresent, youtubeAioRefs: ytRefs }, priority: w('format') });
209
185
  }
210
- // Research pointer: hand the session the competitor set to diff (coverage + originality).
211
186
  const competitors = [
212
187
  ...serp.organicAbove.map(o => ({ rank: o.rank, url: o.url, kind: 'organic-above' })),
213
188
  ...serp.aioReferences.slice(0, 6).map(r => ({ rank: r.rank, url: r.url, kind: 'aio-reference' })),
@@ -236,8 +211,6 @@ export function persistReconPage(db, p) {
236
211
  competitors: JSON.stringify(p.competitors),
237
212
  });
238
213
  }
239
- /** Insert to-dos for a page. `baseline` snapshots the rank/citation state so outcome is measurable.
240
- * De-dupes on (url_key, action) so a re-run doesn't pile up identical open items. */
241
214
  export function insertTodos(db, urlKey, query, todos, baseline, source) {
242
215
  const exists = db.prepare(`SELECT 1 FROM recon_todo WHERE url_key=? AND action=? AND status NOT IN ('shipped','dismissed')`);
243
216
  const ins = db.prepare(`INSERT INTO recon_todo (url_key, query, action, type, rationale, evidence, priority, source, baseline)