Files
Pranay Prakash 8a872529fe docs: make /worlds the canonical home for World docs (#2934)
* docs: make /worlds the canonical home for World docs

The world pages (Local/Postgres/Vercel) and Building a World were
duplicated inside the v4 and v5 docs trees while /worlds/[id] rendered
the v4 copy — hiding v5-only content like multi-region and leaving two
diverging sources of truth.

- Move world docs to an unversioned docs/content/worlds/ collection
  (based on the v5 copies, with inline 4.x callouts for factory naming
  and 5.x-only env vars), rendered at /worlds/*
- Add /worlds/building-a-world; flatten the docs Deploying section to a
  single intro page and drop its Rocket icon
- Point every link, frontmatter ref, and worlds-manifest docs field at
  /worlds/*; add redirects for the removed v5 and building-a-world URLs
- Keep world docs on agent-facing surfaces: search, llms.txt,
  sitemap.md/.xml, and .md exports now serve the worlds collection
- Extend the docs link linter to validate worlds pages (with heading
  anchors) and their outgoing links

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>

* docs: version the world docs like the docs trees (v4/v5 switcher)

Instead of a single unversioned copy, world docs now follow the same
versioning strategy as the docs pages: content/worlds/v4 is served at
/worlds/* (current) and content/worlds/v5 at /v5/worlds/*, restoring the
original per-version content. Each world detail page (and Building a
World) renders the docs version switcher — the worlds listing page has
no natural home for it, so it lives on the world pages themselves.

- Render-time href rewriting on v5 pages now covers /worlds/... links
  (shared rewriteHrefForVersion helper, also used by the v5 docs and
  cookbook routes), and the markdown-export rewrite does the same
- v5 world pages are noindexed with a canonical to /worlds/<id>;
  community worlds stay unversioned (/v5/worlds/<id> redirects)
- /v5/docs/deploying/world/* redirects now land on /v5/worlds/*;
  /v5/worlds and /v5/worlds/compare redirect to the unversioned pages
- Link linter models the versioned worlds URL spaces (v5 pages resolve
  /worlds hrefs against the v5 collection); sitemap.md and the .md
  export routes cover /v5/worlds/*

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>

* docs: fix v4 multi-region anchor and tighten version-prefix matching

Address PR review:
- The v4 Deploying page linked /worlds/vercel#multi-region, but the
  Multi-region section only exists on the v5 world page; use the
  explicit cross-version /v5/worlds/vercel#multi-region link (this was
  the Docs Links CI failure)
- rewriteHrefForVersion now uses the boundary-checked hasPathPrefix
  (shared leaf module lib/geistdocs/path-prefix.ts, also used by
  source.ts) instead of bare startsWith
- buildVersionUrl's shared-route fast path is segment-based rather than
  substring includes()

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>

---------

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 17:15:07 +07:00

84 lines
2.7 KiB
TypeScript

export type DocsVersionId = 'v4' | 'v5';
export interface DocsVersion {
id: DocsVersionId;
label: string;
subtitle: string;
prefix: string;
preRelease: boolean;
}
export const VERSIONS: DocsVersion[] = [
{
id: 'v5',
label: 'v5 (Pre-release)',
subtitle: 'Workflow 5.x',
prefix: '/v5',
preRelease: true,
},
{
id: 'v4',
label: 'v4 (Latest)',
subtitle: 'Workflow 4.x',
prefix: '',
preRelease: false,
},
];
export const PRE_RELEASE_VERSION: DocsVersion = VERSIONS[0];
export const LATEST_VERSION: DocsVersion = VERSIONS[1];
/**
* Derive the active docs version from a pathname. Matches `/v5/...` (or
* `/<lang>/v5/...` once locale prefix is applied) against the pre-release
* prefix; everything else is v4.
*/
export function getVersionFromPathname(pathname: string): DocsVersion {
// The v5 segment sits either at the root (default locale hidden) or right
// after a locale segment — both cases are covered by checking positions
// 0 and 1.
const segments = pathname.split('/').filter(Boolean);
if (segments[0] === 'v5' || segments[1] === 'v5') {
return PRE_RELEASE_VERSION;
}
return LATEST_VERSION;
}
/**
* Build a URL for the same page under a different version. Preserves the
* trailing path after `/docs/` and any locale prefix.
*
* `/docs/...`, `/cookbook/...`, and `/worlds/...` paths are version-specific.
* All other routes are shared across versions and are returned unchanged.
*
* `usePathname()` can return either `/docs/...` (default locale hidden by
* the i18n middleware) or `/<locale>/docs/...` (non-default locale shown).
* We detect the locale segment by checking whether segment 0 is a
* structural path token (`docs` or `v5`) rather than assuming position.
*/
export function buildVersionUrl(
pathname: string,
targetVersion: DocsVersion
): string {
const segments = pathname.split('/').filter(Boolean);
// Structural segments are path tokens that are never locale prefixes.
const isStructural = (s: string | undefined) =>
s === 'docs' || s === 'v5' || s === 'cookbook' || s === 'worlds';
// Versioned routes carry a structural token at the root or right after a
// locale segment; everything else is shared and returned unchanged.
if (!isStructural(segments[0]) && !isStructural(segments[1])) {
return pathname;
}
const localeSegments =
segments[0] && !isStructural(segments[0]) ? segments.slice(0, 1) : [];
let rest = segments.slice(localeSegments.length);
if (rest[0] === 'v5') rest = rest.slice(1);
const prefixSegments = targetVersion.prefix
? [targetVersion.prefix.replace(/^\//, '')]
: [];
const joined = [...localeSegments, ...prefixSegments, ...rest].join('/');
return `/${joined}`.replace(/\/+$/, '') || '/';
}