t3-code-android-nightly/.repos/alchemy-effect/website/astro.config.ts
Julius Marminge 6f9cea00ae
chore(refs): sync Effect and Alchemy references to 4.0.1 and beta.80 (#16170)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 13:22:30 -07:00

1713 lines
61 KiB
TypeScript

import mdx from "@astrojs/mdx";
import react from "@astrojs/react";
import sitemap from "@astrojs/sitemap";
import starlight from "@astrojs/starlight";
import tailwindcss from "@tailwindcss/vite";
import type { AstroIntegration } from "astro";
import { defineConfig } from "astro/config";
import {
copyEditor,
markdownBlocks,
markdownFiles,
type MarkdownFilesOptions,
} from "@alchemy.run/vite-plugin-copy-editor";
import { promises as fs } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import starlightBlog from "starlight-blog";
import { buildOutputChecks, noindexPaths } from "./plugins/build-output.ts";
import { jsdocCopyHandler, jsdocMarkdownStyle } from "./plugins/jsdoc-copy.ts";
import { JSDOC_COPY_STYLE } from "../scripts/jsdoc-blocks.ts";
import providersSidebar from "./src/generated/providers-sidebar.json" with { type: "json" };
import { rewriteReferenceLinks } from "./src/reference-links.ts";
/**
* Every provider has a docs hub: its reference tree renders inside the
* hub's "API Reference" group and its reference URLs belong to the hub tab
* (see docs-tabs.ts). The Reference tab is a directory — its sidebar is
* just the list of providers, each linking to its hub.
*/
function providersSidebarEntry() {
return {
label: "Reference",
collapsed: false,
items: [
{ label: "AWS", link: "/aws" },
{ label: "GCP", link: "/gcp" },
{ label: "Cloudflare", link: "/cloudflare" },
{ label: "Hetzner", link: "/hetzner" },
{ label: "Fly", link: "/fly" },
{ label: "Railway", link: "/railway" },
{ label: "PlanetScale", link: "/planetscale" },
{ label: "Neon", link: "/neon" },
{ label: "Prisma", link: "/prisma" },
{ label: "Better Auth", link: "/better-auth" },
{ label: "Axiom", link: "/axiom" },
{ label: "GitHub", link: "/github" },
{ label: "Stripe", link: "/stripe" },
{ label: "Docker", link: "/docker" },
{ label: "Kubernetes", link: "/kubernetes" },
{ label: "SQL", link: "/sql" },
{ label: "Command", link: "/command" },
{ label: "ACME", link: "/acme" },
],
};
}
/**
* A cloud hub's "API Reference" section: that provider's slice of the generated
* alphabetical list of reference pages below Guides.
* A hub that fronts several provider namespaces (e.g. SQL + Drizzle) passes
* them all and gets one merged API Reference group.
*
* @param {...string} providers Provider labels / directory names (e.g. "Cloudflare")
*/
function providerResourcesEntry(...providers: string[]) {
const entryItems = (provider: string) => {
const group = providersSidebar.find((p) => p.label === provider);
if (group) return group.items;
return [
{ autogenerate: { directory: `providers/${provider}`, collapsed: true } },
];
};
// A single provider's tree is inlined; a multi-namespace hub nests each
// provider under its own subgroup so same-named resources (SQL.D1 vs
// Drizzle.D1) stay distinguishable.
const items =
providers.length === 1
? entryItems(providers[0])
: providers.map((provider) => ({
label: provider,
collapsed: true,
items: entryItems(provider),
}));
return { label: "API Reference", collapsed: false, items };
}
function sortFrontendItems(items: readonly { label: string; link: string }[]) {
return items.toSorted((a, b) => {
const overviewOrder =
Number(b.label === "Overview") - Number(a.label === "Overview");
return (
overviewOrder ||
a.label.localeCompare(b.label, "en", { sensitivity: "base" })
);
});
}
type ReferenceItem =
| { label: string; link: string }
| { label: string; items: readonly ReferenceItem[] };
function providerApiReferenceEntry(...providers: string[]) {
const flatten = (
items: readonly ReferenceItem[],
prefix: readonly string[],
): { label: string; link: string }[] =>
items.flatMap((item) => {
if ("items" in item) {
// Generated category and service groups can share a name.
return flatten(
item.items,
prefix.at(-1) === item.label ? prefix : [...prefix, item.label],
);
}
return [{ label: [...prefix, item.label].join("."), link: item.link }];
});
return {
label: "API Reference",
collapsed: false,
items: providers.flatMap((provider) =>
flatten(
providersSidebar.find((group) => group.label === provider)?.items ?? [],
providers.length > 1 ? [provider] : [],
),
),
};
}
/**
* Copies `src/content/docs/**\/*.{md,mdx}` into the build output dir, preserving
* the directory layout but normalizing extensions to `.md`. This lets the worker
* serve raw markdown for clients (e.g. coding agents) that prefer it.
*/
/**
* Dev only: hand-written docs (`.md`/`.mdx`) are editable in the browser.
* Generated reference pages are edited through their JSDoc instead.
*/
const markdownCopy: MarkdownFilesOptions = {
root: fileURLToPath(new URL(".", import.meta.url)),
exclude: /\/src\/content\/docs\/providers\//,
style: JSDOC_COPY_STYLE,
};
function markdownCopyEditing(): AstroIntegration {
return {
name: "markdown-copy-editing",
hooks: {
"astro:config:setup": ({ command, config }) => {
if (command !== "dev") return;
const processor = config.markdown.processor as
| { name: string; options?: { mdastPlugins?: unknown[] } }
| undefined;
if (processor?.name !== "satteri" || !processor.options?.mdastPlugins) {
return;
}
processor.options.mdastPlugins.unshift(markdownBlocks(markdownCopy));
},
},
};
}
function copyMarkdownSources(): AstroIntegration {
return {
name: "copy-markdown-sources",
hooks: {
"astro:build:done": async ({ dir }) => {
const outDir = fileURLToPath(dir);
/**
* @param {string} srcDir
* @param {{ lowercase?: boolean }} [opts]
* @param {string} [relTo]
*/
async function walk(
srcDir: string,
opts: { lowercase?: boolean } = {},
relTo: string = srcDir,
) {
let entries;
try {
entries = await fs.readdir(srcDir, { withFileTypes: true });
} catch {
return;
}
// Fan the copies out — thousands of tiny files; serial awaits
// dominate the hook's runtime otherwise.
await Promise.all(
entries.map(async (entry) => {
const full = path.join(srcDir, entry.name);
if (entry.isDirectory()) return walk(full, opts, relTo);
if (!entry.isFile()) return;
const ext = path.extname(entry.name).toLowerCase();
if (ext !== ".md" && ext !== ".mdx") return;
let rel = path.relative(relTo, full);
rel = rel.slice(0, rel.length - ext.length) + ".md";
// Starlight lowercases doc URLs (e.g. CamelCase source
// `providers/AWS/S3/Bucket.md` is served at `/providers/aws/s3/bucket`),
// so the raw-markdown copy must live at the lowercased path or the
// worker's `/providers/aws/s3/bucket.md` lookup 404s into HTML.
if (opts.lowercase) rel = rel.toLowerCase();
const target = path.join(outDir, rel);
await fs.mkdir(path.dirname(target), { recursive: true });
await fs.writeFile(
target,
rewriteReferenceLinks(await fs.readFile(full, "utf8")),
);
}),
);
}
// Docs (Starlight content collection) — preserves nested layout under
// /content/docs/ → /<path>.md, lowercased to match Starlight's URLs.
await walk(
fileURLToPath(new URL("./src/content/docs/", import.meta.url)),
{ lowercase: true },
);
// Marketing pages (top-level Astro pages) — exposes /<page>.md so
// agents can fetch raw MDX via the worker's content negotiation. Astro
// page routing preserves case, so don't lowercase these.
await walk(fileURLToPath(new URL("./src/pages/", import.meta.url)));
},
},
};
}
export default defineConfig({
site: "https://alchemy.run",
redirects: {
"/infrastructure-as-effects": "/what-is-alchemy",
"/cli/login": "/cli/profile",
"/drizzle": "/sql",
"/drizzle/migrations": "/sql/drizzle/migrations",
"/better-auth/database-layers": "/better-auth/databases",
"/better-auth/migrations": "/better-auth/guides/migrations",
"/better-auth/upgrading": "/better-auth/upgrades/from-1-6-to-1-7",
},
prefetch: true,
devToolbar: { enabled: false },
trailingSlash: "ignore",
integrations: [
markdownCopyEditing(),
react(),
copyMarkdownSources(),
buildOutputChecks(),
sitemap({
filter: (page) =>
!page.endsWith(".html") &&
!page.endsWith(".md") &&
!page.endsWith(".mdx") &&
// Anything rendered with a `noindex` robots meta: `/auth/*`, and the
// example-less API reference stubs (see `collectNoindexPages`).
!noindexPaths.has(new URL(page).pathname) &&
// starlight-blog's paginated listings (`/blog/2/`, …) — thin pages
// that only repeat post excerpts; the posts themselves are listed.
!/\/blog\/\d+\/?$/.test(page),
}),
starlight({
title: "Alchemy",
favicon: "/favicon.svg",
customCss: ["./src/styles/global.css", "./src/styles/custom.css"],
components: {
ThemeProvider: "./src/components/ThemeProvider.astro",
Header: "./src/components/starlight/Header.astro",
Head: "./src/components/starlight/Head.astro",
Sidebar: "./src/components/starlight/Sidebar.astro",
MarkdownContent: "./src/components/starlight/MarkdownContent.astro",
},
prerender: true,
social: [
{
icon: "github",
label: "GitHub",
href: "https://github.com/alchemy-run/alchemy",
},
{
icon: "discord",
label: "Discord",
href: "/discord",
},
],
editLink: {
baseUrl: "https://github.com/alchemy-run/alchemy/edit/main/website",
},
// One top-level group per docs tab (see src/docs-tabs.ts). The
// docs-tabs-sidebar middleware swaps in the active tab's group, so
// the rendered sidebar only ever navigates within the current tab.
sidebar: [
{
label: "Core",
items: [
{ label: "Getting started", link: "/getting-started" },
{ label: "What is Alchemy?", link: "/what-is-alchemy" },
{
label: "Infrastructure as Code",
items: [
{ label: "Stacks", link: "/infrastructure-as-code/stack" },
{
label: "Resources",
link: "/infrastructure-as-code/resource",
},
{ label: "Actions", link: "/infrastructure-as-code/action" },
{
label: "Inputs & Outputs",
link: "/infrastructure-as-code/outputs",
},
{
label: "References",
link: "/infrastructure-as-code/references",
},
{
label: "Resource Lifecycle",
link: "/infrastructure-as-code/resource-lifecycle",
},
{
label: "Renaming Resources",
link: "/infrastructure-as-code/renaming",
},
{
label: "Providers",
link: "/infrastructure-as-code/provider",
},
{
label: "Custom Provider",
link: "/infrastructure-as-code/custom-provider",
},
{
label: "Local Provider",
link: "/infrastructure-as-code/local-provider",
},
],
},
{
label: "Infrastructure as Effects",
items: [
{
label: "Runtime",
link: "/infrastructure-as-effects/runtime",
},
{
label: "Bindings",
link: "/infrastructure-as-effects/binding",
},
{ label: "Layers", link: "/infrastructure-as-effects/layers" },
{
label: "Event Sources",
link: "/infrastructure-as-effects/event-sources",
},
{
label: "Sinks",
link: "/infrastructure-as-effects/sinks",
},
{ label: "Phases", link: "/infrastructure-as-effects/phases" },
{
label: "Circular Bindings",
link: "/infrastructure-as-effects/circular-bindings",
},
{
label: "Telemetry",
link: "/infrastructure-as-effects/telemetry",
},
{
label: "Custom Runtime",
link: "/infrastructure-as-effects/custom-runtime",
},
],
},
{
label: "APIs",
items: [
{
label: "Overview",
link: "/apis",
},
{
label: "Schemaless RPC",
link: "/apis/schemaless",
},
{
label: "Effect RPC",
link: "/apis/effect-rpc",
},
{
label: "Effect HTTP",
link: "/apis/effect-http",
},
],
},
{
label: "Environments",
items: [
{ label: "Stages", link: "/environments/stages" },
{ label: "Profiles", link: "/environments/profiles" },
{
label: "Auth Providers",
link: "/environments/auth-providers",
},
{
label: "Custom Auth Provider",
link: "/environments/custom-auth-provider",
},
{ label: "Secrets & Config", link: "/environments/secrets" },
{
label: "Secret providers",
link: "/environments/secret-providers",
},
{ label: "Doppler", link: "/environments/doppler" },
{ label: "Infisical", link: "/environments/infisical" },
{
label: "Local development",
link: "/environments/local-development",
},
{ label: "CI", link: "/environments/ci" },
],
},
{
label: "State Store",
items: [
{ label: "State Store", link: "/state-store" },
{
label: "Custom State Store",
link: "/state-store/custom-state-store",
},
],
},
{
label: "Project structure",
items: [
{
label: "File layout",
link: "/project-structure/file-layout",
},
{ label: "Monorepo", link: "/project-structure/monorepo" },
{
label: "Single Stack",
link: "/project-structure/monorepo-single-stack",
},
{
label: "Multiple Stacks",
link: "/project-structure/monorepo-multi-stack",
},
],
},
{
label: "Testing & observability",
items: [
{ label: "Testing", link: "/testing" },
{
label: "Testing a Stack",
link: "/testing/testing-a-stack",
},
{
label: "Testing Providers",
link: "/testing/testing-providers",
},
{ label: "Test harness", link: "/testing/test-harness" },
{ label: "Observability", link: "/testing/observability" },
],
},
],
},
{
label: "CLI",
items: [
{ label: "Overview", link: "/cli" },
{
label: "Deploy",
items: [
{ label: "deploy", link: "/cli/deploy" },
{ label: "plan", link: "/cli/plan" },
{ label: "destroy", link: "/cli/destroy" },
{ label: "drift", link: "/cli/drift" },
{ label: "nuke", link: "/cli/nuke" },
{
label: "Adopting Resources",
link: "/cli/adopting-resources",
},
],
},
{
label: "Develop",
items: [
{ label: "dev", link: "/cli/dev" },
{ label: "logs", link: "/cli/logs" },
],
},
{
label: "Auth",
items: [{ label: "profile", link: "/cli/profile" }],
},
{
label: "State",
items: [
{ label: "state", link: "/cli/state" },
{
label: "Inspecting State",
link: "/cli/inspecting-state",
},
],
},
{
label: "Providers",
items: [
{ label: "aws", link: "/cli/aws" },
{ label: "cloudflare", link: "/cli/cloudflare" },
],
},
{ label: "Migrating from v1", link: "/migrating-from-v1" },
],
},
{
label: "Cloudflare",
items: [
{ label: "Overview", link: "/cloudflare" },
{ label: "Setup", link: "/cloudflare/setup" },
{
label: "Local development",
link: "/cloudflare/local-development",
},
{
label: "Tutorial",
items: [{ autogenerate: { directory: "cloudflare/tutorial" } }],
},
{
label: "Compute",
items: [
{ label: "Workers", link: "/cloudflare/compute/workers" },
{
label: "Gradual deployments",
link: "/cloudflare/compute/gradual-deployments",
},
{
label: "Python Workers",
link: "/cloudflare/compute/python-workers",
},
{ label: "Workers Cache", link: "/cloudflare/compute/cache" },
{
label: "Rate limiting",
link: "/cloudflare/compute/rate-limiting",
},
{
label: "Browser rendering",
link: "/cloudflare/compute/browser-rendering",
},
{
label: "Worker Loader",
link: "/cloudflare/compute/worker-loader",
},
{
label: "Workers for Platforms",
link: "/cloudflare/compute/workers-for-platforms",
},
{
label: "Durable Objects",
link: "/cloudflare/compute/durable-objects",
},
{
label: "Cross-worker DOs",
link: "/cloudflare/compute/cross-worker-durable-object",
},
{
label: "WebSockets",
link: "/cloudflare/compute/hibernatable-websockets",
},
{ label: "Containers", link: "/cloudflare/compute/containers" },
{ label: "Workflows", link: "/cloudflare/compute/workflows" },
],
},
{
label: "Frontend",
items: sortFrontendItems([
{
label: "Overview",
link: "/cloudflare/frontend/frontends",
},
{ label: "Astro", link: "/cloudflare/frontend/astro" },
{ label: "Foldkit", link: "/cloudflare/frontend/foldkit" },
{
label: "Full-stack RPC + Drizzle",
link: "/cloudflare/frontend/full-stack-tanstack-rpc-drizzle",
},
{ label: "Next.js", link: "/cloudflare/frontend/nextjs" },
{ label: "Nuxt", link: "/cloudflare/frontend/nuxt" },
{ label: "Octane", link: "/cloudflare/frontend/octane" },
{
label: "React Router",
link: "/cloudflare/frontend/react-router",
},
{ label: "React SPA", link: "/cloudflare/frontend/vite-spa" },
{
label: "SolidStart",
link: "/cloudflare/frontend/solidstart",
},
{
label: "Static sites",
link: "/cloudflare/frontend/static-site",
},
{
label: "SvelteKit",
link: "/cloudflare/frontend/sveltekit",
},
{
label: "TanStack Start",
link: "/cloudflare/frontend/tanstack-start",
},
{ label: "Vite", link: "/cloudflare/frontend/vite" },
{ label: "Vinext", link: "/cloudflare/frontend/vinext" },
{ label: "Vue", link: "/cloudflare/frontend/vue" },
{ label: "Waku", link: "/cloudflare/frontend/waku" },
]),
},
{
label: "APIs",
items: [
{
label: "Schemaless RPC",
link: "/cloudflare/apis/schemaless-rpc",
},
{ label: "Effect RPC", link: "/cloudflare/apis/effect-rpc" },
{
label: "Effect HTTP",
link: "/cloudflare/apis/effect-http-api",
},
],
},
{
label: "Data",
items: [
{ label: "D1", link: "/cloudflare/data/d1" },
{ label: "KV", link: "/cloudflare/data/kv" },
{ label: "R2", link: "/cloudflare/data/r2" },
{
label: "R2 presigned URLs",
link: "/cloudflare/data/r2-presigned-urls",
},
{ label: "Hyperdrive", link: "/cloudflare/data/hyperdrive" },
{ label: "Drizzle ORM", link: "/cloudflare/data/drizzle" },
{ label: "Prisma ORM", link: "/cloudflare/data/prisma" },
{ label: "Drizzle on D1", link: "/cloudflare/data/d1-drizzle" },
{
label: "Shared database",
link: "/cloudflare/data/shared-database",
},
{
label: "Branch from a shared database",
link: "/cloudflare/data/branch-from-shared-database",
},
{ label: "Artifacts", link: "/cloudflare/data/artifacts" },
],
},
{
label: "Messaging & events",
items: [
{ label: "Queues", link: "/cloudflare/messaging/queues" },
{ label: "Cron triggers", link: "/cloudflare/messaging/cron" },
{
label: "GitHub events",
link: "/cloudflare/messaging/github-events",
},
],
},
{
label: "Email",
items: [
{ label: "Email", link: "/cloudflare/email" },
{
label: "Send & receive email",
link: "/cloudflare/email/send-and-receive",
},
],
},
{
label: "AI",
items: [
{ label: "Workers AI", link: "/cloudflare/ai/workers-ai" },
{ label: "AI Gateway", link: "/cloudflare/ai/ai-gateway" },
{
label: "AI Search (AutoRAG)",
link: "/cloudflare/ai/ai-search",
},
{ label: "Effect AI", link: "/cloudflare/ai/effect-ai" },
{ label: "Vectorize", link: "/cloudflare/ai/vectorize" },
{
label: "Release agent",
link: "/cloudflare/ai/release-agent",
},
],
},
{
label: "Security & secrets",
items: [
{
label: "Secrets & env",
link: "/cloudflare/security/secrets-env",
},
{
label: "Secrets Store",
link: "/cloudflare/security/secrets-store",
},
{ label: "Turnstile", link: "/cloudflare/security/turnstile" },
],
},
{
label: "Observability",
items: [
{
label: "Native tracing",
link: "/cloudflare/observability/workers-tracing",
},
{
label: "Axiom telemetry",
link: "/cloudflare/observability/axiom-observability",
},
{
label: "Analytics Engine",
link: "/cloudflare/observability/analytics-engine",
},
],
},
{
label: "Networking",
items: [
{
label: "Domains & DNS",
link: "/cloudflare/networking/domains",
},
{
label: "Custom domains & routes",
link: "/cloudflare/networking/custom-domains",
},
{ label: "Tunnel", link: "/cloudflare/networking/tunnel" },
],
},
providerResourcesEntry("Cloudflare"),
],
},
{
label: "AWS",
items: [
{ label: "Overview", link: "/aws" },
{ label: "Setup", link: "/aws/setup" },
{ label: "Local development", link: "/aws/local-development" },
{
label: "Tutorial",
items: [{ autogenerate: { directory: "aws/tutorial" } }],
},
{
label: "Compute",
items: [
{
label: "Choosing a runtime",
link: "/aws/compute/choosing-a-runtime",
},
{ label: "Lambda", link: "/aws/compute/lambda" },
{ label: "ECS", link: "/aws/compute/ecs" },
{ label: "EC2", link: "/aws/compute/ec2" },
{ label: "EKS", link: "/aws/compute/eks" },
{ label: "HyperPod", link: "/aws/compute/hyperpod" },
{ label: "Lambda MicroVMs", link: "/aws/compute/microvms" },
],
},
{
label: "Frontend",
items: sortFrontendItems([
{
label: "Overview",
link: "/aws/frontend/websites",
},
{ label: "Astro", link: "/aws/frontend/astro" },
{ label: "Foldkit", link: "/aws/frontend/foldkit" },
{
label: "Full-stack RPC + Drizzle",
link: "/aws/frontend/full-stack-tanstack-rpc-drizzle",
},
{ label: "Next.js", link: "/aws/frontend/nextjs" },
{ label: "Vinext", link: "/aws/frontend/vinext" },
{ label: "Nuxt", link: "/aws/frontend/nuxt" },
{ label: "Octane", link: "/aws/frontend/octane" },
{
label: "React Router",
link: "/aws/frontend/react-router",
},
{ label: "React SPA", link: "/aws/frontend/vite-spa" },
{
label: "SolidStart",
link: "/aws/frontend/solidstart",
},
{
label: "Static sites",
link: "/aws/frontend/static-site",
},
{
label: "SvelteKit",
link: "/aws/frontend/sveltekit",
},
{
label: "TanStack Start",
link: "/aws/frontend/tanstack-start",
},
{ label: "Vite", link: "/aws/frontend/vite" },
{ label: "Vue", link: "/aws/frontend/vue" },
{ label: "Waku", link: "/aws/frontend/waku" },
]),
},
{
label: "APIs",
items: [
{
label: "Schemaless RPC",
link: "/aws/apis/schemaless-rpc",
},
{ label: "Effect RPC", link: "/aws/apis/effect-rpc" },
{
label: "Effect HTTP",
link: "/aws/apis/effect-http-api",
},
{ label: "API Gateway", link: "/aws/apis/api-gateway" },
],
},
{
label: "Data",
items: [
{ label: "DynamoDB", link: "/aws/data/dynamodb" },
{ label: "S3", link: "/aws/data/s3" },
{ label: "RDS & Aurora", link: "/aws/data/rds" },
{ label: "Drizzle + Aurora", link: "/aws/data/drizzle-aurora" },
{ label: "Drizzle + DSQL", link: "/aws/data/drizzle-dsql" },
],
},
{
label: "AI",
items: [
{ label: "Bedrock & Effect AI", link: "/aws/ai/bedrock" },
],
},
{
label: "Messaging & events",
items: [
{ label: "SQS", link: "/aws/messaging/sqs" },
{ label: "SNS", link: "/aws/messaging/sns" },
{ label: "Kinesis", link: "/aws/messaging/kinesis" },
{
label: "EventBridge & Scheduler",
link: "/aws/messaging/eventbridge",
},
{
label: "DynamoDB Streams",
link: "/aws/messaging/dynamodb-streams",
},
{ label: "S3 events", link: "/aws/messaging/s3-events" },
],
},
{
label: "Email",
items: [
{
label: "Sending & managing email",
link: "/aws/email/sending",
},
{
label: "Receiving inbound email",
link: "/aws/email/receiving",
},
],
},
{
label: "Security & secrets",
items: [
{ label: "Secrets & env", link: "/aws/security/secrets-env" },
],
},
{
label: "Observability",
items: [
{ label: "CloudWatch", link: "/aws/observability/cloudwatch" },
],
},
{
label: "Networking",
items: [
{ label: "VPC & networking", link: "/aws/networking" },
{
label: "Custom domains",
link: "/aws/networking/custom-domains",
},
],
},
providerResourcesEntry("AWS"),
],
},
{
label: "GCP",
items: [
{ label: "Overview", link: "/gcp" },
{ label: "Setup", link: "/gcp/setup" },
{
label: "Guides",
items: [
{
label: "Serve an API on Cloud Run",
link: "/gcp/guides/cloud-run-api",
},
{
label: "Ingest events into BigQuery",
link: "/gcp/guides/event-pipeline",
},
{
label: "Cache with Memorystore",
link: "/gcp/guides/memorystore",
},
{
label: "How bindings grant IAM",
link: "/gcp/guides/bindings",
},
],
},
providerResourcesEntry("GCP"),
],
},
{
label: "Hetzner",
items: [
{ label: "Overview", link: "/hetzner" },
{ label: "Setup", link: "/hetzner/setup" },
{
label: "Tutorial",
items: [{ autogenerate: { directory: "hetzner/tutorial" } }],
},
{
label: "Frontend",
items: sortFrontendItems([
{
label: "Overview",
link: "/hetzner/frontend/websites",
},
{ label: "Astro", link: "/hetzner/frontend/astro" },
{ label: "Foldkit", link: "/hetzner/frontend/foldkit" },
{ label: "Next.js", link: "/hetzner/frontend/nextjs" },
{ label: "Vinext", link: "/hetzner/frontend/vinext" },
{ label: "Nuxt", link: "/hetzner/frontend/nuxt" },
{ label: "Octane", link: "/hetzner/frontend/octane" },
{
label: "React Router",
link: "/hetzner/frontend/react-router",
},
{
label: "SolidStart",
link: "/hetzner/frontend/solidstart",
},
{
label: "Static sites",
link: "/hetzner/frontend/static-site",
},
{
label: "SvelteKit",
link: "/hetzner/frontend/sveltekit",
},
{
label: "TanStack Start",
link: "/hetzner/frontend/tanstack-start",
},
{ label: "Vite", link: "/hetzner/frontend/vite" },
{ label: "Vocs", link: "/hetzner/frontend/vocs" },
{ label: "Waku", link: "/hetzner/frontend/waku" },
]),
},
{
label: "Compute",
items: [
{ label: "Servers", link: "/hetzner/compute/servers" },
{ label: "Services", link: "/hetzner/compute/services" },
],
},
{
label: "Data",
items: [
{ label: "Volumes", link: "/hetzner/data/volumes" },
{
label: "Drizzle + Postgres",
link: "/hetzner/data/drizzle-postgres",
},
],
},
{
label: "Networking",
items: [
{ label: "Networking", link: "/hetzner/networking" },
{ label: "DNS", link: "/hetzner/networking/dns" },
],
},
providerResourcesEntry("Hetzner"),
],
},
{
label: "Fly",
items: [
{ label: "Overview", link: "/fly" },
{ label: "Setup", link: "/fly/setup" },
{
label: "Tutorial",
items: [{ autogenerate: { directory: "fly/tutorial" } }],
},
{
label: "Frontend",
items: sortFrontendItems([
{
label: "Overview",
link: "/fly/frontend/websites",
},
{ label: "Astro", link: "/fly/frontend/astro" },
{ label: "Foldkit", link: "/fly/frontend/foldkit" },
{ label: "Next.js", link: "/fly/frontend/nextjs" },
{ label: "Vinext", link: "/fly/frontend/vinext" },
{ label: "Nuxt", link: "/fly/frontend/nuxt" },
{ label: "Octane", link: "/fly/frontend/octane" },
{
label: "React Router",
link: "/fly/frontend/react-router",
},
{
label: "SolidStart",
link: "/fly/frontend/solidstart",
},
{
label: "Static sites",
link: "/fly/frontend/static-site",
},
{
label: "SvelteKit",
link: "/fly/frontend/sveltekit",
},
{
label: "TanStack Start",
link: "/fly/frontend/tanstack-start",
},
{ label: "Vite", link: "/fly/frontend/vite" },
{ label: "Vocs", link: "/fly/frontend/vocs" },
{ label: "Waku", link: "/fly/frontend/waku" },
]),
},
{
label: "Compute",
items: [
{ label: "Apps", link: "/fly/compute/apps" },
{ label: "Machines", link: "/fly/compute/machines" },
{ label: "Services", link: "/fly/compute/services" },
{
label: "Connect Services",
link: "/fly/compute/connecting-services",
},
{
label: "Blue/green deployments",
link: "/fly/compute/deployments",
},
{ label: "Sprites", link: "/fly/compute/sprites" },
{ label: "Regions", link: "/fly/compute/regions" },
],
},
{
label: "Data",
items: [
{ label: "Volumes", link: "/fly/data/volumes" },
{ label: "Postgres", link: "/fly/data/postgres" },
{
label: "Drizzle + Postgres",
link: "/fly/data/drizzle-postgres",
},
{ label: "Redis", link: "/fly/data/redis" },
{ label: "Tigris", link: "/fly/data/tigris" },
{ label: "Secrets", link: "/fly/data/secrets" },
],
},
{
label: "Networking",
items: [{ label: "IPs & certificates", link: "/fly/networking" }],
},
providerResourcesEntry("Fly"),
],
},
{
label: "Railway",
items: [
{ label: "Overview", link: "/railway" },
{ label: "Setup", link: "/railway/setup" },
{
label: "Tutorial",
items: [{ autogenerate: { directory: "railway/tutorial" } }],
},
{
label: "Frontend",
items: sortFrontendItems([
{
label: "Overview",
link: "/railway/frontend/websites",
},
{ label: "Astro", link: "/railway/frontend/astro" },
{ label: "Foldkit", link: "/railway/frontend/foldkit" },
{ label: "Next.js", link: "/railway/frontend/nextjs" },
{ label: "Vinext", link: "/railway/frontend/vinext" },
{ label: "Nuxt", link: "/railway/frontend/nuxt" },
{ label: "Octane", link: "/railway/frontend/octane" },
{
label: "React Router",
link: "/railway/frontend/react-router",
},
{
label: "SolidStart",
link: "/railway/frontend/solidstart",
},
{
label: "Static sites",
link: "/railway/frontend/static-site",
},
{
label: "SvelteKit",
link: "/railway/frontend/sveltekit",
},
{
label: "TanStack Start",
link: "/railway/frontend/tanstack-start",
},
{ label: "Vite", link: "/railway/frontend/vite" },
{ label: "Vocs", link: "/railway/frontend/vocs" },
{ label: "Waku", link: "/railway/frontend/waku" },
]),
},
{
label: "Compute",
items: [
{ label: "Projects", link: "/railway/compute/projects" },
{ label: "Services", link: "/railway/compute/services" },
{
label: "Functions, templates & VMs",
link: "/railway/compute/functions",
},
{
label: "Environments",
link: "/railway/compute/environments",
},
{ label: "Regions", link: "/railway/compute/regions" },
],
},
{
label: "Data",
items: [
{ label: "Volumes", link: "/railway/data/volumes" },
{ label: "Postgres", link: "/railway/data/postgres" },
{ label: "MySQL", link: "/railway/data/mysql" },
{
label: "Drizzle + Postgres",
link: "/railway/data/drizzle-postgres",
},
{
label: "Drizzle + MySQL",
link: "/railway/data/drizzle-mysql",
},
{ label: "Mongo", link: "/railway/data/mongo" },
{ label: "Redis", link: "/railway/data/redis" },
{ label: "Variables", link: "/railway/data/variables" },
{ label: "Buckets", link: "/railway/data/buckets" },
],
},
{
label: "Networking",
items: [
{
label: "Custom domains, TCP & private networks",
link: "/railway/networking",
},
],
},
providerResourcesEntry("Railway"),
],
},
{
label: "PlanetScale",
items: [
{ label: "Overview", link: "/planetscale" },
{ label: "Setup", link: "/planetscale/setup" },
{
label: "Data",
items: [
{ label: "Postgres", link: "/planetscale/data/postgres" },
{ label: "MySQL", link: "/planetscale/data/mysql" },
{ label: "Migrations", link: "/planetscale/data/migrations" },
{ label: "Credentials", link: "/planetscale/data/credentials" },
{ label: "Backups", link: "/planetscale/data/backups" },
],
},
{
label: "Guides",
items: [
{
label: "Preview branches per PR",
link: "/planetscale/guides/preview-branches",
},
{
label: "Drizzle ORM",
link: "/planetscale/guides/drizzle",
},
],
},
providerResourcesEntry("Planetscale"),
],
},
{
label: "Neon",
items: [
{ label: "Overview", link: "/neon" },
{ label: "Setup", link: "/neon/setup" },
{ label: "Organization governance", link: "/neon/governance" },
{
label: "Frontend",
items: [
{ label: "Vite", link: "/neon/frontend/vite" },
{ label: "Astro", link: "/neon/frontend/astro" },
{ label: "Next.js", link: "/neon/frontend/nextjs" },
{ label: "Nuxt", link: "/neon/frontend/nuxt" },
{ label: "SvelteKit", link: "/neon/frontend/sveltekit" },
{ label: "React Router", link: "/neon/frontend/react-router" },
{ label: "SolidStart", link: "/neon/frontend/solidstart" },
{
label: "TanStack Start",
link: "/neon/frontend/tanstack-start",
},
{ label: "Waku", link: "/neon/frontend/waku" },
{ label: "Octane", link: "/neon/frontend/octane" },
{ label: "Foldkit", link: "/neon/frontend/foldkit" },
{ label: "Vocs", link: "/neon/frontend/vocs" },
{ label: "Static Site", link: "/neon/frontend/static-site" },
],
},
{
label: "Upload tutorial",
items: [
{ label: "Overview", link: "/neon/tutorial" },
{ label: "Backend", link: "/neon/tutorial/backend" },
{ label: "Functions", link: "/neon/tutorial/functions" },
{ label: "Browser", link: "/neon/tutorial/frontend" },
{ label: "Preview branches", link: "/neon/tutorial/previews" },
],
},
{
label: "Data",
items: [
{ label: "Branching", link: "/neon/data/branching" },
{ label: "Connections", link: "/neon/data/connections" },
{ label: "Migrations", link: "/neon/data/migrations" },
],
},
{
label: "Guides",
items: [
{
label: "Preview branches per PR",
link: "/neon/guides/preview-branches",
},
{ label: "AI Gateway setup", link: "/neon/guides/ai-gateway" },
{
label: "Production Auth",
link: "/neon/guides/production-auth",
},
{
label: "Private networking",
link: "/neon/guides/private-networking",
},
{
label: "Custom domains",
link: "/neon/guides/custom-domains",
},
{
label: "State and recovery",
link: "/neon/guides/state-recovery",
},
{ label: "Drizzle ORM", link: "/neon/guides/drizzle" },
],
},
providerResourcesEntry("Neon"),
],
},
{
label: "Prisma",
items: [
{ label: "Overview", link: "/prisma" },
{ label: "Setup", link: "/prisma/setup" },
{
label: "Tutorial",
items: [{ autogenerate: { directory: "prisma/tutorial" } }],
},
{
label: "Compute",
items: [
{ label: "Apps", link: "/prisma/compute/apps" },
{ label: "Deployments", link: "/prisma/compute/deployments" },
],
},
{
label: "Data",
items: [
{ label: "Postgres", link: "/prisma/data/postgres" },
{
label: "Drizzle + Postgres",
link: "/prisma/data/drizzle-postgres",
},
{ label: "Branches", link: "/prisma/data/branches" },
{ label: "Connections", link: "/prisma/data/connections" },
{ label: "Buckets", link: "/prisma/data/buckets" },
],
},
{
label: "Frontend",
items: sortFrontendItems([
{ label: "Overview", link: "/prisma/frontend/websites" },
{ label: "Astro", link: "/prisma/frontend/astro" },
{ label: "Foldkit", link: "/prisma/frontend/foldkit" },
{ label: "Next.js", link: "/prisma/frontend/nextjs" },
{ label: "Vinext", link: "/prisma/frontend/vinext" },
{ label: "Nuxt", link: "/prisma/frontend/nuxt" },
{ label: "Octane", link: "/prisma/frontend/octane" },
{
label: "React Router",
link: "/prisma/frontend/react-router",
},
{ label: "SolidStart", link: "/prisma/frontend/solidstart" },
{ label: "Static sites", link: "/prisma/frontend/static-site" },
{ label: "SvelteKit", link: "/prisma/frontend/sveltekit" },
{
label: "TanStack Start",
link: "/prisma/frontend/tanstack-start",
},
{ label: "Vite", link: "/prisma/frontend/vite" },
{ label: "Vocs", link: "/prisma/frontend/vocs" },
{ label: "Waku", link: "/prisma/frontend/waku" },
]),
},
{
label: "Guides",
items: [
{
label: "Connect from Workers",
link: "/prisma/guides/cloudflare-workers",
},
],
},
providerApiReferenceEntry("Prisma"),
],
},
{
label: "Stripe",
items: [
{ label: "Overview", link: "/stripe" },
{ label: "Setup", link: "/stripe/setup" },
{
label: "Guides",
items: [
{
label: "Sell a subscription",
link: "/stripe/guides/subscriptions",
},
{
label: "Onboard merchants with Connect",
link: "/stripe/guides/connect",
},
{
label: "React to Stripe events",
link: "/stripe/guides/webhooks",
},
],
},
providerResourcesEntry("Stripe"),
],
},
{
label: "Better Auth",
items: [
{ label: "Overview", link: "/better-auth" },
{
label: "Tutorial",
collapsed: false,
items: [{ autogenerate: { directory: "better-auth/tutorial" } }],
},
{
label: "Sign-in providers",
items: [
{
autogenerate: { directory: "better-auth/sign-in-providers" },
},
],
},
{
label: "Databases",
items: [{ autogenerate: { directory: "better-auth/databases" } }],
},
{
label: "Guides",
items: [
{
label: "Config and secrets",
link: "/better-auth/guides/configuration",
},
{
label: "HTTP API middleware",
link: "/better-auth/guides/http-api-middleware",
},
{
label: "Secondary storage",
link: "/better-auth/guides/secondary-storage",
},
{ label: "Migrations", link: "/better-auth/guides/migrations" },
{
label: "Upgrading from 1.6 to 1.7.5",
link: "/better-auth/upgrades/from-1-6-to-1-7",
},
],
},
{ ...providerResourcesEntry("BetterAuth"), label: "Reference" },
],
},
{
label: "Axiom",
items: [
{ label: "Overview", link: "/axiom" },
{ label: "Setup", link: "/axiom/setup" },
{
label: "Data",
items: [
{ label: "Datasets & ingest", link: "/axiom/data/ingest" },
],
},
{
label: "Guides",
items: [
{ label: "Alerting", link: "/axiom/guides/alerting" },
{ label: "Dashboards", link: "/axiom/guides/dashboards" },
{ label: "Annotations", link: "/axiom/guides/annotations" },
],
},
providerResourcesEntry("Axiom"),
],
},
{
label: "GitHub",
items: [
{ label: "Overview", link: "/github" },
{ label: "Setup", link: "/github/setup" },
{ label: "Repositories", link: "/github/repository" },
{
label: "Actions secrets & variables",
link: "/github/actions-config",
},
{ label: "Webhooks & events", link: "/github/events" },
providerResourcesEntry("GitHub"),
],
},
{
label: "Git",
items: [
{ label: "Overview", link: "/git" },
{ label: "Getting Started", link: "/git/getting-started" },
{
label: "Tutorial",
items: [
{
label: "Part 1: Push your first repository",
link: "/git/tutorial/part-1",
},
{
label: "Part 2: Control access",
link: "/git/tutorial/part-2",
},
{
label: "Part 3: Publish a repository",
link: "/git/tutorial/part-3",
},
{
label: "Part 4: Give users their own credentials",
link: "/git/tutorial/part-4",
},
{
label: "Part 5: Add your application's API",
link: "/git/tutorial/part-5",
},
{
label: "Part 6: Protect a branch",
link: "/git/tutorial/part-6",
},
],
},
{
label: "Using your host",
items: [
{ label: "Cloning & pushing", link: "/git/clone-and-push" },
{ label: "Repositories", link: "/git/repositories" },
{ label: "Pull requests", link: "/git/pull-requests" },
{
label: "GitHub API compatibility",
link: "/git/github-api",
},
],
},
{
label: "Building blocks",
items: [
{ label: "Overview", link: "/git/blocks" },
{ label: "HTTP routes", link: "/git/blocks/server" },
{ label: "Engine operations", link: "/git/blocks/engine" },
{ label: "Repository", link: "/git/blocks/repositories" },
{ label: "Registry", link: "/git/blocks/registry" },
{ label: "Blob Store", link: "/git/blocks/blob-store" },
{ label: "Hasher", link: "/git/blocks/hasher" },
{ label: "Auth", link: "/git/blocks/auth" },
],
},
{
label: "Recipes",
items: [
{ label: "Overview", link: "/git/recipes" },
{ label: "Scaling", link: "/git/recipes/scaling" },
{ label: "All on Cloudflare", link: "/git/recipes/cloudflare" },
{
label: "Bytes in S3, hashing on Lambda",
link: "/git/recipes/cloudflare-aws",
},
{
label: "Bring your own store",
link: "/git/recipes/your-own-store",
},
],
},
],
},
{
label: "Docker",
items: [
{ label: "Overview", link: "/docker" },
{ label: "Setup", link: "/docker/setup" },
{ label: "Run local services", link: "/docker/local-services" },
{ label: "Build & push images", link: "/docker/build-and-push" },
providerResourcesEntry("Docker"),
],
},
{
label: "Kubernetes",
items: [
{ label: "Overview", link: "/kubernetes" },
{ label: "Setup", link: "/kubernetes/setup" },
{
label: "Tutorial",
items: [{ autogenerate: { directory: "kubernetes/tutorial" } }],
},
{
label: "Clusters",
items: [
{
label: "Connecting to clusters",
link: "/kubernetes/clusters/connecting",
},
{
label: "Container registries",
link: "/kubernetes/clusters/registries",
},
{ label: "Local clusters", link: "/kubernetes/clusters/local" },
{ label: "Amazon EKS", link: "/kubernetes/clusters/eks" },
{
label: "Cluster adapters",
link: "/kubernetes/clusters/cluster-adapters",
},
],
},
{
label: "Workloads",
items: [
{
label: "Deployments",
link: "/kubernetes/workloads/deployments",
},
{
label: "Jobs & CronJobs",
link: "/kubernetes/workloads/jobs",
},
{
label: "Container images",
link: "/kubernetes/workloads/images",
},
{
label: "Configuration & bindings",
link: "/kubernetes/workloads/bindings",
},
{
label: "How objects are managed",
link: "/kubernetes/workloads/object-lifecycle",
},
],
},
{
label: "Objects",
items: [
{ label: "Manifests", link: "/kubernetes/objects/manifests" },
{
label: "Helm charts",
link: "/kubernetes/objects/helm-charts",
},
],
},
providerApiReferenceEntry("Kubernetes"),
],
},
{
label: "SQL",
items: [
{ label: "Overview", link: "/sql" },
{ label: "Databases", link: "/sql/databases" },
{
label: "Effect SQL",
items: [
{ label: "Postgres", link: "/sql/effect-sql/postgres" },
{ label: "MySQL", link: "/sql/effect-sql/mysql" },
{ label: "D1", link: "/sql/effect-sql/d1" },
{ label: "Migrations", link: "/sql/effect-sql/migrations" },
{
label: "Connection lifecycle",
link: "/sql/effect-sql/lifecycle",
},
],
},
{
label: "Drizzle",
items: [
{ label: "Postgres", link: "/sql/drizzle/postgres" },
{ label: "MySQL", link: "/sql/drizzle/mysql" },
{ label: "D1", link: "/sql/drizzle/d1" },
{ label: "Migrations", link: "/sql/drizzle/migrations" },
],
},
{
label: "Prisma ORM",
items: [
{ label: "Postgres", link: "/sql/prisma/postgres" },
{ label: "Contracts", link: "/sql/prisma/contracts" },
{ label: "Migrations", link: "/sql/prisma/migrations" },
],
},
providerApiReferenceEntry("SQL", "Drizzle"),
],
},
{
label: "Command",
items: [
{ label: "Overview", link: "/command" },
{
label: "Memoized builds & commands",
link: "/command/memoization",
},
{ label: "Dev servers", link: "/command/dev-servers" },
providerResourcesEntry("Command"),
],
},
{
label: "ACME",
items: [
{ label: "Overview", link: "/acme" },
{ label: "Getting started", link: "/acme/getting-started" },
{
label: "Certificate authorities",
link: "/acme/certificate-authorities",
},
{ label: "DNS validation", link: "/acme/dns-validation" },
{ label: "Renewal & revocation", link: "/acme/renewal" },
{ label: "Runtime issuance", link: "/acme/runtime" },
{ label: "Using certificates", link: "/acme/using-certificates" },
{ label: "Troubleshooting", link: "/acme/troubleshooting" },
providerResourcesEntry("ACME"),
],
},
providersSidebarEntry(),
],
// starlight-blog feeds this many posts into the sidebar's "Recent"
// group, which `src/blog-sidebar.ts` re-buckets into Releases/Posts.
// We want every post listed, so set it effectively unlimited.
plugins: [starlightBlog({ recentPostCount: Number.MAX_SAFE_INTEGER })],
routeMiddleware: ["./src/blog-sidebar.ts", "./src/docs-tabs-sidebar.ts"],
}),
mdx(),
],
vite: {
plugins: [
copyEditor({
handlers: {
jsdoc: jsdocCopyHandler(),
md: markdownFiles(markdownCopy),
},
markdownStyles: { [JSDOC_COPY_STYLE]: jsdocMarkdownStyle },
}),
tailwindcss(),
],
server: {
// Dev-only: allow sharing the dev server through cloudflared quick
// tunnels (random *.trycloudflare.com hostnames).
allowedHosts: [".trycloudflare.com"],
},
ssr: {
// Sätteri (Astro 7's markdown processor) loads a platform-native
// binding via CJS require. Bundling its JS loader into the prerender
// chunks breaks that resolution under bun's isolated node_modules
// layout, so keep it external — it resolves from website/node_modules
// (declared as a direct dependency) instead.
external: ["satteri", "takumi-js"],
},
},
experimental: {
incrementalBuild: true,
},
});