diff --git a/src/server/lib/dataforseoBacklinks.test.ts b/src/server/lib/dataforseoBacklinks.test.ts index 2f95688..b8e20ad 100644 --- a/src/server/lib/dataforseoBacklinks.test.ts +++ b/src/server/lib/dataforseoBacklinks.test.ts @@ -24,6 +24,24 @@ describe("normalizeBacklinksTarget", () => { }); }); + it("trims trailing slashes from non-root page URLs", () => { + expect( + normalizeBacklinksTarget("https://github.com/every-app/open-seo/"), + ).toEqual({ + apiTarget: "https://github.com/every-app/open-seo", + displayTarget: "https://github.com/every-app/open-seo", + scope: "page", + }); + }); + + it("keeps trailing slashes for root page URLs", () => { + expect(normalizeBacklinksTarget("https://example.com/")).toEqual({ + apiTarget: "https://example.com/", + displayTarget: "https://example.com/", + scope: "page", + }); + }); + it("treats bare hostnames as domain lookups", () => { expect(normalizeBacklinksTarget("Example.com")).toEqual({ apiTarget: "example.com", diff --git a/src/server/lib/dataforseoBacklinksTarget.ts b/src/server/lib/dataforseoBacklinksTarget.ts index 9e5caa5..6613cf2 100644 --- a/src/server/lib/dataforseoBacklinksTarget.ts +++ b/src/server/lib/dataforseoBacklinksTarget.ts @@ -11,6 +11,17 @@ type NormalizeBacklinksTargetOptions = { scope?: BacklinksLookupInput["scope"]; }; +function normalizePageTargetUrl(url: URL, hostname: string): string { + const normalizedUrl = new URL(url.toString()); + normalizedUrl.hostname = hostname; + + if (normalizedUrl.pathname.length > 1) { + normalizedUrl.pathname = normalizedUrl.pathname.replace(/\/+$/, ""); + } + + return normalizedUrl.toString(); +} + export function normalizeBacklinksTarget( input: string, options: NormalizeBacklinksTargetOptions = {}, @@ -63,23 +74,26 @@ export function normalizeBacklinksTarget( if (requestedScope === "page") { const normalizedUrl = new URL(parsed.toString()); - normalizedUrl.hostname = exactHostname; if (!hasExplicitProtocol && !hasMeaningfulPath) { normalizedUrl.pathname = "/"; } + + const normalizedTarget = normalizePageTargetUrl( + normalizedUrl, + exactHostname, + ); return { - apiTarget: normalizedUrl.toString(), - displayTarget: normalizedUrl.toString(), + apiTarget: normalizedTarget, + displayTarget: normalizedTarget, scope: "page", }; } if (hasExplicitProtocol || hasMeaningfulPath) { - const normalizedUrl = new URL(parsed.toString()); - normalizedUrl.hostname = exactHostname; + const normalizedTarget = normalizePageTargetUrl(parsed, exactHostname); return { - apiTarget: normalizedUrl.toString(), - displayTarget: normalizedUrl.toString(), + apiTarget: normalizedTarget, + displayTarget: normalizedTarget, scope: "page", }; }