Files
Pamela Chia 5a7c0d6d84 fix(docs): resolve legacy sdk reference urls (#51064)
I made the crawler renderer resolve legacy JavaScript and Dart reference
slugs to their current sections, and updated authored guide and SDK spec
links to use them. Exact slugs still win, ambiguous bare slugs still
return 404, and `file-buckets-listv2` remains a section slug in
canonical links. I kept the www redirect work in a separate draft PR
because the apps deploy independently.

## To test

- [x] On the Docs preview, request `reference/javascript/order` and
`reference/dart/get-user` with a bot user agent. Expect the intended
heading and canonical URL.
- [x] Request `reference/javascript/file-buckets-listv2` with bot and
browser user agents. Expect it to open the list v2 section.
- [x] Request `reference/swift/get-user` and the Kotlin reference root
with a bot user agent. Expect the intended heading.
- [x] Open the Storage quickstart guide and follow its upload reference
link. Expect the current JavaScript upload section.

## Linear

refs GROWTH-1293


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Reference pages now resolve legacy aliases and ambiguous slugs more
accurately, with canonical links that preserve explicit SDK versions.
* SDK version paths are recognized only when the full path segment
matches the version format, improving reference-page routing.

* **Documentation**
* Updated API reference links across authentication, storage, security,
and SDK guides to point to current pages.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-30 17:11:53 -07:00

91 lines
3.0 KiB
TypeScript

import { clientSdkIds } from '~/content/navigation.references'
import { BASE_PATH } from '~/lib/constants'
import MARKDOWN_SLUGS from '~/public/markdown/manifest.json'
import { negotiateMarkdown } from 'common/markdown-negotiation'
import { isbot } from 'isbot'
import { NextResponse, type NextRequest } from 'next/server'
const REFERENCE_PATH = `${BASE_PATH ?? ''}/reference`
const GUIDES_PATH = `${BASE_PATH ?? ''}/guides`
const GUIDES_MARKDOWN_SLUGS = new Set(MARKDOWN_SLUGS)
export function middleware(request: NextRequest) {
const url = new URL(request.url)
const { pathname } = url
if (pathname.startsWith(GUIDES_PATH + '/')) {
const isMdSuffix = pathname.endsWith('.md')
const slug = pathname.replace(`${GUIDES_PATH}/`, '').replace(/\.md$/, '')
const decision = negotiateMarkdown(
{ acceptHeader: request.headers.get('accept') ?? '' },
{ hasMarkdownVariant: GUIDES_MARKDOWN_SLUGS.has(slug), isMarkdownSuffix: isMdSuffix }
)
if (decision === 'not-acceptable') {
return new NextResponse('Not Acceptable', {
status: 406,
headers: { 'Cache-Control': 'no-store', Vary: 'Accept' },
})
}
if (decision === 'markdown') {
const rewriteUrl = new URL(url)
rewriteUrl.pathname = `${BASE_PATH ?? ''}/api/guides-md/${slug}`
return NextResponse.rewrite(rewriteUrl)
}
}
if (!pathname.startsWith(REFERENCE_PATH)) {
return NextResponse.next()
}
if (isbot(request.headers.get('user-agent'))) {
let [, lib, maybeVersion, ...slug] = pathname.replace(REFERENCE_PATH, '').split('/')
if (clientSdkIds.includes(lib)) {
const version = /^v\d+$/.test(maybeVersion) ? maybeVersion : undefined
if (!version) {
slug = [maybeVersion, ...slug]
}
if (slug.length > 0) {
const rewriteUrl = new URL(url)
rewriteUrl.pathname = (BASE_PATH ?? '') + '/api/crawlers'
return NextResponse.rewrite(rewriteUrl)
}
}
}
const [, lib, maybeVersion] = pathname.replace(REFERENCE_PATH, '').split('/')
if (lib === 'cli') {
const rewritePath = [REFERENCE_PATH, 'cli'].join('/')
return NextResponse.rewrite(new URL(rewritePath, request.url))
}
// Spike (DOCS-1268): only the bare /reference/api needs normalizing now.
// /reference/api/<slug> has its own statically generated page — don't
// collapse it back to the monolith.
if (lib === 'api' && !maybeVersion) {
const rewritePath = [REFERENCE_PATH, 'api'].join('/')
return NextResponse.rewrite(new URL(rewritePath, request.url))
}
if (lib?.startsWith('self-hosting-')) {
const rewritePath = [REFERENCE_PATH, lib].join('/')
return NextResponse.rewrite(new URL(rewritePath, request.url))
}
if (clientSdkIds.includes(lib)) {
const version = /^v\d+$/.test(maybeVersion) ? maybeVersion : null
const rewritePath = [REFERENCE_PATH, lib, version].filter(Boolean).join('/')
return NextResponse.rewrite(new URL(rewritePath, request.url))
}
return NextResponse.next()
}
export const config = {
matcher: ['/reference/:path*', '/guides/:path*'],
}