fix(socials): canonicalize twitter.com → x.com in normalizeProfileUrl

Merged
SW
3mo ago
+38-4

Summary

Canonicalizes twitter.com/* → x.com/* inside normalizeProfileUrl so every reader/writer agrees on one socials.profile_url upsert key. Fixes the X scrape gap where results persisted to orphaned x.com/* twin rows while artists stayed connected to stale twitter.com/* rows.

Tracking issue: recoupable/chat#1851 (PR #1 of 2 required + 1 optional). Canonical-domain decision (x.com — official brand, matches the Apify actor's echo) recorded on the issue 2026-07-06.

What changed

  • lib/socials/normalizeProfileUrl.ts: after stripping protocol/www./trailing slash, rewrite a leading twitter.com host to x.com (anchored — nottwitter.com and path occurrences untouched; case-insensitive host match; preserves handle casing).
  • lib/socials/__tests__/normalizeProfileUrl.test.ts: 5 new canonicalization cases (equal keys for both spellings, x.com passthrough, no-false-positive domains, bare domain); 2 existing expectations updated to the new contract.

Merge sequencing

No docs PR — no request/response contract change (same reasoning as docs#262 closed unmerged on chat#1840). This PR leads; the database dedupe migration (chat#1851 PR #2) follows it, since the migration implements the same canonical-domain decision. Independent of the optional tweetless-fallback PR.

Tests

  • RED→GREEN: new cases failed against the old implementation (5 failed | 9 passed), pass after (14 passed).
  • Full domain sweep: vitest run lib/socials lib/apify lib/artist lib/artists → 53 files, 263 tests passed.
  • tsc --noEmit: 200 pre-existing errors on clean origin/test (all in lib/trigger tests), 200 after — no new errors. eslint clean on changed files.

Done-when (from chat#1851)

  • Unit test normalizeProfileUrl("https://x.com/Foo") === normalizeProfileUrl("https://twitter.com/Foo")
  • Live X scrape for an artist connected to a twitter.com/* row updates that row (preview verification pending — note: full effect on existing rows also needs the PR #2 migration, since current connected rows keep their twitter.com keys until migrated)

🤖 Generated with Claude Code


Summary by cubic

Canonicalizes twitter.com/* to x.com/* during profile URL normalization so all readers/writers use the same upsert key. Fixes X scrape write-backs landing on orphaned x.com rows while artists stayed linked to twitter.com (chat#1851).

  • Bug Fixes
    • After stripping protocol/www/trailing slash, rewrite leading twitter.com host to x.com (anchored, case-insensitive, preserves handle casing).
    • Tests cover canonicalization, equal keys for both spellings, x.com passthrough, no false positives, and bare domain; updated prior expectations accordingly.

Summary by CodeRabbit

  • Bug Fixes

    • Profile URLs are now normalized more consistently, including converting Twitter/X profile links to the preferred domain format.
    • Existing cleanup still removes protocol, www., and trailing slashes so links are stored in a uniform way.
  • Documentation

    • Expanded guidance for how profile URL normalization handles domain aliases.