Skip to content

Domain Rating + citation-landscape insights on the Citations page#327

Open
jrhizor wants to merge 9 commits into
mainfrom
emdash/experiment-with-dr-fto3h
Open

Domain Rating + citation-landscape insights on the Citations page#327
jrhizor wants to merge 9 commits into
mainfrom
emdash/experiment-with-dr-fto3h

Conversation

@jrhizor

@jrhizor jrhizor commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

What

A Domain Rating (DR) + citation-landscape area on the Citations page, built on Ahrefs' free, key-less DR API. Exploratory; iterated down to a focused set.

DR cache

  • packages/lib/src/ahrefs.ts — free domain-rating-free endpoint (no key). Transient failures (429/5xx) throw and are never written, so the cache can't be poisoned.
  • Two interchangeable backends behind one loader (apps/web/src/lib/domain-rating-cache.ts), selected by DR_CACHE_BACKEND:
    • sqlite (default) — local node:sqlite file (temporary/experiment).
    • postgres — the new global, cross-host domain_ratings table (~1-week TTL). Migration file is committed but NOT applied (0010_domain_ratings_cache.sql); set DR_CACHE_BACKEND=postgres after applying it. Bulk PK load handles thousands of domains in one query; one failing domain never aborts the batch; DB errors degrade gracefully.
  • Reads-only over Postgres + the DR cache; no destructive schema changes (one additive table, not yet migrated).

On the page

  • Citation quadrants — DR × citations scatter, dots sized by citations, median crosshairs + quadrant labels, category toggles, and a plain-language authority-bar verdict (content-gated vs. authority-gated).
  • Cited domains & Cited URLs — sortable / searchable / category-filterable / paginated data tables (@tanstack/react-table + shadcn table). The domains table folds in DR + run-based citation volatility as sortable columns.
  • Competitor page opportunities — per-prompt bubble graph: demand (total citations) vs. your citation share; red = you're absent.
  • Per-prompt citation map — per-prompt domain scatter (DR × citations, sized by pages) + authority-/content-driven prompt rankings.

Notes

  • DR is weak as a global predictor of citations but useful as a segmented feasibility gauge (it matters in YMYL/health verticals, not lifestyle ones) — hence the quadrant category toggles and the authority verdict.
  • The insights server still computes a few aggregates that are no longer rendered (trimmed from the UI this pass) — can be removed in a follow-up, along with the now-superseded "Top Cited Domains/URLs" lists in citations-display.tsx.

Verification

  • @workspace/lib 215 tests, @workspace/web 127 tests pass; tsc --noEmit + biome lint clean on changed files. Storybook stories added (browser render not run here — no Playwright browser in this env).

Pulls Ahrefs free Domain Rating per cited domain (cached locally via node:sqlite at $DR_CACHE_PATH) and adds an experimental insights area: DR citation quadrants with category toggles + authority verdict, a per-prompt citation map, sortable/searchable/filterable cited-domain (with DR + run-based volatility) and cited-URL data tables, plus source-type, scoreboard, kingmaker, winnability and branded/unbranded cuts. Reads-only over Postgres + the DR cache; no DB schema changes.
@vercel

vercel Bot commented Jun 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
elmo Ready Ready Preview, Comment Jun 12, 2026 7:43am

Request Review

Adds a global domain_ratings table (migration file only — NOT applied) caching Ahrefs DR per domain with a ~1-week TTL. New Postgres store mirrors the SQLite loader and is selected via DR_CACHE_BACKEND=postgres (default stays sqlite, so nothing activates until the migration is applied). Resilient: bulk PK load handles thousands of domains in one query; transient Ahrefs failures are never written (no cache poisoning); a single failing domain never aborts the batch; DB errors degrade gracefully.
Cleanup pass: removed the low-signal cards (scoreboard, source-type mix, kingmakers, possible-untracked-competitors, winnability, authority-bar-by-source-type). The landscape block is now Cited domains + Cited URLs data tables, a Competitor page opportunities prompt-bubble graph (demand vs. your citation share), and the per-prompt citation map. The server still returns the removed aggregates (unused by the UI) — can be trimmed in a follow-up.
…lists

Server fn now computes only what the UI renders (cited-domains/URLs tables, per-prompt map); dropped the unused scoreboard/source-type/kingmaker/winnability/etc. aggregations and the orphaned getCitationPromptDomainStats read. Removed the old Top Cited Domains/URLs lists from citations-display (superseded by the new sortable data tables); kept the stats, what's-changed, opportunities, and subreddit sections.
… dead CitationsDisplay props

Prompt-detail citations tab now renders the sortable/searchable CitedDomainsTable (DR/volatility columns hidden — no per-prompt DR) + CitedUrlsTable, restoring the lists lost when those sections moved out of the shared CitationsDisplay. Removed the now-unused brandName/maxDomains/maxUrls/onCompetitorAdded props from CitationsDisplayProps and both callers.
Conflicts resolved (citations rework #325 vs DR-experiment):
- server/citations.ts: take main's getCitationsFn rewrite (Google module,
  page types, domain-categories.server split); keep the experiment's
  citation-filters helper (resolveEnabledPromptIds/hasTagFilter) which
  domain-ratings.ts also depends on. Faithful extraction of main's inline
  filter, so behavior is unchanged.
- citations-display.tsx: take main's full component (Top Cited tables,
  Google Shopping, TrackDomainPopover).
- citations.tsx route: render main's CitationsDisplay (with onCompetitorAdded
  + restored useQueryClient/brandKeys/dashboardKeys) and keep the experiment's
  DomainRatingCorrelation + CitationInsights below it.
- prompts/$promptId.tsx: keep the experiment's CitedDomains/UrlsTable restore
  alongside CitationsDisplay; merge both import sets.
- citation-insights.ts / domain-ratings.ts: import categorizeDomain from
  domain-categories.server (main moved it out of domain-categories).
jrhizor added 2 commits June 12, 2026 00:35
Resolved against main's citations split (#336/#358 filtered-list shell,
#353 prompt Google Shopping, #354 dep bumps):
- server/citations.ts: keep citation-filters helper; take main's
  postgres-read import + google-module.ts extraction (dropped now-unused
  PerPromptCitationPageRow / getEffectiveBrandedStatus).
- citations.tsx route: adopt main's FilteredListShell + useListFilters
  (router search params) structure; render DomainRatingCorrelation +
  CitationInsights inside the shell children, adapted to filters.tags.
- apps/web/package.json: main's @TanStack bumps + keep @tanstack/react-table
  (DR cited-tables dep).
- pnpm-lock.yaml: took main's lockfile, reran pnpm install to add react-table.
- prompts/$promptId.tsx, citations-display.tsx, postgres-read.ts auto-merged;
  cited-tables restore + DR cache preserved, Google Shopping intact via
  CitationsDisplay. typecheck clean, 172 web unit tests pass.
Relocate the DR-experiment components alongside main's citation cards:
- domain-rating-correlation.tsx, citation-insights.tsx, citation-tables.tsx
  → apps/web/src/components/citations/
- update imports in the citations route, prompt-detail, and stories.

Left rendered as route-level siblings of CitationsDisplay (they self-fetch
via useDomainRatings/useCitationInsights), since CitationsDisplay stays a
pure presentational composition fed by citationData.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant