From 131dae6b39c87317211aadde76e59f1c7ac45801 Mon Sep 17 00:00:00 2001 From: Amadeus Demarzi Date: Wed, 6 May 2026 22:42:54 -0700 Subject: [PATCH 1/2] Add support for tangled.org Freed up a lot of github specific code, and potentially should make future additions of other services a lot easier... --- .../app/(diffshub)/(view)/[...path]/page.tsx | 32 ++++ .../[owner]/[repo]/[...githubPath]/page.tsx | 23 --- .../(view)/_components/CodeViewHeader.tsx | 8 +- .../(view)/_components/ReviewUI.tsx | 7 +- .../(view)/_components/usePatchLoader.ts | 40 ++-- .../(diffshub)/(view)/_components/utils.ts | 58 ++++-- .../app/(diffshub)/_home/HomeFetchForm.tsx | 8 +- apps/docs/app/api/{gh => }/diff/larg.patch | 0 apps/docs/app/api/{gh => }/diff/larg2.patch | 0 apps/docs/app/api/{gh => }/diff/larg3.patch | 0 apps/docs/app/api/{gh => }/diff/route.ts | 171 ++++++++++++++---- apps/docs/app/api/{gh => }/diff/smol.patch | 0 apps/docs/app/gh/CodeViewHeader.tsx | 11 +- apps/docs/app/gh/utils.ts | 42 +++-- 14 files changed, 264 insertions(+), 136 deletions(-) create mode 100644 apps/docs/app/(diffshub)/(view)/[...path]/page.tsx delete mode 100644 apps/docs/app/(diffshub)/(view)/[owner]/[repo]/[...githubPath]/page.tsx rename apps/docs/app/api/{gh => }/diff/larg.patch (100%) rename apps/docs/app/api/{gh => }/diff/larg2.patch (100%) rename apps/docs/app/api/{gh => }/diff/larg3.patch (100%) rename apps/docs/app/api/{gh => }/diff/route.ts (59%) rename apps/docs/app/api/{gh => }/diff/smol.patch (100%) diff --git a/apps/docs/app/(diffshub)/(view)/[...path]/page.tsx b/apps/docs/app/(diffshub)/(view)/[...path]/page.tsx new file mode 100644 index 000000000..919b6e8c0 --- /dev/null +++ b/apps/docs/app/(diffshub)/(view)/[...path]/page.tsx @@ -0,0 +1,32 @@ +import { redirect } from 'next/navigation'; + +import { ReviewUI } from '../_components/ReviewUI'; + +// Viewer route that mirrors the upstream path. GitHub is the public default, +// while hidden alternate domains can opt in through the `domain` query param. +export default async function DiffshubViewByPathPage({ + params, + searchParams, +}: { + params: Promise<{ path: string[] }>; + searchParams: Promise<{ domain?: string | string[] }>; +}) { + const { path } = await params; + const { domain } = await searchParams; + if (path.length === 0) { + redirect('/'); + } + const requestedDomain = Array.isArray(domain) ? domain[0] : domain; + const upstreamPath = `/${path.join('/')}`; + const host = + requestedDomain == null || requestedDomain === '' + ? 'github.com' + : requestedDomain; + const url = `https://${host}${upstreamPath}`; + + return ( +
+ +
+ ); +} diff --git a/apps/docs/app/(diffshub)/(view)/[owner]/[repo]/[...githubPath]/page.tsx b/apps/docs/app/(diffshub)/(view)/[owner]/[repo]/[...githubPath]/page.tsx deleted file mode 100644 index 1f8ada2a7..000000000 --- a/apps/docs/app/(diffshub)/(view)/[owner]/[repo]/[...githubPath]/page.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { redirect } from 'next/navigation'; - -import { ReviewUI } from '../../../_components/ReviewUI'; - -// Viewer route that mirrors GitHub paths after `/owner/repo`, letting GitHub -// decide whether the path has a `.diff` or `.patch` response. -export default async function DiffshubViewByGitHubPathPage({ - params, -}: { - params: Promise<{ owner: string; repo: string; githubPath: string[] }>; -}) { - const { owner, repo, githubPath } = await params; - if (githubPath.length === 0) { - redirect('/'); - } - const url = `https://github.com/${owner}/${repo}/${githubPath.join('/')}`; - - return ( -
- -
- ); -} diff --git a/apps/docs/app/(diffshub)/(view)/_components/CodeViewHeader.tsx b/apps/docs/app/(diffshub)/(view)/_components/CodeViewHeader.tsx index b32f5d8b9..71e4a5f8e 100644 --- a/apps/docs/app/(diffshub)/(view)/_components/CodeViewHeader.tsx +++ b/apps/docs/app/(diffshub)/(view)/_components/CodeViewHeader.tsx @@ -25,7 +25,7 @@ import { } from 'react'; import { DiffsHubLogo } from './DiffsHubLogo'; -import { getGitHubPath } from './utils'; +import { getPatchViewerHref } from './utils'; import { Button } from '@/components/ui/button'; import { ButtonGroup, ButtonGroupItem } from '@/components/ui/button-group'; import { @@ -103,15 +103,15 @@ export const CodeViewHeader = memo(function CodeViewHeader({ (event: FormEvent) => { event.preventDefault(); const normalizedURL = url.trim(); - const githubPath = getGitHubPath(normalizedURL); - if (githubPath == null) { + const viewerHref = getPatchViewerHref(normalizedURL); + if (viewerHref == null) { console.error('Invalid URL', normalizedURL); return; } setURL(normalizedURL); startTransition(() => { - router.push(githubPath); + router.push(viewerHref); }); } ); diff --git a/apps/docs/app/(diffshub)/(view)/_components/ReviewUI.tsx b/apps/docs/app/(diffshub)/(view)/_components/ReviewUI.tsx index ec2963235..540c4d1ee 100644 --- a/apps/docs/app/(diffshub)/(view)/_components/ReviewUI.tsx +++ b/apps/docs/app/(diffshub)/(view)/_components/ReviewUI.tsx @@ -28,10 +28,12 @@ import { } from './utils'; interface ReviewUIProps { + domain?: string; initialUrl: string; + path: string; } -export function ReviewUI({ initialUrl }: ReviewUIProps) { +export function ReviewUI({ domain, initialUrl, path }: ReviewUIProps) { const isWorkerPoolReadyOrDisable = useIsWorkerPoolReadyOrDisabled(); const [diffStyle, setDiffStyle] = useState<'split' | 'unified'>('split'); const [fileTreeOverlayOpen, setFileTreeOverlayOpen] = useState(false); @@ -56,8 +58,9 @@ export function ReviewUI({ initialUrl }: ReviewUIProps) { treeSource, viewerKey, } = usePatchLoader({ - initialUrl, + domain, onLoadStart: handlePatchLoadStart, + path, viewerRef, }); diff --git a/apps/docs/app/(diffshub)/(view)/_components/usePatchLoader.ts b/apps/docs/app/(diffshub)/(view)/_components/usePatchLoader.ts index 9282b6173..62c8eaa1c 100644 --- a/apps/docs/app/(diffshub)/(view)/_components/usePatchLoader.ts +++ b/apps/docs/app/(diffshub)/(view)/_components/usePatchLoader.ts @@ -32,7 +32,6 @@ import type { CodeViewSavedCommentItem, CommentMetadata, } from './types'; -import { getGitHubPath } from './utils'; const STREAM_PUBLISH_FILE_BATCH_SIZE = 25; const STREAM_PUBLISH_INTERVAL_MS = 100; @@ -41,8 +40,9 @@ const STREAM_TREE_PUBLISH_FILE_BATCH_SIZE = 1_000; const STREAM_TREE_PUBLISH_INTERVAL_MS = 1_000; interface UsePatchLoaderOptions { - initialUrl: string; + domain?: string; onLoadStart?(): void; + path: string; viewerRef: RefObject | null>; } @@ -60,8 +60,9 @@ interface UsePatchLoaderResult { } export function usePatchLoader({ - initialUrl, + domain, onLoadStart, + path, viewerRef, }: UsePatchLoaderOptions): UsePatchLoaderResult { const [initialItems, setInitialItems] = useState< @@ -86,18 +87,12 @@ export function usePatchLoader({ const requestIdRef = useRef(0); useEffect(() => { - const githubPath = getGitHubPath(initialUrl); - if (githubPath == null) { - setInitialItems([]); - setTreeSource(null); - setDiffStats(null); - setCommentFileByItemId(null); - setCommentSections([]); - setErrorMessage('Enter a valid GitHub URL.'); - setLoadState('error'); - return; + const patchRequestKey = + domain == null || domain === '' ? path : `${domain}${path}`; + const patchSearchParams = new URLSearchParams({ path }); + if (domain != null && domain !== '') { + patchSearchParams.set('domain', domain); } - const resolvedGitHubPath = githubPath; const controller = new AbortController(); const requestId = ++requestIdRef.current; @@ -116,7 +111,7 @@ export function usePatchLoader({ async function loadPatch() { try { - const cacheKeyPrefix = encodeURIComponent(resolvedGitHubPath); + const cacheKeyPrefix = encodeURIComponent(patchRequestKey); async function commitFullPatch(patchContent: string) { if (!isCurrentRequest()) { return; @@ -127,10 +122,7 @@ export function usePatchLoader({ if (!isCurrentRequest()) { return; } - const loadedData = buildCodeViewData( - patchContent, - resolvedGitHubPath - ); + const loadedData = buildCodeViewData(patchContent, patchRequestKey); if (!isCurrentRequest()) { return; } @@ -144,10 +136,10 @@ export function usePatchLoader({ } console.time('-- request time'); - const response = await fetch( - `/api/gh/diff?path=${encodeURIComponent(resolvedGitHubPath)}`, - { cache: 'no-store', signal: controller.signal } - ); + const response = await fetch(`/api/diff?${patchSearchParams}`, { + cache: 'no-store', + signal: controller.signal, + }); console.timeEnd('-- request time'); // This only catches route setup errors. GitHub fetch failures are @@ -329,7 +321,7 @@ export function usePatchLoader({ return () => { controller.abort(); }; - }, [initialUrl, loadAttempt, onLoadStart, viewerRef]); + }, [domain, loadAttempt, onLoadStart, path, viewerRef]); const retryLoad = useCallback(() => { setLoadAttempt((attempt) => attempt + 1); diff --git a/apps/docs/app/(diffshub)/(view)/_components/utils.ts b/apps/docs/app/(diffshub)/(view)/_components/utils.ts index 210dcf0ee..afba68cf9 100644 --- a/apps/docs/app/(diffshub)/(view)/_components/utils.ts +++ b/apps/docs/app/(diffshub)/(view)/_components/utils.ts @@ -56,33 +56,57 @@ export function isSavedAnnotation( export function getGitHubPath(input: string): string | undefined { try { const parsedURL = new URL(input); - if (parsedURL.hostname === GITHUB_HOST) { - if (parsedURL.pathname === '/') { - return undefined; - } - return parsedURL.pathname; - } + return getGitHubPathFromURL(parsedURL); + } catch { + return undefined; + } +} - if (parsedURL.hostname !== GITHUB_RAW_DIFF_HOST) { - return undefined; +export function getPatchViewerHref(input: string): string | undefined { + try { + const parsedURL = new URL(input); + const githubPath = getGitHubPathFromURL(parsedURL); + if (githubPath != null) { + return githubPath; } - const rawDiffMatch = RAW_GITHUB_DIFF_PATH_PATTERN.exec(parsedURL.pathname); - if (rawDiffMatch == null) { - return undefined; + if (parsedURL.pathname !== '/') { + return `${parsedURL.pathname}?domain=${encodeURIComponent( + parsedURL.hostname + )}`; } - const owner = rawDiffMatch[1]; - const repo = rawDiffMatch[2]; - const pullFile = rawDiffMatch[3]; - if (owner == null || repo == null || pullFile == null) { + return undefined; + } catch { + return undefined; + } +} + +function getGitHubPathFromURL(parsedURL: URL): string | undefined { + if (parsedURL.hostname === GITHUB_HOST) { + if (parsedURL.pathname === '/') { return undefined; } + return parsedURL.pathname; + } - return `/${owner}/${repo}/pull/${pullFile}`; - } catch { + if (parsedURL.hostname !== GITHUB_RAW_DIFF_HOST) { + return undefined; + } + + const rawDiffMatch = RAW_GITHUB_DIFF_PATH_PATTERN.exec(parsedURL.pathname); + if (rawDiffMatch == null) { + return undefined; + } + + const owner = rawDiffMatch[1]; + const repo = rawDiffMatch[2]; + const pullFile = rawDiffMatch[3]; + if (owner == null || repo == null || pullFile == null) { return undefined; } + + return `/${owner}/${repo}/pull/${pullFile}`; } // Translates the diff-level change type surfaced by @pierre/diffs into the diff --git a/apps/docs/app/(diffshub)/_home/HomeFetchForm.tsx b/apps/docs/app/(diffshub)/_home/HomeFetchForm.tsx index 6ea546c8b..ba42496e6 100644 --- a/apps/docs/app/(diffshub)/_home/HomeFetchForm.tsx +++ b/apps/docs/app/(diffshub)/_home/HomeFetchForm.tsx @@ -10,7 +10,7 @@ import { useTransition, } from 'react'; -import { getGitHubPath } from '../(view)/_components/utils'; +import { getPatchViewerHref } from '../(view)/_components/utils'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -31,13 +31,13 @@ export const HomeFetchForm = memo(function HomeFetchForm() { const formData = new FormData(event.currentTarget); const urlField = formData.get('url'); const rawUrl = typeof urlField === 'string' ? urlField.trim() : ''; - const githubPath = getGitHubPath(rawUrl); - if (githubPath == null) { + const viewerHref = getPatchViewerHref(rawUrl); + if (viewerHref == null) { setErrorMessage('Enter a valid GitHub URL.'); return; } - startTransition(() => router.push(githubPath)); + startTransition(() => router.push(viewerHref)); }, [router] ); diff --git a/apps/docs/app/api/gh/diff/larg.patch b/apps/docs/app/api/diff/larg.patch similarity index 100% rename from apps/docs/app/api/gh/diff/larg.patch rename to apps/docs/app/api/diff/larg.patch diff --git a/apps/docs/app/api/gh/diff/larg2.patch b/apps/docs/app/api/diff/larg2.patch similarity index 100% rename from apps/docs/app/api/gh/diff/larg2.patch rename to apps/docs/app/api/diff/larg2.patch diff --git a/apps/docs/app/api/gh/diff/larg3.patch b/apps/docs/app/api/diff/larg3.patch similarity index 100% rename from apps/docs/app/api/gh/diff/larg3.patch rename to apps/docs/app/api/diff/larg3.patch diff --git a/apps/docs/app/api/gh/diff/route.ts b/apps/docs/app/api/diff/route.ts similarity index 59% rename from apps/docs/app/api/gh/diff/route.ts rename to apps/docs/app/api/diff/route.ts index 665599293..d1bcfa446 100644 --- a/apps/docs/app/api/gh/diff/route.ts +++ b/apps/docs/app/api/diff/route.ts @@ -10,51 +10,69 @@ const NON_DIFF_RESPONSE_MESSAGE = 'GitHub did not return a diff for this URL.'; const NON_WHITESPACE_PATTERN = /\S/; const RAW_GITHUB_DIFF_PATH_PATTERN = /^\/raw\/[^/]+\/[^/]+\/pull\/[^/]+\.(?:diff|patch)$/; +const LOCAL_PATCH_GITHUB_PATH = '/nodejs/node/pull/59805'; +const LOCAL_PATCH_FIXTURE = 'larg.patch'; +// 'smol.patch' +const HIDDEN_PATCH_DOMAIN_RULES = [ + { domainRoot: 'tangled.org', defaultExtension: '.patch' }, +] as const; + +interface ResolvedPatchRequest { + fixture?: string; + patchURL: string; + sourceURL?: string; +} -// Validates the GitHub-relative path, normalizes it to a raw diff URL, and +// Validates the accepted path or URL, normalizes it to a raw diff URL, and // returns a streaming proxy response so the client can render files as they // arrive instead of waiting for the full patch text. export async function GET(request: NextRequest) { const searchParams = request.nextUrl.searchParams; const path = searchParams.get('path'); + const domain = searchParams.get('domain'); + const url = searchParams.get('url'); - if (!path) { - return createTextResponse('Path parameter is required', { status: 400 }); - } - - // Override to fetch default patch without requiring GitHub, to help avoid - // abuse and potential rate limits - if (path === '/nodejs/node/pull/59805') { - try { - const localPatchPath = join( - process.cwd(), - 'app/api/gh/diff', - 'larg.patch' - // 'smol.patch' - ); - const patchContent = await readFile(localPatchPath, 'utf-8'); - return createPatchTextResponse(patchContent, { sourceURL: 'local' }); - } catch (error) { - return createTextResponse( - `Failed to read local patch: ${error instanceof Error ? error.message : 'Unknown error'}`, - { status: 500 } - ); - } + if (path == null && url == null) { + return createTextResponse('Path or URL parameter is required', { + status: 400, + }); } try { // The client normally sends only the GitHub-relative path, but GitHub also - // exposes raw PR diffs through patch-diff.githubusercontent.com. Keep this - // as a narrow allowlist so the route cannot become a general URL fetcher. - const patchURL = resolvePatchURL(path); - if (patchURL == null) { + // exposes raw PR diffs through patch-diff.githubusercontent.com. Tangled + // paths use an explicit domain query parameter and are normalized to their + // patch endpoint. + const patchRequest = resolvePatchRequest(path, domain, url); + if (patchRequest == null) { return createTextResponse('Invalid GitHub patch URL format', { status: 400, }); } - return createPatchStreamResponse(patchURL, request.signal, { - sourceURL: patchURL, + // Override to fetch default patch without requiring GitHub, to help avoid + // abuse and potential rate limits. + if (patchRequest.fixture != null) { + try { + const localPatchPath = join( + process.cwd(), + 'app/api/diff', + patchRequest.fixture + ); + const patchContent = await readFile(localPatchPath, 'utf-8'); + return createPatchTextResponse(patchContent, { + sourceURL: patchRequest.sourceURL ?? patchRequest.patchURL, + }); + } catch (error) { + return createTextResponse( + `Failed to read local patch: ${error instanceof Error ? error.message : 'Unknown error'}`, + { status: 500 } + ); + } + } + + return createPatchStreamResponse(patchRequest.patchURL, request.signal, { + sourceURL: patchRequest.sourceURL ?? patchRequest.patchURL, }); } catch (error) { return createTextResponse( @@ -64,12 +82,33 @@ export async function GET(request: NextRequest) { } } -// Resolves the accepted GitHub URL shapes to the exact upstream URL to fetch. -// Most callers send a GitHub-relative path, but this also permits GitHub's raw -// PR diff host without opening the route up to arbitrary domains. -function resolvePatchURL(input: string): string | undefined { +// Resolves the accepted URL shapes to the exact upstream URL to fetch. Most +// callers send a GitHub-relative path, but this also permits GitHub's raw PR +// diff host and Tangled patch URLs without becoming a general URL fetcher. +function resolvePatchRequest( + path: string | null, + domain: string | null, + url: string | null +): ResolvedPatchRequest | undefined { + if (url != null) { + return resolvePatchURLInput(url); + } + + if (path == null) { + return undefined; + } + + if (domain != null) { + const patchURL = resolveDomainPatchURL(domain, path); + return patchURL == null ? undefined : { patchURL }; + } + + return resolvePatchURLInput(path); +} + +function resolvePatchURLInput(input: string): ResolvedPatchRequest | undefined { if (input.startsWith('/')) { - return resolveGitHubPath(input); + return resolveGitHubPatchRequest(input); } let parsedURL: URL; @@ -84,14 +123,74 @@ function resolvePatchURL(input: string): string | undefined { } if (parsedURL.hostname === GITHUB_HOST) { - return resolveGitHubPath(parsedURL.pathname); + return resolveGitHubPatchRequest(parsedURL.pathname); } if ( parsedURL.hostname === GITHUB_RAW_DIFF_HOST && RAW_GITHUB_DIFF_PATH_PATTERN.test(parsedURL.pathname) ) { - return parsedURL.href; + return { patchURL: parsedURL.href }; + } + + const domainPatchURL = resolveDomainPatchURL( + parsedURL.hostname, + parsedURL.pathname + ); + return domainPatchURL == null ? undefined : { patchURL: domainPatchURL }; +} + +function resolveGitHubPatchRequest( + path: string +): ResolvedPatchRequest | undefined { + if (path === LOCAL_PATCH_GITHUB_PATH) { + return { + fixture: LOCAL_PATCH_FIXTURE, + patchURL: 'local', + sourceURL: 'local', + }; + } + + const patchURL = resolveGitHubPath(path); + return patchURL == null ? undefined : { patchURL }; +} + +function resolveDomainPatchURL( + domain: string, + path: string +): string | undefined { + const domainRule = getHiddenPatchDomainRule(domain); + if (domainRule == null) { + return undefined; + } + + const pathWithLeadingSlash = path.startsWith('/') ? path : `/${path}`; + const url = new URL(`https://${domainRule.hostname}`); + url.pathname = pathWithLeadingSlash; + if (!url.pathname.endsWith(domainRule.defaultExtension)) { + url.pathname += domainRule.defaultExtension; + } + + return url.href; +} + +function getHiddenPatchDomainRule( + domain: string +): { defaultExtension: string; hostname: string } | undefined { + let hostname: string; + try { + hostname = new URL(`https://${domain}`).hostname; + } catch { + return undefined; + } + + for (const domainRule of HIDDEN_PATCH_DOMAIN_RULES) { + if ( + hostname === domainRule.domainRoot || + hostname.endsWith(`.${domainRule.domainRoot}`) + ) { + return { defaultExtension: domainRule.defaultExtension, hostname }; + } } return undefined; diff --git a/apps/docs/app/api/gh/diff/smol.patch b/apps/docs/app/api/diff/smol.patch similarity index 100% rename from apps/docs/app/api/gh/diff/smol.patch rename to apps/docs/app/api/diff/smol.patch diff --git a/apps/docs/app/gh/CodeViewHeader.tsx b/apps/docs/app/gh/CodeViewHeader.tsx index 85e29d323..d3ef7f303 100644 --- a/apps/docs/app/gh/CodeViewHeader.tsx +++ b/apps/docs/app/gh/CodeViewHeader.tsx @@ -28,7 +28,6 @@ import type { } from './types'; import { createCodeViewFileTreeSource, - getGitHubPath, mapChangeTypeToGitStatus, } from './utils'; import { Button } from '@/components/ui/button'; @@ -86,20 +85,18 @@ export const CodeViewHeader = memo(function CodeViewHeader({ const [url, setURL] = useState(DEFAULT_PR_URL); const renderPullRequest = useStableCallback(async (input: string) => { const normalizedURL = input.trim(); - const githubPath = getGitHubPath(normalizedURL); - if (githubPath == null) { + if (normalizedURL.length === 0) { console.error('Invalid URL', normalizedURL); return undefined; } + const patchSearchParams = new URLSearchParams({ url: normalizedURL }); setFetching(true); lastLoadedURLRef.current = normalizedURL; try { console.time('-- request time'); - const response = await fetch( - `/api/gh/diff?path=${encodeURIComponent(githubPath)}` - ); + const response = await fetch(`/api/diff?${patchSearchParams}`); console.timeEnd('-- request time'); // This endpoint opens a local stream before GitHub responds, so this @@ -119,7 +116,7 @@ export const CodeViewHeader = memo(function CodeViewHeader({ const parsedPatches = parsePatchFiles( patchContent, // Use the url as a cache key - encodeURIComponent(githubPath) + encodeURIComponent(normalizedURL) ); console.timeEnd('-- parsing patches'); diff --git a/apps/docs/app/gh/utils.ts b/apps/docs/app/gh/utils.ts index beddc1188..926388151 100644 --- a/apps/docs/app/gh/utils.ts +++ b/apps/docs/app/gh/utils.ts @@ -56,33 +56,37 @@ export function isSavedAnnotation( export function getGitHubPath(input: string): string | undefined { try { const parsedURL = new URL(input); - if (parsedURL.hostname === GITHUB_HOST) { - if (parsedURL.pathname === '/') { - return undefined; - } - return parsedURL.pathname; - } + return getGitHubPathFromURL(parsedURL); + } catch { + return undefined; + } +} - if (parsedURL.hostname !== GITHUB_RAW_DIFF_HOST) { +function getGitHubPathFromURL(parsedURL: URL): string | undefined { + if (parsedURL.hostname === GITHUB_HOST) { + if (parsedURL.pathname === '/') { return undefined; } + return parsedURL.pathname; + } - const rawDiffMatch = RAW_GITHUB_DIFF_PATH_PATTERN.exec(parsedURL.pathname); - if (rawDiffMatch == null) { - return undefined; - } + if (parsedURL.hostname !== GITHUB_RAW_DIFF_HOST) { + return undefined; + } - const owner = rawDiffMatch[1]; - const repo = rawDiffMatch[2]; - const pullFile = rawDiffMatch[3]; - if (owner == null || repo == null || pullFile == null) { - return undefined; - } + const rawDiffMatch = RAW_GITHUB_DIFF_PATH_PATTERN.exec(parsedURL.pathname); + if (rawDiffMatch == null) { + return undefined; + } - return `/${owner}/${repo}/pull/${pullFile}`; - } catch { + const owner = rawDiffMatch[1]; + const repo = rawDiffMatch[2]; + const pullFile = rawDiffMatch[3]; + if (owner == null || repo == null || pullFile == null) { return undefined; } + + return `/${owner}/${repo}/pull/${pullFile}`; } // Translates the diff-level change type surfaced by @pierre/diffs into the From baf737f022d45109e1d6af7a796a42953382d459 Mon Sep 17 00:00:00 2001 From: Amadeus Demarzi Date: Thu, 7 May 2026 00:25:38 -0700 Subject: [PATCH 2/2] Fix for review --- AGENTS.md | 4 +++- apps/docs/app/api/diff/route.ts | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index f4e2f83c9..dd6fcc95f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -102,7 +102,9 @@ bun run format ``` **Important:** Always run `bun run format` from the monorepo root after making -changes to ensure consistent formatting. +changes to ensure consistent formatting. For code changes, verification is not +complete until you have also run `bun run lint` from the monorepo root and the +relevant `bun run tsc` typecheck. - Always preserve trailing newlines at the end of files. diff --git a/apps/docs/app/api/diff/route.ts b/apps/docs/app/api/diff/route.ts index d1bcfa446..fe6f675c7 100644 --- a/apps/docs/app/api/diff/route.ts +++ b/apps/docs/app/api/diff/route.ts @@ -166,7 +166,8 @@ function resolveDomainPatchURL( const pathWithLeadingSlash = path.startsWith('/') ? path : `/${path}`; const url = new URL(`https://${domainRule.hostname}`); - url.pathname = pathWithLeadingSlash; + const normalizedPath = pathWithLeadingSlash.replace(/\/+$/, ''); + url.pathname = normalizedPath === '' ? '/' : normalizedPath; if (!url.pathname.endsWith(domainRule.defaultExtension)) { url.pathname += domainRule.defaultExtension; }