@houtini/seo-audit-console 0.4.3 → 0.4.6

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.
@@ -1,19 +1,50 @@
1
+ /** The flag threshold, and the only band boundaries we assert. Raw cross-encoder logits have no
2
+ * intrinsic scale, so a bare "8.18" is unreadable — every consumer gets the threshold, the band
3
+ * convention and the run's own distribution alongside the number. */
4
+ export declare const WEAK_PASSAGE_THRESHOLD = 3;
5
+ export declare const PASSAGE_SCALE_NOTE = "Raw ms-marco-MiniLM cross-encoder logit for the single best passage vs the page's top query. Unbounded (roughly -11..+11), higher = more relevant; it is a relative measure, not a percentage. Our bands: <0 no relevant passage, 0-3 weak (flagged as weak-passage-answer), 3-6 adequate, 6+ a dense, directly-extractable answer. The bands are this tool's convention, not the model's \u2014 compare a score against the run distribution as well.";
6
+ export interface PassageScoreRow {
7
+ url: string;
8
+ query: string;
9
+ impressions: number;
10
+ score: number;
11
+ band: string;
12
+ relevanceProbability: number;
13
+ }
14
+ export interface PassageScoreResult {
15
+ scored: number;
16
+ flagged: number;
17
+ threshold: number;
18
+ scale: string;
19
+ distribution: {
20
+ min: number;
21
+ p25: number;
22
+ median: number;
23
+ p75: number;
24
+ max: number;
25
+ } | null;
26
+ pages: PassageScoreRow[];
27
+ pagesReturned: number;
28
+ pagesTotal: number;
29
+ weakest: {
30
+ url: string;
31
+ query: string;
32
+ score: number;
33
+ }[];
34
+ }
1
35
  /**
2
36
  * For each ranking page (top GSC query, has body chunks), score every chunk against that query with
3
37
  * the local cross-encoder and persist the single best-passage score — the proxy for how neural /
4
38
  * AI search judges whether the page has a dense, extractable answer. On-demand (ML inference cost);
5
39
  * bounded to pages that actually rank. Powers the `weak-passage-answer` check.
40
+ *
41
+ * Returns the score for EVERY page it scored, not just the failures: "does any passage confidently
42
+ * answer the query" is a question about the passing pages too, and having to go to query_data for
43
+ * pages.max_passage_score to answer it was an undocumented extra hop.
6
44
  */
7
45
  export declare function scoreSitePassages(dataDir: string, siteUrl: string, opts?: {
8
46
  limit?: number;
9
47
  minImpressions?: number;
10
- }): Promise<{
11
- scored: number;
12
- flagged: number;
13
- weakest: {
14
- url: string;
15
- query: string;
16
- score: number;
17
- }[];
18
- }>;
48
+ maxPagesReturned?: number;
49
+ }): Promise<PassageScoreResult>;
19
50
  //# sourceMappingURL=passageScore.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"passageScore.d.ts","sourceRoot":"","sources":["../../src/core/passageScore.ts"],"names":[],"mappings":"AAKA;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAO,GACrD,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAAE,CAAC,CAwCxG"}
1
+ {"version":3,"file":"passageScore.d.ts","sourceRoot":"","sources":["../../src/core/passageScore.ts"],"names":[],"mappings":"AAKA;;qEAEqE;AACrE,eAAO,MAAM,sBAAsB,IAAI,CAAC;AACxC,eAAO,MAAM,kBAAkB,2bACyZ,CAAC;AASzb,MAAM,WAAW,eAAe;IAAG,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,oBAAoB,EAAE,MAAM,CAAA;CAAE;AAC/I,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC5F,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC1D;AAED;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAO,GAChF,OAAO,CAAC,kBAAkB,CAAC,CAoD7B"}
@@ -2,18 +2,33 @@ import { AuditDatabase } from './AuditDatabase.js';
2
2
  import { dbPathFor } from './paths.js';
3
3
  import { gscFreshness } from './gscFreshness.js';
4
4
  import { maxPassageScore } from './reranker.js';
5
+ /** The flag threshold, and the only band boundaries we assert. Raw cross-encoder logits have no
6
+ * intrinsic scale, so a bare "8.18" is unreadable — every consumer gets the threshold, the band
7
+ * convention and the run's own distribution alongside the number. */
8
+ export const WEAK_PASSAGE_THRESHOLD = 3;
9
+ export const PASSAGE_SCALE_NOTE = 'Raw ms-marco-MiniLM cross-encoder logit for the single best passage vs the page\'s top query. Unbounded (roughly -11..+11), higher = more relevant; it is a relative measure, not a percentage. Our bands: <0 no relevant passage, 0-3 weak (flagged as weak-passage-answer), 3-6 adequate, 6+ a dense, directly-extractable answer. The bands are this tool\'s convention, not the model\'s — compare a score against the run distribution as well.';
10
+ /** sigmoid(logit) — the model is trained with a binary relevance objective, so this reads as an
11
+ * approximate P(passage is relevant). Useful for "is 8.18 comfortable?" (it is: ~0.9997). */
12
+ const relevanceProbability = (score) => Math.round((1 / (1 + Math.exp(-score))) * 1000) / 1000;
13
+ const band = (s) => (s < 0 ? 'none' : s < WEAK_PASSAGE_THRESHOLD ? 'weak' : s < 6 ? 'adequate' : 'strong');
14
+ const quantile = (sorted, q) => sorted.length ? sorted[Math.min(sorted.length - 1, Math.floor(q * (sorted.length - 1)))] : 0;
5
15
  /**
6
16
  * For each ranking page (top GSC query, has body chunks), score every chunk against that query with
7
17
  * the local cross-encoder and persist the single best-passage score — the proxy for how neural /
8
18
  * AI search judges whether the page has a dense, extractable answer. On-demand (ML inference cost);
9
19
  * bounded to pages that actually rank. Powers the `weak-passage-answer` check.
20
+ *
21
+ * Returns the score for EVERY page it scored, not just the failures: "does any passage confidently
22
+ * answer the query" is a question about the passing pages too, and having to go to query_data for
23
+ * pages.max_passage_score to answer it was an undocumented extra hop.
10
24
  */
11
25
  export async function scoreSitePassages(dataDir, siteUrl, opts = {}) {
12
26
  const db = new AuditDatabase(dbPathFor(dataDir, siteUrl));
13
27
  try {
28
+ const empty = { scored: 0, flagged: 0, threshold: WEAK_PASSAGE_THRESHOLD, scale: PASSAGE_SCALE_NOTE, distribution: null, pages: [], pagesReturned: 0, pagesTotal: 0, weakest: [] };
14
29
  const maxDate = gscFreshness(db.db).effectiveMax;
15
30
  if (!maxDate)
16
- return { scored: 0, flagged: 0, weakest: [] };
31
+ return empty;
17
32
  const minImpr = opts.minImpressions ?? 50;
18
33
  const win = `date > date('${maxDate}', '-28 days') AND date <= '${maxDate}'`;
19
34
  const pages = db.db.prepare(`SELECT s.page_key urlKey, s.query query, s.impr impressions, p.body_chunks bc FROM
@@ -25,6 +40,7 @@ export async function scoreSitePassages(dataDir, siteUrl, opts = {}) {
25
40
  const upd = db.db.prepare(`UPDATE pages SET max_passage_score=@s, max_passage_query=@q, max_passage_impr=@i, max_passage_at=datetime('now') WHERE url_key=@u`);
26
41
  let scored = 0, flagged = 0;
27
42
  const weakest = [];
43
+ const all = [];
28
44
  for (const pg of pages) {
29
45
  let chunks;
30
46
  try {
@@ -44,13 +60,23 @@ export async function scoreSitePassages(dataDir, siteUrl, opts = {}) {
44
60
  const score = Math.round(max * 100) / 100;
45
61
  upd.run({ s: score, q: pg.query, i: pg.impressions, u: pg.urlKey });
46
62
  scored++;
47
- if (score < 3) {
63
+ all.push({ url: pg.urlKey, query: pg.query, impressions: pg.impressions, score, band: band(score), relevanceProbability: relevanceProbability(score) });
64
+ if (score < WEAK_PASSAGE_THRESHOLD) {
48
65
  flagged++;
49
66
  weakest.push({ url: pg.urlKey, query: pg.query, score });
50
67
  }
51
68
  }
52
69
  weakest.sort((a, b) => a.score - b.score);
53
- return { scored, flagged, weakest: weakest.slice(0, 20) };
70
+ const sorted = all.map(p => p.score).sort((a, b) => a - b);
71
+ const cap = opts.maxPagesReturned ?? 200;
72
+ return {
73
+ scored, flagged, threshold: WEAK_PASSAGE_THRESHOLD, scale: PASSAGE_SCALE_NOTE,
74
+ distribution: sorted.length
75
+ ? { min: sorted[0], p25: quantile(sorted, 0.25), median: quantile(sorted, 0.5), p75: quantile(sorted, 0.75), max: sorted[sorted.length - 1] }
76
+ : null,
77
+ pages: all.slice(0, cap), pagesReturned: Math.min(cap, all.length), pagesTotal: all.length,
78
+ weakest: weakest.slice(0, 20),
79
+ };
54
80
  }
55
81
  finally {
56
82
  db.close();
@@ -1 +1 @@
1
- {"version":3,"file":"passageScore.js","sourceRoot":"","sources":["../../src/core/passageScore.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"passageScore.js","sourceRoot":"","sources":["../../src/core/passageScore.ts"],"names":[],"mappings":"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"}
@@ -15,11 +15,19 @@ export type UaMode = 'browser' | 'googlebot';
15
15
  export interface CompetitorContent {
16
16
  url: string;
17
17
  kind: 'video' | 'reddit' | 'page';
18
+ via: 'supadata' | 'reddit-json' | 'firecrawl' | 'plain-fetch' | 'none';
18
19
  title: string | null;
19
20
  content: string;
20
21
  cached: boolean;
21
22
  error?: string;
22
23
  }
24
+ /** Exact host or subdomain match — NEVER a substring test. A substring `/x\.com/` classified
25
+ * pimax.com as video and sent every Shopify product page to the transcriber, which 400'd
26
+ * (build lesson, 2026-08-07: that one regex is why scrapeCompetitors returned almost nothing). */
27
+ export declare function hostIsOneOf(host: string, domains: string[]): boolean;
28
+ /** Which fetcher a URL will be routed to — exposed so recon can report routing per URL
29
+ * instead of leaving a silent misroute to look like an empty competitor set. */
30
+ export declare function routeFor(url: string): CompetitorContent['kind'];
23
31
  /** Reddit serves clean JSON at url.json — title + selftext + top comments, no scraper needed.
24
32
  * Needs the browser header profile (UA + Google referer) — a bot UA gets a 403. */
25
33
  export declare function fetchRedditJson(url: string, maxChars?: number, ua?: UaMode): Promise<{
@@ -1 +1 @@
1
- {"version":3,"file":"reconResearch.d.ts","sourceRoot":"","sources":["../../src/core/reconResearch.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAQ1D;;;iGAGiG;AACjG,MAAM,MAAM,MAAM,GAAG,SAAS,GAAG,WAAW,CAAC;AAmB7C;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;IAClC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAKD;mFACmF;AACnF,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,SAAO,EAAE,EAAE,GAAE,MAAkB,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAkB9I;AAED;8FAC8F;AAC9F,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,SAAO,EAAE,EAAE,GAAE,MAAkB,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAQ7I;AAED,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,MAAM,EACX,OAAO,EAAE;IAAE,SAAS,EAAE,eAAe,GAAG,IAAI,CAAC;IAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAA;CAAE,EAC/E,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAO,GAC5C,OAAO,CAAC,iBAAiB,CAAC,CA2B5B"}
1
+ {"version":3,"file":"reconResearch.d.ts","sourceRoot":"","sources":["../../src/core/reconResearch.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAQ1D;;;iGAGiG;AACjG,MAAM,MAAM,MAAM,GAAG,SAAS,GAAG,WAAW,CAAC;AAmB7C;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;IAClC,GAAG,EAAE,UAAU,GAAG,aAAa,GAAG,WAAW,GAAG,aAAa,GAAG,MAAM,CAAC;IACvE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAMD;;kGAEkG;AAClG,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAGpE;AAKD;gFACgF;AAChF,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAG/D;AAED;mFACmF;AACnF,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,SAAO,EAAE,EAAE,GAAE,MAAkB,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAkB9I;AAED;8FAC8F;AAC9F,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,SAAO,EAAE,EAAE,GAAE,MAAkB,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAQ7I;AAED,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,MAAM,EACX,OAAO,EAAE;IAAE,SAAS,EAAE,eAAe,GAAG,IAAI,CAAC;IAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAA;CAAE,EAC/E,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAO,GAC5C,OAAO,CAAC,iBAAiB,CAAC,CA8B5B"}
@@ -20,8 +20,24 @@ function browserHeaders(mode) {
20
20
  'upgrade-insecure-requests': '1',
21
21
  };
22
22
  }
23
- const isVideo = (host) => /youtube|youtu\.be|tiktok|twitter\.com|x\.com/.test(host);
24
- const isReddit = (host) => /reddit\.com/.test(host);
23
+ /** Hosts whose content only Supadata can read (transcripts) and hosts we read via Reddit's own JSON. */
24
+ const VIDEO_HOSTS = ['youtube.com', 'youtu.be', 'tiktok.com', 'twitter.com', 'x.com', 'vimeo.com', 'dailymotion.com', 'instagram.com', 'facebook.com'];
25
+ const REDDIT_HOSTS = ['reddit.com', 'redd.it'];
26
+ /** Exact host or subdomain match — NEVER a substring test. A substring `/x\.com/` classified
27
+ * pimax.com as video and sent every Shopify product page to the transcriber, which 400'd
28
+ * (build lesson, 2026-08-07: that one regex is why scrapeCompetitors returned almost nothing). */
29
+ export function hostIsOneOf(host, domains) {
30
+ const h = hostOf(host);
31
+ return domains.some(d => h === d || h.endsWith(`.${d}`));
32
+ }
33
+ const isVideo = (host) => hostIsOneOf(host, VIDEO_HOSTS);
34
+ const isReddit = (host) => hostIsOneOf(host, REDDIT_HOSTS);
35
+ /** Which fetcher a URL will be routed to — exposed so recon can report routing per URL
36
+ * instead of leaving a silent misroute to look like an empty competitor set. */
37
+ export function routeFor(url) {
38
+ const host = hostOf(url);
39
+ return isVideo(host) ? 'video' : isReddit(host) ? 'reddit' : 'page';
40
+ }
25
41
  /** Reddit serves clean JSON at url.json — title + selftext + top comments, no scraper needed.
26
42
  * Needs the browser header profile (UA + Google referer) — a bot UA gets a 403. */
27
43
  export async function fetchRedditJson(url, maxChars = 4000, ua = 'browser') {
@@ -60,25 +76,26 @@ export async function fetchPlainPage(url, maxChars = 4000, ua = 'browser') {
60
76
  return { title: ex.title, content: text.slice(0, maxChars) };
61
77
  }
62
78
  export async function fetchCompetitorContent(url, clients, opts = {}) {
63
- const host = hostOf(url);
64
79
  const max = opts.maxChars ?? 4000;
65
80
  const ua = opts.ua ?? 'browser';
81
+ const kind = routeFor(url);
82
+ let firecrawlError = null;
66
83
  try {
67
- if (isVideo(host)) {
84
+ if (kind === 'video') {
68
85
  if (!clients.supadata)
69
- return { url, kind: 'video', title: null, content: '', cached: false, error: 'SUPADATA_API_KEY not set — cannot transcribe video' };
86
+ return { url, kind, via: 'none', title: null, content: '', cached: false, error: 'SUPADATA_API_KEY not set — cannot transcribe video' };
70
87
  const t = await clients.supadata.transcript(url, { maxChars: Math.max(max, 5000) });
71
- return { url, kind: 'video', title: null, content: t.content, cached: t.cached };
88
+ return { url, kind, via: 'supadata', title: null, content: t.content, cached: t.cached };
72
89
  }
73
- if (isReddit(host)) {
90
+ if (kind === 'reddit') {
74
91
  // .json first (cleaner structured data); HTML fallback. Both need the browser profile.
75
92
  try {
76
93
  const r = await fetchRedditJson(url, max, ua);
77
- return { url, kind: 'reddit', title: r.title, content: r.content, cached: false };
94
+ return { url, kind, via: 'reddit-json', title: r.title, content: r.content, cached: false };
78
95
  }
79
96
  catch {
80
97
  const r = await fetchPlainPage(url, max, ua);
81
- return { url, kind: 'reddit', title: r.title, content: r.content, cached: false };
98
+ return { url, kind, via: 'plain-fetch', title: r.title, content: r.content, cached: false };
82
99
  }
83
100
  }
84
101
  // Pages: Firecrawl first (cleaner, handles JS); fall back to the free browser-profile fetch
@@ -86,16 +103,20 @@ export async function fetchCompetitorContent(url, clients, opts = {}) {
86
103
  if (clients.firecrawl) {
87
104
  try {
88
105
  const s = await clients.firecrawl.scrape(url, { maxChars: max, proxy: 'auto' });
89
- return { url, kind: 'page', title: s.title, content: s.markdown, cached: s.cached };
106
+ return { url, kind, via: 'firecrawl', title: s.title, content: s.markdown, cached: s.cached };
107
+ }
108
+ catch (e) {
109
+ firecrawlError = e instanceof Error ? e.message : String(e); /* fall through to the browser-profile fetch */
90
110
  }
91
- catch { /* fall through to the browser-profile fetch */ }
92
111
  }
93
112
  const p = await fetchPlainPage(url, max, ua);
94
- return { url, kind: 'page', title: p.title, content: p.content, cached: false };
113
+ return { url, kind, via: 'plain-fetch', title: p.title, content: p.content, cached: false };
95
114
  }
96
115
  catch (e) {
97
- const kind = isVideo(host) ? 'video' : isReddit(host) ? 'reddit' : 'page';
98
- return { url, kind, title: null, content: '', cached: false, error: e instanceof Error ? e.message : String(e) };
116
+ // Report BOTH failures when Firecrawl was tried first — "plain fetch 403" alone hides the
117
+ // fact that the paid fetcher refused the site for a different reason.
118
+ const msg = e instanceof Error ? e.message : String(e);
119
+ return { url, kind, via: 'none', title: null, content: '', cached: false, error: firecrawlError ? `${msg} (firecrawl first: ${firecrawlError})` : msg };
99
120
  }
100
121
  }
101
122
  //# sourceMappingURL=reconResearch.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"reconResearch.js","sourceRoot":"","sources":["../../src/core/reconResearch.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"reconResearch.js","sourceRoot":"","sources":["../../src/core/reconResearch.ts"],"names":[],"mappings":"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"}
@@ -8,14 +8,17 @@ import type { DfsResponse } from './DataForSeoClient.js';
8
8
  export interface SerpReconResult {
9
9
  itemTypes: string[];
10
10
  ourOrganicRank: number | null;
11
+ serpDepth: number;
11
12
  aioPresent: boolean;
12
- aioCitesUs: boolean;
13
+ aioCitesUs: boolean | null;
13
14
  aioAsync: boolean;
15
+ aioResolved: boolean;
14
16
  aioReferences: {
15
17
  rank: number | null;
16
18
  domain: string;
17
19
  url: string;
18
20
  title: string;
21
+ nested: boolean;
19
22
  }[];
20
23
  videoPresent: boolean;
21
24
  organicAbove: {
@@ -32,13 +35,16 @@ export interface SerpReconResult {
32
35
  }
33
36
  /** Registrable-ish host: lowercase, strip scheme/path/www. Good enough to match a domain field. */
34
37
  export declare function hostOf(s: string | undefined | null): string;
35
- export declare function parseSerpForRecon(resp: DfsResponse, ownDomain: string): SerpReconResult;
38
+ export declare function parseSerpForRecon(resp: DfsResponse, ownDomain: string, serpDepth?: number): SerpReconResult;
36
39
  /**
37
40
  * The deterministic verdict from organic rank x AIO citation (Richard's rule):
38
41
  * strong organic but NOT in the AIO = a data-accuracy/freshness problem — Google ranks
39
42
  * us but won't quote us.
40
43
  */
41
- export type ReconVerdict = 'defend-and-deepen' | 'accuracy-or-freshness' | 'consolidate-weak-page' | 'competitive-gap';
44
+ export type ReconVerdict = 'defend-and-deepen' | 'accuracy-or-freshness' | 'consolidate-weak-page' | 'competitive-gap' | 'page-cannot-rank';
45
+ /** How to say "we didn't find you" without implying a position we never measured. Rank 11 and
46
+ * rank 80 both come back as null, so the honest phrasing has to carry the depth we fetched. */
47
+ export declare const rankPhrase: (r: SerpReconResult) => string;
42
48
  export declare function reconVerdict(r: SerpReconResult): {
43
49
  verdict: ReconVerdict;
44
50
  note: string;
@@ -1 +1 @@
1
- {"version":3,"file":"serpRecon.d.ts","sourceRoot":"","sources":["../../src/core/serpRecon.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEzD;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,aAAa,EAAE;QAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACrF,YAAY,EAAE,OAAO,CAAC;IACtB,YAAY,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC7E,UAAU,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC9D;AAED,mGAAmG;AACnG,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAG3D;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,GAAG,eAAe,CA2CvF;AAED;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,mBAAmB,GAAG,uBAAuB,GAAG,uBAAuB,GAAG,iBAAiB,CAAC;AAEvH,wBAAgB,YAAY,CAAC,CAAC,EAAE,eAAe,GAAG;IAAE,OAAO,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAkBxF"}
1
+ {"version":3,"file":"serpRecon.d.ts","sourceRoot":"","sources":["../../src/core/serpRecon.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEzD;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE;QAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;IACtG,YAAY,EAAE,OAAO,CAAC;IACtB,YAAY,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC7E,UAAU,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC9D;AAED,mGAAmG;AACnG,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAG3D;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,SAAK,GAAG,eAAe,CA2DvG;AAED;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,mBAAmB,GAAG,uBAAuB,GAAG,uBAAuB,GAAG,iBAAiB,GAAG,kBAAkB,CAAC;AAE5I;+FAC+F;AAC/F,eAAO,MAAM,UAAU,GAAI,GAAG,eAAe,KAAG,MACwH,CAAC;AAEzK,wBAAgB,YAAY,CAAC,CAAC,EAAE,eAAe,GAAG;IAAE,OAAO,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CA2BxF"}
@@ -4,21 +4,21 @@ export function hostOf(s) {
4
4
  return '';
5
5
  return String(s).replace(/^https?:\/\//i, '').replace(/\/.*$/, '').replace(/^www\./i, '').toLowerCase().trim();
6
6
  }
7
- export function parseSerpForRecon(resp, ownDomain) {
7
+ export function parseSerpForRecon(resp, ownDomain, serpDepth = 20) {
8
8
  const own = hostOf(ownDomain);
9
9
  const result = resp.tasks?.[0]?.result?.[0] ?? {};
10
10
  const items = result.items ?? [];
11
11
  const itemTypes = result.item_types ?? [...new Set(items.map(i => i.type))];
12
12
  let ourOrganicRank = null;
13
13
  const organic = [];
14
- let aioPresent = false, aioCitesUs = false, aioAsync = false, videoPresent = false;
14
+ let aioPresent = false, aioAsync = false, videoPresent = false;
15
15
  const aioReferences = [];
16
16
  const videoItems = [];
17
17
  for (const it of items) {
18
18
  const t = it.type;
19
19
  if (t === 'organic') {
20
20
  const domain = hostOf(it.domain || it.url);
21
- const rank = Number(it.rank_group) || organic.length + 1;
21
+ const rank = Number(it.rank_group) || organic.length + 1; // rank_group = position among ORGANIC results
22
22
  organic.push({ rank, domain, url: it.url ?? '', title: it.title ?? '' });
23
23
  if (own && domain === own && ourOrganicRank == null)
24
24
  ourOrganicRank = rank;
@@ -26,46 +26,74 @@ export function parseSerpForRecon(resp, ownDomain) {
26
26
  else if (t === 'ai_overview') {
27
27
  aioPresent = true;
28
28
  aioAsync = !!it.asynchronous_ai_overview;
29
- const refs = it.references ?? [];
29
+ // Citations live in TWO places and the nested set is NOT a subset of the top-level one
30
+ // (verified against cached responses: "ai website builder" had 1 top-level reference and
31
+ // 3 further domains only present on the ai_overview_element chunks). Read both, dedupe.
32
+ const seenRef = new Set();
30
33
  let i = 0;
31
- for (const ref of refs) {
32
- const domain = hostOf(ref.domain || ref.url);
33
- aioReferences.push({ rank: Number(ref.rank_absolute) || ++i, domain, url: ref.url ?? '', title: ref.title ?? '' });
34
- if (own && domain === own)
35
- aioCitesUs = true;
36
- }
34
+ const collect = (refs, nested) => {
35
+ for (const ref of refs ?? []) {
36
+ const url = ref?.url ?? '';
37
+ if (!url || seenRef.has(url))
38
+ continue;
39
+ seenRef.add(url);
40
+ aioReferences.push({ rank: Number(ref.rank_absolute) || ++i, domain: hostOf(ref.domain || url), url, title: ref.title ?? '', nested });
41
+ }
42
+ };
43
+ collect(it.references, false);
44
+ for (const el of it.items ?? [])
45
+ collect(el?.references, true);
37
46
  }
38
47
  else if (t === 'video' || t === 'short_videos') {
39
48
  videoPresent = true;
40
49
  for (const v of it.items ?? []) {
41
50
  if (v?.url)
42
- videoItems.push({ url: v.url, title: v.title ?? '', source: hostOf(v.url) });
51
+ videoItems.push({ url: v.url, title: v.title ?? '', source: hostOf(v.source || v.domain || v.url) });
43
52
  }
44
53
  }
45
54
  }
55
+ // Citation is only knowable when the AIO actually resolved. An async AIO with no references
56
+ // is UNKNOWN, not "not cited" — 5 of 11 cached AIO SERPs came back that way, and reading them
57
+ // as "not cited" manufactured accuracy-or-freshness verdicts out of a missing fetch.
58
+ const aioResolved = aioPresent && aioReferences.length > 0;
59
+ const aioCitesUs = !aioPresent ? false
60
+ : aioResolved ? aioReferences.some(r => own && r.domain === own)
61
+ : null;
46
62
  // Organic ranked above us (the ones actually beating us); if we're absent, the whole top block.
47
63
  const organicAbove = ourOrganicRank == null
48
64
  ? organic.slice(0, 8)
49
65
  : organic.filter(o => o.rank < ourOrganicRank);
50
- return { itemTypes, ourOrganicRank, aioPresent, aioCitesUs, aioAsync, aioReferences, videoPresent, organicAbove, videoItems };
66
+ return { itemTypes, ourOrganicRank, serpDepth, aioPresent, aioCitesUs, aioAsync, aioResolved, aioReferences, videoPresent, organicAbove, videoItems };
51
67
  }
68
+ /** How to say "we didn't find you" without implying a position we never measured. Rank 11 and
69
+ * rank 80 both come back as null, so the honest phrasing has to carry the depth we fetched. */
70
+ export const rankPhrase = (r) => r.ourOrganicRank != null ? `#${r.ourOrganicRank}` : `absent from the top ${r.serpDepth} organic results (true position unknown, and unmeasured beyond ${r.serpDepth})`;
52
71
  export function reconVerdict(r) {
53
72
  const strongOrganic = r.ourOrganicRank != null && r.ourOrganicRank <= 5;
54
73
  const shape = r.videoPresent ? ' The SERP carries a video pack, so some click loss is format, not content.' : '';
74
+ const pos = rankPhrase(r);
55
75
  if (!r.aioPresent) {
56
76
  return strongOrganic
57
- ? { verdict: 'defend-and-deepen', note: `No AI Overview on this SERP; you rank organic #${r.ourOrganicRank}. Defend and deepen.${shape}` }
58
- : { verdict: 'competitive-gap', note: `No AI Overview; you rank organic ${r.ourOrganicRank ?? 'outside top results'}. Close the competitive gap.${shape}` };
77
+ ? { verdict: 'defend-and-deepen', note: `No AI Overview on this SERP; you rank organic ${pos}. Defend and deepen.${shape}` }
78
+ : { verdict: 'competitive-gap', note: `No AI Overview; you rank organic ${pos}. Close the competitive gap.${shape}` };
79
+ }
80
+ // AIO present but unresolved (async, no references pulled): citation is UNKNOWN. Classify on
81
+ // organic strength alone and say so — never bill an unfetched AIO as "does not cite you".
82
+ if (r.aioCitesUs == null) {
83
+ const why = `An AI Overview is present but its citations were not resolved${r.aioAsync ? ' (Google loads it asynchronously)' : ''}, so whether it cites you is UNKNOWN — re-run with the async AI Overview loaded before treating this as an accuracy problem.`;
84
+ return strongOrganic
85
+ ? { verdict: 'defend-and-deepen', note: `You rank organic ${pos}. ${why}${shape}` }
86
+ : { verdict: 'competitive-gap', note: `You rank organic ${pos}. ${why}${shape}` };
59
87
  }
60
88
  if (strongOrganic && r.aioCitesUs) {
61
- return { verdict: 'defend-and-deepen', note: `You rank organic #${r.ourOrganicRank} and the AI Overview already cites you. Deepen to become the primary source.${shape}` };
89
+ return { verdict: 'defend-and-deepen', note: `You rank organic ${pos} and the AI Overview already cites you. Deepen to become the primary source.${shape}` };
62
90
  }
63
91
  if (strongOrganic && !r.aioCitesUs) {
64
- return { verdict: 'accuracy-or-freshness', note: `You rank organic #${r.ourOrganicRank} but the AI Overview does NOT cite you — a data-accuracy or freshness signal. Make the facts current, correct and marked up so they are liftable.${shape}` };
92
+ return { verdict: 'accuracy-or-freshness', note: `You rank organic ${pos} but the AI Overview does NOT cite you (checked ${r.aioReferences.length} references) — a data-accuracy or freshness signal. Make the facts current, correct and marked up so they are liftable.${shape}` };
65
93
  }
66
94
  if (!strongOrganic && r.aioCitesUs) {
67
- return { verdict: 'consolidate-weak-page', note: `The AI Overview cites you but your organic rank is weak (${r.ourOrganicRank ?? 'absent'}) — you have a quotable nugget on an under-powered page; consolidate and strengthen it.${shape}` };
95
+ return { verdict: 'consolidate-weak-page', note: `The AI Overview cites you but your organic rank is weak (${pos}) — you have a quotable nugget on an under-powered page; consolidate and strengthen it.${shape}` };
68
96
  }
69
- return { verdict: 'competitive-gap', note: `Not cited in the AI Overview and organic rank is ${r.ourOrganicRank ?? 'absent'} — full coverage gap; run the deep competitor diff.${shape}` };
97
+ return { verdict: 'competitive-gap', note: `Not cited in the AI Overview (checked ${r.aioReferences.length} references) and organic rank is ${pos} — full coverage gap; run the deep competitor diff.${shape}` };
70
98
  }
71
99
  //# sourceMappingURL=serpRecon.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"serpRecon.js","sourceRoot":"","sources":["../../src/core/serpRecon.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"serpRecon.js","sourceRoot":"","sources":["../../src/core/serpRecon.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA2EpE,wBAAgB,OAAO,IAAI,MAAM,CAGhC;AA8KD,wBAAgB,YAAY,IAAI;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,GAAG,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAw0D9E"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA2EpE,wBAAgB,OAAO,IAAI,MAAM,CAGhC;AA8KD,wBAAgB,YAAY,IAAI;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,GAAG,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CA26D9E"}