* Resolve a domain to its canonical brand identity * Follows redirects and resolves through house portfolios
(
domain: string,
options: { maxRedirects?: number; skipCache?: boolean } = {}
)
| 624 | * Follows redirects and resolves through house portfolios |
| 625 | */ |
| 626 | async resolveBrand( |
| 627 | domain: string, |
| 628 | options: { maxRedirects?: number; skipCache?: boolean } = {} |
| 629 | ): Promise<ResolvedBrand | null> { |
| 630 | const { maxRedirects = 3, skipCache = false } = options; |
| 631 | const normalizedDomain = domain.replace(/^https?:\/\//, '').replace(/\/$/, ''); |
| 632 | const cacheKey = `resolve:${normalizedDomain}`; |
| 633 | |
| 634 | // Check resolution cache unless explicitly skipped |
| 635 | if (!skipCache) { |
| 636 | const cached = this.resolutionCache.get(cacheKey); |
| 637 | if (cached !== undefined) { |
| 638 | return cached; |
| 639 | } |
| 640 | } |
| 641 | |
| 642 | let currentDomain = normalizedDomain; |
| 643 | let redirectCount = 0; |
| 644 | |
| 645 | while (redirectCount < maxRedirects) { |
| 646 | const validationResult = await this.validateDomain(currentDomain, { skipCache }); |
| 647 | |
| 648 | if (!validationResult.valid || !validationResult.raw_data) { |
| 649 | this.resolutionCache.set(cacheKey, null); |
| 650 | return null; |
| 651 | } |
| 652 | |
| 653 | const data = validationResult.raw_data as BrandJson; |
| 654 | |
| 655 | switch (validationResult.variant) { |
| 656 | case 'authoritative_location': { |
| 657 | const authData = data as AuthoritativeLocationVariant; |
| 658 | try { |
| 659 | const url = new URL(authData.authoritative_location); |
| 660 | currentDomain = url.hostname + url.pathname; |
| 661 | redirectCount++; |
| 662 | continue; |
| 663 | } catch { |
| 664 | this.resolutionCache.set(cacheKey, null); |
| 665 | return null; |
| 666 | } |
| 667 | } |
| 668 | |
| 669 | case 'house_redirect': { |
| 670 | const redirectData = data as HouseRedirectVariant; |
| 671 | currentDomain = redirectData.house; |
| 672 | redirectCount++; |
| 673 | continue; |
| 674 | } |
| 675 | |
| 676 | case 'brand_agent': { |
| 677 | const agentData = data as BrandAgentVariant; |
| 678 | const result: ResolvedBrand = { |
| 679 | canonical_id: currentDomain, |
| 680 | canonical_domain: currentDomain, |
| 681 | brand_name: currentDomain, // Agent should provide the name via MCP |
| 682 | brand_agent_url: agentData.brand_agent.url, |
| 683 | source: 'brand_json', |
no test coverage detected