Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .agents/skills/documentation/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ The tree is organized by the surface a reader is working with:
- `swagger/` covers the Swagger document, its editor and chat surfaces, and generation strategy;
- `e2e/` covers why generated e2e exists, how to develop against it, and benchmarking.

The site carries no beginner walkthrough at present. A ten-step tutorial once sat at `website/src/content/tutorial/**` and published at https://nestia.io/tutorial, but it shipped unfinished and was withdrawn; the restoration is pending in its own pull request. Do not reintroduce `/tutorial` links into `docs/**` until that work lands.
The ten-step ordered walkthrough is not under `docs/`. It sits at `website/src/content/tutorial/**` and publishes at https://nestia.io/tutorial as a top-level page of its own, numbered in its `_meta.ts` from `1. Welcome` through `10. Where to Next`. `src/content/docs/_meta.ts` keeps an `href` entry pointing at it so the reference section still links back to the walkthrough it defers to, and `public/docs/tutorial/**` holds redirect stubs for the old nested URLs.

Every docs folder and `src/content/` itself carries a `_meta.ts` that Nextra reads in declaration order for sidebar order and labels. Two of them — `src/content/_meta.ts` and `src/content/docs/_meta.ts` — use the typed `satisfies MetaRecord` form; the four per-feature folders use the plain default-exported object. Beyond ordering, those meta files carry `type: "page" | "menu" | "separator"` for navigation structure, `display: "hidden"` to keep a page reachable while removing it from the sidebar, and `href` to point an entry at a different target — mostly internal routes such as `/api`, `/blog`, and `/editor`, and occasionally an external URL. Update the matching `_meta.ts` whenever a guide is added, renamed, moved, hidden, or exposed.
Every docs folder and `src/content/` itself carries a `_meta.ts` that Nextra reads in declaration order for sidebar order and labels. Three of them — `src/content/_meta.ts`, `src/content/docs/_meta.ts`, and `src/content/tutorial/_meta.ts` — use the typed `satisfies MetaRecord` form; the four per-feature folders use the plain default-exported object. Beyond ordering, those meta files carry `type: "page" | "menu" | "separator"` for navigation structure, `display: "hidden"` to keep a page reachable while removing it from the sidebar, and `href` to point an entry at a different target — mostly internal routes such as `/api`, `/blog`, and `/editor`, and occasionally an external URL. Update the matching `_meta.ts` whenever a guide is added, renamed, moved, hidden, or exposed.

When emitted code is the point, pair the TypeScript source with the generated SDK, Swagger, or e2e output in side-by-side tabs so the reader sees what the transform produces. Back performance claims with a specific `benchmark/results/**` figure instead of an adjective.

Expand Down
79 changes: 79 additions & 0 deletions website/build/tutorial-redirects.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
"""Regenerate the redirect stubs under `public/docs/tutorial/`.

The tutorial moved from `/docs/tutorial` to the top-level `/tutorial`. The
site is a static export (`output: "export"`), so there is no server to issue
a 301 and `next.config.ts`'s `rewrites` do not apply either -- Next warns
about exactly that at build time. Files under `public/` are copied to the
output tree verbatim, which makes them the one mechanism that still answers
at the old paths.

Run from `website/`:

python build/tutorial-redirects.py

Delete the whole `public/docs/tutorial` tree once the old URLs stop
receiving traffic; nothing else depends on it.
"""

import io
import os

# Mirrors the keys of `src/content/tutorial/_meta.ts`. "" is the index.
CHAPTERS = [
"",
"quickstart",
"endpoint",
"validate",
"sdk",
"consume",
"swagger",
"e2e",
"simulator",
"next",
]

BASE = os.path.join("public", "docs", "tutorial")

TEMPLATE = """<!doctype html>
<!--
Redirect stub. The tutorial moved from /docs/tutorial to the top-level
/tutorial, and this site is a static export, so there is no server to issue
a 301 -- these files stand in for one at the old paths.

Generated by build/tutorial-redirects.py. Delete the whole
public/docs/tutorial tree once the old URLs no longer receive traffic.
-->
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Moved to {dest}</title>
<link rel="canonical" href="https://nestia.io{dest}" />
<meta name="robots" content="noindex" />
<meta http-equiv="refresh" content="0; url={dest}" />
<script>
// Carries the fragment across, which a meta refresh drops -- several
// reference pages deep-link to anchors such as #4-handle-errors.
location.replace("{dest}" + location.hash);
</script>
</head>
<body>
<p>This page moved to <a href="{dest}">{dest}</a>.</p>
</body>
</html>
"""


def main() -> None:
for chapter in CHAPTERS:
dest = "/tutorial/" + (chapter + "/" if chapter else "")
directory = os.path.join(BASE, chapter) if chapter else BASE
os.makedirs(directory, exist_ok=True)
path = os.path.join(directory, "index.html")
io.open(path, "w", encoding="utf-8", newline="").write(
TEMPLATE.format(dest=dest)
)
print("wrote", path)


if __name__ == "__main__":
main()
10 changes: 9 additions & 1 deletion website/next-sitemap.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,15 @@ const sitemapConfig = {
transform: async (config, url) => ({
loc: url,
changefreq: url === "/" ? "daily" : "weekly",
priority: url === "/" ? 1.0 : url.startsWith("/docs") ? 0.8 : 0.6,
// The tutorial is the entry path for a new reader, so it ranks with the
// guides rather than falling to the catch-all now that it is no longer
// nested under `/docs`.
priority:
url === "/"
? 1.0
: url.startsWith("/docs") || url.startsWith("/tutorial")
? 0.8
: 0.6,
lastmod: new Date().toISOString(),
}),
};
Expand Down
26 changes: 26 additions & 0 deletions website/public/docs/tutorial/consume/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!doctype html>
<!--
Redirect stub. The tutorial moved from /docs/tutorial to the top-level
/tutorial, and this site is a static export, so there is no server to issue
a 301 -- these files stand in for one at the old paths.

Generated by build/tutorial-redirects.py. Delete the whole
public/docs/tutorial tree once the old URLs no longer receive traffic.
-->
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Moved to /tutorial/consume/</title>
<link rel="canonical" href="https://nestia.io/tutorial/consume/" />
<meta name="robots" content="noindex" />
<meta http-equiv="refresh" content="0; url=/tutorial/consume/" />
<script>
// Carries the fragment across, which a meta refresh drops -- several
// reference pages deep-link to anchors such as #4-handle-errors.
location.replace("/tutorial/consume/" + location.hash);
</script>
</head>
<body>
<p>This page moved to <a href="/tutorial/consume/">/tutorial/consume/</a>.</p>
</body>
</html>
26 changes: 26 additions & 0 deletions website/public/docs/tutorial/e2e/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!doctype html>
<!--
Redirect stub. The tutorial moved from /docs/tutorial to the top-level
/tutorial, and this site is a static export, so there is no server to issue
a 301 -- these files stand in for one at the old paths.

Generated by build/tutorial-redirects.py. Delete the whole
public/docs/tutorial tree once the old URLs no longer receive traffic.
-->
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Moved to /tutorial/e2e/</title>
<link rel="canonical" href="https://nestia.io/tutorial/e2e/" />
<meta name="robots" content="noindex" />
<meta http-equiv="refresh" content="0; url=/tutorial/e2e/" />
<script>
// Carries the fragment across, which a meta refresh drops -- several
// reference pages deep-link to anchors such as #4-handle-errors.
location.replace("/tutorial/e2e/" + location.hash);
</script>
</head>
<body>
<p>This page moved to <a href="/tutorial/e2e/">/tutorial/e2e/</a>.</p>
</body>
</html>
26 changes: 26 additions & 0 deletions website/public/docs/tutorial/endpoint/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!doctype html>
<!--
Redirect stub. The tutorial moved from /docs/tutorial to the top-level
/tutorial, and this site is a static export, so there is no server to issue
a 301 -- these files stand in for one at the old paths.

Generated by build/tutorial-redirects.py. Delete the whole
public/docs/tutorial tree once the old URLs no longer receive traffic.
-->
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Moved to /tutorial/endpoint/</title>
<link rel="canonical" href="https://nestia.io/tutorial/endpoint/" />
<meta name="robots" content="noindex" />
<meta http-equiv="refresh" content="0; url=/tutorial/endpoint/" />
<script>
// Carries the fragment across, which a meta refresh drops -- several
// reference pages deep-link to anchors such as #4-handle-errors.
location.replace("/tutorial/endpoint/" + location.hash);
</script>
</head>
<body>
<p>This page moved to <a href="/tutorial/endpoint/">/tutorial/endpoint/</a>.</p>
</body>
</html>
26 changes: 26 additions & 0 deletions website/public/docs/tutorial/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!doctype html>
<!--
Redirect stub. The tutorial moved from /docs/tutorial to the top-level
/tutorial, and this site is a static export, so there is no server to issue
a 301 -- these files stand in for one at the old paths.

Generated by build/tutorial-redirects.py. Delete the whole
public/docs/tutorial tree once the old URLs no longer receive traffic.
-->
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Moved to /tutorial/</title>
<link rel="canonical" href="https://nestia.io/tutorial/" />
<meta name="robots" content="noindex" />
<meta http-equiv="refresh" content="0; url=/tutorial/" />
<script>
// Carries the fragment across, which a meta refresh drops -- several
// reference pages deep-link to anchors such as #4-handle-errors.
location.replace("/tutorial/" + location.hash);
</script>
</head>
<body>
<p>This page moved to <a href="/tutorial/">/tutorial/</a>.</p>
</body>
</html>
26 changes: 26 additions & 0 deletions website/public/docs/tutorial/next/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!doctype html>
<!--
Redirect stub. The tutorial moved from /docs/tutorial to the top-level
/tutorial, and this site is a static export, so there is no server to issue
a 301 -- these files stand in for one at the old paths.

Generated by build/tutorial-redirects.py. Delete the whole
public/docs/tutorial tree once the old URLs no longer receive traffic.
-->
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Moved to /tutorial/next/</title>
<link rel="canonical" href="https://nestia.io/tutorial/next/" />
<meta name="robots" content="noindex" />
<meta http-equiv="refresh" content="0; url=/tutorial/next/" />
<script>
// Carries the fragment across, which a meta refresh drops -- several
// reference pages deep-link to anchors such as #4-handle-errors.
location.replace("/tutorial/next/" + location.hash);
</script>
</head>
<body>
<p>This page moved to <a href="/tutorial/next/">/tutorial/next/</a>.</p>
</body>
</html>
26 changes: 26 additions & 0 deletions website/public/docs/tutorial/quickstart/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!doctype html>
<!--
Redirect stub. The tutorial moved from /docs/tutorial to the top-level
/tutorial, and this site is a static export, so there is no server to issue
a 301 -- these files stand in for one at the old paths.

Generated by build/tutorial-redirects.py. Delete the whole
public/docs/tutorial tree once the old URLs no longer receive traffic.
-->
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Moved to /tutorial/quickstart/</title>
<link rel="canonical" href="https://nestia.io/tutorial/quickstart/" />
<meta name="robots" content="noindex" />
<meta http-equiv="refresh" content="0; url=/tutorial/quickstart/" />
<script>
// Carries the fragment across, which a meta refresh drops -- several
// reference pages deep-link to anchors such as #4-handle-errors.
location.replace("/tutorial/quickstart/" + location.hash);
</script>
</head>
<body>
<p>This page moved to <a href="/tutorial/quickstart/">/tutorial/quickstart/</a>.</p>
</body>
</html>
26 changes: 26 additions & 0 deletions website/public/docs/tutorial/sdk/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!doctype html>
<!--
Redirect stub. The tutorial moved from /docs/tutorial to the top-level
/tutorial, and this site is a static export, so there is no server to issue
a 301 -- these files stand in for one at the old paths.

Generated by build/tutorial-redirects.py. Delete the whole
public/docs/tutorial tree once the old URLs no longer receive traffic.
-->
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Moved to /tutorial/sdk/</title>
<link rel="canonical" href="https://nestia.io/tutorial/sdk/" />
<meta name="robots" content="noindex" />
<meta http-equiv="refresh" content="0; url=/tutorial/sdk/" />
<script>
// Carries the fragment across, which a meta refresh drops -- several
// reference pages deep-link to anchors such as #4-handle-errors.
location.replace("/tutorial/sdk/" + location.hash);
</script>
</head>
<body>
<p>This page moved to <a href="/tutorial/sdk/">/tutorial/sdk/</a>.</p>
</body>
</html>
26 changes: 26 additions & 0 deletions website/public/docs/tutorial/simulator/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!doctype html>
<!--
Redirect stub. The tutorial moved from /docs/tutorial to the top-level
/tutorial, and this site is a static export, so there is no server to issue
a 301 -- these files stand in for one at the old paths.

Generated by build/tutorial-redirects.py. Delete the whole
public/docs/tutorial tree once the old URLs no longer receive traffic.
-->
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Moved to /tutorial/simulator/</title>
<link rel="canonical" href="https://nestia.io/tutorial/simulator/" />
<meta name="robots" content="noindex" />
<meta http-equiv="refresh" content="0; url=/tutorial/simulator/" />
<script>
// Carries the fragment across, which a meta refresh drops -- several
// reference pages deep-link to anchors such as #4-handle-errors.
location.replace("/tutorial/simulator/" + location.hash);
</script>
</head>
<body>
<p>This page moved to <a href="/tutorial/simulator/">/tutorial/simulator/</a>.</p>
</body>
</html>
26 changes: 26 additions & 0 deletions website/public/docs/tutorial/swagger/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!doctype html>
<!--
Redirect stub. The tutorial moved from /docs/tutorial to the top-level
/tutorial, and this site is a static export, so there is no server to issue
a 301 -- these files stand in for one at the old paths.

Generated by build/tutorial-redirects.py. Delete the whole
public/docs/tutorial tree once the old URLs no longer receive traffic.
-->
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Moved to /tutorial/swagger/</title>
<link rel="canonical" href="https://nestia.io/tutorial/swagger/" />
<meta name="robots" content="noindex" />
<meta http-equiv="refresh" content="0; url=/tutorial/swagger/" />
<script>
// Carries the fragment across, which a meta refresh drops -- several
// reference pages deep-link to anchors such as #4-handle-errors.
location.replace("/tutorial/swagger/" + location.hash);
</script>
</head>
<body>
<p>This page moved to <a href="/tutorial/swagger/">/tutorial/swagger/</a>.</p>
</body>
</html>
26 changes: 26 additions & 0 deletions website/public/docs/tutorial/validate/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!doctype html>
<!--
Redirect stub. The tutorial moved from /docs/tutorial to the top-level
/tutorial, and this site is a static export, so there is no server to issue
a 301 -- these files stand in for one at the old paths.

Generated by build/tutorial-redirects.py. Delete the whole
public/docs/tutorial tree once the old URLs no longer receive traffic.
-->
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Moved to /tutorial/validate/</title>
<link rel="canonical" href="https://nestia.io/tutorial/validate/" />
<meta name="robots" content="noindex" />
<meta http-equiv="refresh" content="0; url=/tutorial/validate/" />
<script>
// Carries the fragment across, which a meta refresh drops -- several
// reference pages deep-link to anchors such as #4-handle-errors.
location.replace("/tutorial/validate/" + location.hash);
</script>
</head>
<body>
<p>This page moved to <a href="/tutorial/validate/">/tutorial/validate/</a>.</p>
</body>
</html>
5 changes: 3 additions & 2 deletions website/src/app/(docs)/[[...mdxPath]]/page.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ export default async function Page(props) {

// The sidebar wash is scoped to sidebar-bearing routes via `body:has(...)`
// in global.css, so the landing keeps a plain white gutter. This marker is
// what that selector keys off.
const isDocsPage = params.mdxPath?.[0] === "docs";
// what that selector keys off. `tutorial` is a top-level page of its own
// but still renders a sidebar, so it belongs in the same set as `docs`.
const isDocsPage = ["docs", "tutorial"].includes(params.mdxPath?.[0]);

return (
<Wrapper toc={toc} metadata={metadata}>
Expand Down
Loading
Loading