mirror of
https://github.com/CopilotKit/CopilotKit.git
synced 2026-09-14 16:26:20 +08:00
docs: 301 retired /ag-ui mirror paths to docs.ag-ui.com
#7092 deleted showcase/shell-docs/src/content/ag-ui/ and its route. The 96 mirror URLs still in the production sitemap carry search ranking; without redirects they become 404s once the site rebuilds. 87 of the 96 slugs exist unchanged on docs.ag-ui.com, so a wildcard carries them. The nine that do not are listed explicitly ahead of the wildcard so a reader never lands on an upstream 404.
This commit is contained in:
@@ -294,6 +294,91 @@ const CHANNEL_REDIRECTS: PermanentRedirect[] = [
|
||||
...CHANNEL_ROOT_REDIRECTS,
|
||||
];
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Retired AG-UI mirror (`/ag-ui/*`).
|
||||
//
|
||||
// The mirrored AG-UI docs tree was deleted in #7092; AG-UI documentation is
|
||||
// canonical at docs.ag-ui.com. 96 `/ag-ui/*` URLs were in the production
|
||||
// sitemap and carry accumulated search ranking, so they are handed to the
|
||||
// upstream site with a 301 rather than left to 404.
|
||||
//
|
||||
// The mirror's slugs match upstream 1:1 for 87 of the 96 paths (verified by
|
||||
// HTTP probe against docs.ag-ui.com), so a single wildcard carries the bulk
|
||||
// of the tree. The nine paths with no upstream page are listed explicitly
|
||||
// ABOVE the wildcard so the specific rule wins.
|
||||
//
|
||||
// Suffix handling: a raw Markdown request reaches redirects before the
|
||||
// `.md` / `.mdx` rewrite, and upstream serves `.md` but NOT `.mdx`
|
||||
// (docs.ag-ui.com/concepts/agents.md -> 200, .../concepts/agents.mdx -> 404),
|
||||
// so both suffixes are collapsed onto upstream `.md`.
|
||||
// ----------------------------------------------------------------
|
||||
const AG_UI_DOCS_ORIGIN = "https://docs.ag-ui.com";
|
||||
|
||||
function agUiMirrorRedirects(
|
||||
mirrorPath: string,
|
||||
upstreamPath: string,
|
||||
): PermanentRedirect[] {
|
||||
return [
|
||||
{
|
||||
source: `/ag-ui${mirrorPath}.mdx`,
|
||||
destination: `${AG_UI_DOCS_ORIGIN}${upstreamPath}.md`,
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: `/ag-ui${mirrorPath}.md`,
|
||||
destination: `${AG_UI_DOCS_ORIGIN}${upstreamPath}.md`,
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: `/ag-ui${mirrorPath}`,
|
||||
destination: `${AG_UI_DOCS_ORIGIN}${upstreamPath}`,
|
||||
permanent: true,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
// Retired mirror paths whose slug does NOT exist upstream. Each destination
|
||||
// was probed and returns 200; the bare wildcard below would send these to an
|
||||
// upstream 404, which is worse for readers than the page simply being gone.
|
||||
const AG_UI_MIRROR_EXCEPTIONS = [
|
||||
// Both drafts graduated into the Concepts section upstream.
|
||||
["/drafts/interrupts", "/concepts/interrupts"],
|
||||
["/drafts/multimodal-messages", "/concepts/messages"],
|
||||
// The Dart SDK kept every page except its client overview.
|
||||
["/sdk/dart/client/overview", "/sdk/dart/overview"],
|
||||
// Upstream reduced the Rust SDK to a single overview page.
|
||||
["/sdk/rust/client/agent-trait", "/sdk/rust/overview"],
|
||||
["/sdk/rust/client/http-agent", "/sdk/rust/overview"],
|
||||
["/sdk/rust/client/subscriber", "/sdk/rust/overview"],
|
||||
["/sdk/rust/core/events", "/sdk/rust/overview"],
|
||||
["/sdk/rust/core/overview", "/sdk/rust/overview"],
|
||||
["/sdk/rust/core/types", "/sdk/rust/overview"],
|
||||
] as const;
|
||||
|
||||
const AG_UI_MIRROR_REDIRECTS: PermanentRedirect[] = [
|
||||
...AG_UI_MIRROR_EXCEPTIONS.flatMap(([mirrorPath, upstreamPath]) =>
|
||||
agUiMirrorRedirects(mirrorPath, upstreamPath),
|
||||
),
|
||||
// The mirror root rendered the AG-UI introduction page.
|
||||
...agUiMirrorRedirects("", "/introduction"),
|
||||
// Everything else keeps its slug upstream.
|
||||
{
|
||||
source: "/ag-ui/:path*.mdx",
|
||||
destination: `${AG_UI_DOCS_ORIGIN}/:path*.md`,
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/ag-ui/:path*.md",
|
||||
destination: `${AG_UI_DOCS_ORIGIN}/:path*.md`,
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/ag-ui/:path*",
|
||||
destination: `${AG_UI_DOCS_ORIGIN}/:path*`,
|
||||
permanent: true,
|
||||
},
|
||||
];
|
||||
|
||||
// NEXT_PUBLIC_BASE_URL and NEXT_PUBLIC_SHELL_URL are read at REQUEST
|
||||
// time by the server `getRuntimeConfig()` reader and injected into the
|
||||
// client via `window.__SHOWCASE_CONFIG__` from the root layout. They
|
||||
@@ -375,6 +460,9 @@ const nextConfig: NextConfig = {
|
||||
// OSS-615: legacy global, scoped, generated-reference, and Bots URLs
|
||||
// resolve directly to the canonical Slack/Teams guide trees.
|
||||
...CHANNEL_REDIRECTS,
|
||||
// OSS: the retired `/ag-ui/*` mirror hands its search equity to the
|
||||
// canonical upstream docs at docs.ag-ui.com (see AG_UI_MIRROR_REDIRECTS).
|
||||
...AG_UI_MIRROR_REDIRECTS,
|
||||
{
|
||||
// Built-in agent is the default framework, so its overview page
|
||||
// is the docs root. Avoid surfacing a redundant "Introduction"
|
||||
|
||||
@@ -56,4 +56,87 @@ describe("next.config redirects", () => {
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it("hands the retired /ag-ui mirror to docs.ag-ui.com", async () => {
|
||||
vi.stubEnv("NEXT_PUBLIC_BASE_URL", "http://localhost:3003");
|
||||
vi.stubEnv("NEXT_PUBLIC_SHELL_URL", "http://localhost:3000");
|
||||
|
||||
const nextConfig = (await import("../../../next.config")).default;
|
||||
const redirects = (await nextConfig.redirects?.()) ?? [];
|
||||
|
||||
expect(redirects).toEqual(
|
||||
expect.arrayContaining([
|
||||
// Bulk of the mirror keeps its slug upstream.
|
||||
{
|
||||
source: "/ag-ui/:path*",
|
||||
destination: "https://docs.ag-ui.com/:path*",
|
||||
permanent: true,
|
||||
},
|
||||
// Upstream serves `.md` but not `.mdx`, so both collapse onto `.md`.
|
||||
{
|
||||
source: "/ag-ui/:path*.md",
|
||||
destination: "https://docs.ag-ui.com/:path*.md",
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/ag-ui/:path*.mdx",
|
||||
destination: "https://docs.ag-ui.com/:path*.md",
|
||||
permanent: true,
|
||||
},
|
||||
// Mirror root rendered the upstream introduction page.
|
||||
{
|
||||
source: "/ag-ui",
|
||||
destination: "https://docs.ag-ui.com/introduction",
|
||||
permanent: true,
|
||||
},
|
||||
// Paths with no upstream equivalent land on the nearest live page.
|
||||
{
|
||||
source: "/ag-ui/drafts/interrupts",
|
||||
destination: "https://docs.ag-ui.com/concepts/interrupts",
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/ag-ui/drafts/multimodal-messages",
|
||||
destination: "https://docs.ag-ui.com/concepts/messages",
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/ag-ui/sdk/dart/client/overview",
|
||||
destination: "https://docs.ag-ui.com/sdk/dart/overview",
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/ag-ui/sdk/rust/core/types",
|
||||
destination: "https://docs.ag-ui.com/sdk/rust/overview",
|
||||
permanent: true,
|
||||
},
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it("orders every /ag-ui exception ahead of the catch-all", async () => {
|
||||
vi.stubEnv("NEXT_PUBLIC_BASE_URL", "http://localhost:3003");
|
||||
vi.stubEnv("NEXT_PUBLIC_SHELL_URL", "http://localhost:3000");
|
||||
|
||||
const nextConfig = (await import("../../../next.config")).default;
|
||||
const redirects = (await nextConfig.redirects?.()) ?? [];
|
||||
|
||||
const catchAll = redirects.findIndex(
|
||||
(redirect) => redirect.source === "/ag-ui/:path*",
|
||||
);
|
||||
expect(catchAll).toBeGreaterThan(-1);
|
||||
|
||||
const exceptions = redirects.filter(
|
||||
(redirect) =>
|
||||
typeof redirect.destination === "string" &&
|
||||
redirect.destination.startsWith("https://docs.ag-ui.com") &&
|
||||
!redirect.source.includes(":path*"),
|
||||
);
|
||||
// 9 retired paths with no upstream equivalent + the mirror root, each
|
||||
// emitted bare, `.md` and `.mdx`.
|
||||
expect(exceptions).toHaveLength(30);
|
||||
for (const exception of exceptions) {
|
||||
expect(redirects.indexOf(exception)).toBeLessThan(catchAll);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user