1import mdx from "@astrojs/mdx";
2import react from "@astrojs/react";
3import sitemap from "@astrojs/sitemap";
4import starlight from "@astrojs/starlight";
5import tailwindcss from "@tailwindcss/vite";
6import type { AstroIntegration } from "astro";
7import { defineConfig } from "astro/config";
8import {
9 copyEditor,
10 markdownBlocks,
11 markdownFiles,
12 type MarkdownFilesOptions,
13} from "@alchemy.run/vite-plugin-copy-editor";
14import { promises as fs } from "node:fs";
15import path from "node:path";
16import { fileURLToPath } from "node:url";
17import starlightBlog from "starlight-blog";
18import { buildOutputChecks, noindexPaths } from "./plugins/build-output.ts";
19import { jsdocCopyHandler, jsdocMarkdownStyle } from "./plugins/jsdoc-copy.ts";
20import { JSDOC_COPY_STYLE } from "../scripts/jsdoc-blocks.ts";
21import providersSidebar from "./src/generated/providers-sidebar.json" with { type: "json" };
22import { rewriteReferenceLinks } from "./src/reference-links.ts";
23
24/**
25 * Every provider has a docs hub: its reference tree renders inside the
26 * hub's "API Reference" group and its reference URLs belong to the hub tab
27 * (see docs-tabs.ts). The Reference tab is a directory — its sidebar is
28 * just the list of providers, each linking to its hub.
29 */
30function providersSidebarEntry() {
31 return {
32 label: "Reference",
33 collapsed: false,
34 items: [
35 { label: "AWS", link: "/aws" },
36 { label: "GCP", link: "/gcp" },
37 { label: "Cloudflare", link: "/cloudflare" },
38 { label: "Hetzner", link: "/hetzner" },
39 { label: "Fly", link: "/fly" },
40 { label: "Railway", link: "/railway" },
41 { label: "PlanetScale", link: "/planetscale" },
42 { label: "Neon", link: "/neon" },
43 { label: "Prisma", link: "/prisma" },
44 { label: "Better Auth", link: "/better-auth" },
45 { label: "Axiom", link: "/axiom" },
46 { label: "GitHub", link: "/github" },
47 { label: "Stripe", link: "/stripe" },
48 { label: "Docker", link: "/docker" },
49 { label: "Kubernetes", link: "/kubernetes" },
50 { label: "SQL", link: "/sql" },
51 { label: "Command", link: "/command" },
52 { label: "ACME", link: "/acme" },
53 ],
54 };
55}
56
57/**
58 * A cloud hub's "API Reference" section: that provider's slice of the generated
59 * alphabetical list of reference pages below Guides.
60 * A hub that fronts several provider namespaces (e.g. SQL + Drizzle) passes
61 * them all and gets one merged API Reference group.
62 *
63 * @param {...string} providers Provider labels / directory names (e.g. "Cloudflare")
64 */
65function providerResourcesEntry(...providers: string[]) {
66 const entryItems = (provider: string) => {
67 const group = providersSidebar.find((p) => p.label === provider);
68 if (group) return group.items;
69 return [
70 { autogenerate: { directory: `providers/${provider}`, collapsed: true } },
71 ];
72 };
73 // A single provider's tree is inlined; a multi-namespace hub nests each
74 // provider under its own subgroup so same-named resources (SQL.D1 vs
75 // Drizzle.D1) stay distinguishable.
76 const items =
77 providers.length === 1
78 ? entryItems(providers[0])
79 : providers.map((provider) => ({
80 label: provider,
81 collapsed: true,
82 items: entryItems(provider),
83 }));
84 return { label: "API Reference", collapsed: false, items };
85}
86
87function sortFrontendItems(items: readonly { label: string; link: string }[]) {
88 return items.toSorted((a, b) => {
89 const overviewOrder =
90 Number(b.label === "Overview") - Number(a.label === "Overview");
91 return (
92 overviewOrder ||
93 a.label.localeCompare(b.label, "en", { sensitivity: "base" })
94 );
95 });
96}
97
98type ReferenceItem =
99 | { label: string; link: string }
100 | { label: string; items: readonly ReferenceItem[] };
101
102function providerApiReferenceEntry(...providers: string[]) {
103 const flatten = (
104 items: readonly ReferenceItem[],
105 prefix: readonly string[],
106 ): { label: string; link: string }[] =>
107 items.flatMap((item) => {
108 if ("items" in item) {
109 // Generated category and service groups can share a name.
110 return flatten(
111 item.items,
112 prefix.at(-1) === item.label ? prefix : [...prefix, item.label],
113 );
114 }
115 return [{ label: [...prefix, item.label].join("."), link: item.link }];
116 });
117
118 return {
119 label: "API Reference",
120 collapsed: false,
121 items: providers.flatMap((provider) =>
122 flatten(
123 providersSidebar.find((group) => group.label === provider)?.items ?? [],
124 providers.length > 1 ? [provider] : [],
125 ),
126 ),
127 };
128}
129
130/**
131 * Copies `src/content/docs/**\/*.{md,mdx}` into the build output dir, preserving
132 * the directory layout but normalizing extensions to `.md`. This lets the worker
133 * serve raw markdown for clients (e.g. coding agents) that prefer it.
134 */
135/**
136 * Dev only: hand-written docs (`.md`/`.mdx`) are editable in the browser.
137 * Generated reference pages are edited through their JSDoc instead.
138 */
139const markdownCopy: MarkdownFilesOptions = {
140 root: fileURLToPath(new URL(".", import.meta.url)),
141 exclude: /\/src\/content\/docs\/providers\//,
142 style: JSDOC_COPY_STYLE,
143};
144
145function markdownCopyEditing(): AstroIntegration {
146 return {
147 name: "markdown-copy-editing",
148 hooks: {
149 "astro:config:setup": ({ command, config }) => {
150 if (command !== "dev") return;
151 const processor = config.markdown.processor as
152 | { name: string; options?: { mdastPlugins?: unknown[] } }
153 | undefined;
154 if (processor?.name !== "satteri" || !processor.options?.mdastPlugins) {
155 return;
156 }
157 processor.options.mdastPlugins.unshift(markdownBlocks(markdownCopy));
158 },
159 },
160 };
161}
162
163function copyMarkdownSources(): AstroIntegration {
164 return {
165 name: "copy-markdown-sources",
166 hooks: {
167 "astro:build:done": async ({ dir }) => {
168 const outDir = fileURLToPath(dir);
169
170 /**
171 * @param {string} srcDir
172 * @param {{ lowercase?: boolean }} [opts]
173 * @param {string} [relTo]
174 */
175 async function walk(
176 srcDir: string,
177 opts: { lowercase?: boolean } = {},
178 relTo: string = srcDir,
179 ) {
180 let entries;
181 try {
182 entries = await fs.readdir(srcDir, { withFileTypes: true });
183 } catch {
184 return;
185 }
186 // Fan the copies out — thousands of tiny files; serial awaits
187 // dominate the hook's runtime otherwise.
188 await Promise.all(
189 entries.map(async (entry) => {
190 const full = path.join(srcDir, entry.name);
191 if (entry.isDirectory()) return walk(full, opts, relTo);
192 if (!entry.isFile()) return;
193 const ext = path.extname(entry.name).toLowerCase();
194 if (ext !== ".md" && ext !== ".mdx") return;
195 let rel = path.relative(relTo, full);
196 rel = rel.slice(0, rel.length - ext.length) + ".md";
197 // Starlight lowercases doc URLs (e.g. CamelCase source
198 // `providers/AWS/S3/Bucket.md` is served at `/providers/aws/s3/bucket`),
199 // so the raw-markdown copy must live at the lowercased path or the
200 // worker's `/providers/aws/s3/bucket.md` lookup 404s into HTML.
201 if (opts.lowercase) rel = rel.toLowerCase();
202 const target = path.join(outDir, rel);
203 await fs.mkdir(path.dirname(target), { recursive: true });
204 await fs.writeFile(
205 target,
206 rewriteReferenceLinks(await fs.readFile(full, "utf8")),
207 );
208 }),
209 );
210 }
211
212 // Docs (Starlight content collection) — preserves nested layout under
213 // /content/docs/ → /<path>.md, lowercased to match Starlight's URLs.
214 await walk(
215 fileURLToPath(new URL("./src/content/docs/", import.meta.url)),
216 { lowercase: true },
217 );
218 // Marketing pages (top-level Astro pages) — exposes /<page>.md so
219 // agents can fetch raw MDX via the worker's content negotiation. Astro
220 // page routing preserves case, so don't lowercase these.
221 await walk(fileURLToPath(new URL("./src/pages/", import.meta.url)));
222 },
223 },
224 };
225}
226
227export default defineConfig({
228 site: "https://alchemy.run",
229 redirects: {
230 "/infrastructure-as-effects": "/what-is-alchemy",
231 "/cli/login": "/cli/profile",
232 "/drizzle": "/sql",
233 "/drizzle/migrations": "/sql/drizzle/migrations",
234 "/better-auth/database-layers": "/better-auth/databases",
235 "/better-auth/migrations": "/better-auth/guides/migrations",
236 "/better-auth/upgrading": "/better-auth/upgrades/from-1-6-to-1-7",
237 },
238 prefetch: true,
239 devToolbar: { enabled: false },
240 trailingSlash: "ignore",
241 integrations: [
242 markdownCopyEditing(),
243 react(),
244 copyMarkdownSources(),
245 buildOutputChecks(),
246 sitemap({
247 filter: (page) =>
248 !page.endsWith(".html") &&
249 !page.endsWith(".md") &&
250 !page.endsWith(".mdx") &&
251 // Anything rendered with a `noindex` robots meta: `/auth/*`, and the
252 // example-less API reference stubs (see `collectNoindexPages`).
253 !noindexPaths.has(new URL(page).pathname) &&
254 // starlight-blog's paginated listings (`/blog/2/`, …) — thin pages
255 // that only repeat post excerpts; the posts themselves are listed.
256 !/\/blog\/\d+\/?$/.test(page),
257 }),
258 starlight({
259 title: "Alchemy",
260 favicon: "/favicon.svg",
261 customCss: ["./src/styles/global.css", "./src/styles/custom.css"],
262 components: {
263 ThemeProvider: "./src/components/ThemeProvider.astro",
264 Header: "./src/components/starlight/Header.astro",
265 Head: "./src/components/starlight/Head.astro",
266 Sidebar: "./src/components/starlight/Sidebar.astro",
267 MarkdownContent: "./src/components/starlight/MarkdownContent.astro",
268 },
269 prerender: true,
270 social: [
271 {
272 icon: "github",
273 label: "GitHub",
274 href: "https://github.com/alchemy-run/alchemy",
275 },
276 {
277 icon: "discord",
278 label: "Discord",
279 href: "/discord",
280 },
281 ],
282 editLink: {
283 baseUrl: "https://github.com/alchemy-run/alchemy/edit/main/website",
284 },
285 // One top-level group per docs tab (see src/docs-tabs.ts). The
286 // docs-tabs-sidebar middleware swaps in the active tab's group, so
287 // the rendered sidebar only ever navigates within the current tab.
288 sidebar: [
289 {
290 label: "Core",
291 items: [
292 { label: "Getting started", link: "/getting-started" },
293 { label: "What is Alchemy?", link: "/what-is-alchemy" },
294 {
295 label: "Infrastructure as Code",
296 items: [
297 { label: "Stacks", link: "/infrastructure-as-code/stack" },
298 {
299 label: "Resources",
300 link: "/infrastructure-as-code/resource",
301 },
302 { label: "Actions", link: "/infrastructure-as-code/action" },
303 {
304 label: "Inputs & Outputs",
305 link: "/infrastructure-as-code/outputs",
306 },
307 {
308 label: "References",
309 link: "/infrastructure-as-code/references",
310 },
311 {
312 label: "Resource Lifecycle",
313 link: "/infrastructure-as-code/resource-lifecycle",
314 },
315 {
316 label: "Renaming Resources",
317 link: "/infrastructure-as-code/renaming",
318 },
319 {
320 label: "Providers",
321 link: "/infrastructure-as-code/provider",
322 },
323 {
324 label: "Custom Provider",
325 link: "/infrastructure-as-code/custom-provider",
326 },
327 {
328 label: "Local Provider",
329 link: "/infrastructure-as-code/local-provider",
330 },
331 ],
332 },
333 {
334 label: "Infrastructure as Effects",
335 items: [
336 {
337 label: "Runtime",
338 link: "/infrastructure-as-effects/runtime",
339 },
340 {
341 label: "Bindings",
342 link: "/infrastructure-as-effects/binding",
343 },
344 { label: "Layers", link: "/infrastructure-as-effects/layers" },
345 {
346 label: "Event Sources",
347 link: "/infrastructure-as-effects/event-sources",
348 },
349 {
350 label: "Sinks",
351 link: "/infrastructure-as-effects/sinks",
352 },
353 { label: "Phases", link: "/infrastructure-as-effects/phases" },
354 {
355 label: "Circular Bindings",
356 link: "/infrastructure-as-effects/circular-bindings",
357 },
358 {
359 label: "Telemetry",
360 link: "/infrastructure-as-effects/telemetry",
361 },
362 {
363 label: "Custom Runtime",
364 link: "/infrastructure-as-effects/custom-runtime",
365 },
366 ],
367 },
368 {
369 label: "APIs",
370 items: [
371 {
372 label: "Overview",
373 link: "/apis",
374 },
375 {
376 label: "Schemaless RPC",
377 link: "/apis/schemaless",
378 },
379 {
380 label: "Effect RPC",
381 link: "/apis/effect-rpc",
382 },
383 {
384 label: "Effect HTTP",
385 link: "/apis/effect-http",
386 },
387 ],
388 },
389 {
390 label: "Environments",
391 items: [
392 { label: "Stages", link: "/environments/stages" },
393 { label: "Profiles", link: "/environments/profiles" },
394 {
395 label: "Auth Providers",
396 link: "/environments/auth-providers",
397 },
398 {
399 label: "Custom Auth Provider",
400 link: "/environments/custom-auth-provider",
401 },
402 { label: "Secrets & Config", link: "/environments/secrets" },
403 {
404 label: "Secret providers",
405 link: "/environments/secret-providers",
406 },
407 { label: "Doppler", link: "/environments/doppler" },
408 { label: "Infisical", link: "/environments/infisical" },
409 {
410 label: "Local development",
411 link: "/environments/local-development",
412 },
413 { label: "CI", link: "/environments/ci" },
414 ],
415 },
416 {
417 label: "State Store",
418 items: [
419 { label: "State Store", link: "/state-store" },
420 {
421 label: "Custom State Store",
422 link: "/state-store/custom-state-store",
423 },
424 ],
425 },
426 {
427 label: "Project structure",
428 items: [
429 {
430 label: "File layout",
431 link: "/project-structure/file-layout",
432 },
433 { label: "Monorepo", link: "/project-structure/monorepo" },
434 {
435 label: "Single Stack",
436 link: "/project-structure/monorepo-single-stack",
437 },
438 {
439 label: "Multiple Stacks",
440 link: "/project-structure/monorepo-multi-stack",
441 },
442 ],
443 },
444 {
445 label: "Testing & observability",
446 items: [
447 { label: "Testing", link: "/testing" },
448 {
449 label: "Testing a Stack",
450 link: "/testing/testing-a-stack",
451 },
452 {
453 label: "Testing Providers",
454 link: "/testing/testing-providers",
455 },
456 { label: "Test harness", link: "/testing/test-harness" },
457 { label: "Observability", link: "/testing/observability" },
458 ],
459 },
460 ],
461 },
462 {
463 label: "CLI",
464 items: [
465 { label: "Overview", link: "/cli" },
466 {
467 label: "Deploy",
468 items: [
469 { label: "deploy", link: "/cli/deploy" },
470 { label: "plan", link: "/cli/plan" },
471 { label: "destroy", link: "/cli/destroy" },
472 { label: "drift", link: "/cli/drift" },
473 { label: "nuke", link: "/cli/nuke" },
474 {
475 label: "Adopting Resources",
476 link: "/cli/adopting-resources",
477 },
478 ],
479 },
480 {
481 label: "Develop",
482 items: [
483 { label: "dev", link: "/cli/dev" },
484 { label: "logs", link: "/cli/logs" },
485 ],
486 },
487 {
488 label: "Auth",
489 items: [{ label: "profile", link: "/cli/profile" }],
490 },
491 {
492 label: "State",
493 items: [
494 { label: "state", link: "/cli/state" },
495 {
496 label: "Inspecting State",
497 link: "/cli/inspecting-state",
498 },
499 ],
500 },
501 {
502 label: "Providers",
503 items: [
504 { label: "aws", link: "/cli/aws" },
505 { label: "cloudflare", link: "/cli/cloudflare" },
506 ],
507 },
508 { label: "Migrating from v1", link: "/migrating-from-v1" },
509 ],
510 },
511 {
512 label: "Cloudflare",
513 items: [
514 { label: "Overview", link: "/cloudflare" },
515 { label: "Setup", link: "/cloudflare/setup" },
516 {
517 label: "Local development",
518 link: "/cloudflare/local-development",
519 },
520 {
521 label: "Tutorial",
522 items: [{ autogenerate: { directory: "cloudflare/tutorial" } }],
523 },
524 {
525 label: "Compute",
526 items: [
527 { label: "Workers", link: "/cloudflare/compute/workers" },
528 {
529 label: "Gradual deployments",
530 link: "/cloudflare/compute/gradual-deployments",
531 },
532 {
533 label: "Python Workers",
534 link: "/cloudflare/compute/python-workers",
535 },
536 { label: "Workers Cache", link: "/cloudflare/compute/cache" },
537 {
538 label: "Rate limiting",
539 link: "/cloudflare/compute/rate-limiting",
540 },
541 {
542 label: "Browser rendering",
543 link: "/cloudflare/compute/browser-rendering",
544 },
545 {
546 label: "Worker Loader",
547 link: "/cloudflare/compute/worker-loader",
548 },
549 {
550 label: "Workers for Platforms",
551 link: "/cloudflare/compute/workers-for-platforms",
552 },
553 {
554 label: "Durable Objects",
555 link: "/cloudflare/compute/durable-objects",
556 },
557 {
558 label: "Cross-worker DOs",
559 link: "/cloudflare/compute/cross-worker-durable-object",
560 },
561 {
562 label: "WebSockets",
563 link: "/cloudflare/compute/hibernatable-websockets",
564 },
565 { label: "Containers", link: "/cloudflare/compute/containers" },
566 { label: "Workflows", link: "/cloudflare/compute/workflows" },
567 ],
568 },
569 {
570 label: "Frontend",
571 items: sortFrontendItems([
572 {
573 label: "Overview",
574 link: "/cloudflare/frontend/frontends",
575 },
576 { label: "Astro", link: "/cloudflare/frontend/astro" },
577 { label: "Foldkit", link: "/cloudflare/frontend/foldkit" },
578 {
579 label: "Full-stack RPC + Drizzle",
580 link: "/cloudflare/frontend/full-stack-tanstack-rpc-drizzle",
581 },
582 { label: "Next.js", link: "/cloudflare/frontend/nextjs" },
583 { label: "Nuxt", link: "/cloudflare/frontend/nuxt" },
584 { label: "Octane", link: "/cloudflare/frontend/octane" },
585 {
586 label: "React Router",
587 link: "/cloudflare/frontend/react-router",
588 },
589 { label: "React SPA", link: "/cloudflare/frontend/vite-spa" },
590 {
591 label: "SolidStart",
592 link: "/cloudflare/frontend/solidstart",
593 },
594 {
595 label: "Static sites",
596 link: "/cloudflare/frontend/static-site",
597 },
598 {
599 label: "SvelteKit",
600 link: "/cloudflare/frontend/sveltekit",
601 },
602 {
603 label: "TanStack Start",
604 link: "/cloudflare/frontend/tanstack-start",
605 },
606 { label: "Vite", link: "/cloudflare/frontend/vite" },
607 { label: "Vinext", link: "/cloudflare/frontend/vinext" },
608 { label: "Vue", link: "/cloudflare/frontend/vue" },
609 { label: "Waku", link: "/cloudflare/frontend/waku" },
610 ]),
611 },
612 {
613 label: "APIs",
614 items: [
615 {
616 label: "Schemaless RPC",
617 link: "/cloudflare/apis/schemaless-rpc",
618 },
619 { label: "Effect RPC", link: "/cloudflare/apis/effect-rpc" },
620 {
621 label: "Effect HTTP",
622 link: "/cloudflare/apis/effect-http-api",
623 },
624 ],
625 },
626 {
627 label: "Data",
628 items: [
629 { label: "D1", link: "/cloudflare/data/d1" },
630 { label: "KV", link: "/cloudflare/data/kv" },
631 { label: "R2", link: "/cloudflare/data/r2" },
632 {
633 label: "R2 presigned URLs",
634 link: "/cloudflare/data/r2-presigned-urls",
635 },
636 { label: "Hyperdrive", link: "/cloudflare/data/hyperdrive" },
637 { label: "Drizzle ORM", link: "/cloudflare/data/drizzle" },
638 { label: "Prisma ORM", link: "/cloudflare/data/prisma" },
639 { label: "Drizzle on D1", link: "/cloudflare/data/d1-drizzle" },
640 {
641 label: "Shared database",
642 link: "/cloudflare/data/shared-database",
643 },
644 {
645 label: "Branch from a shared database",
646 link: "/cloudflare/data/branch-from-shared-database",
647 },
648 { label: "Artifacts", link: "/cloudflare/data/artifacts" },
649 ],
650 },
651 {
652 label: "Messaging & events",
653 items: [
654 { label: "Queues", link: "/cloudflare/messaging/queues" },
655 { label: "Cron triggers", link: "/cloudflare/messaging/cron" },
656 {
657 label: "GitHub events",
658 link: "/cloudflare/messaging/github-events",
659 },
660 ],
661 },
662 {
663 label: "Email",
664 items: [
665 { label: "Email", link: "/cloudflare/email" },
666 {
667 label: "Send & receive email",
668 link: "/cloudflare/email/send-and-receive",
669 },
670 ],
671 },
672 {
673 label: "AI",
674 items: [
675 { label: "Workers AI", link: "/cloudflare/ai/workers-ai" },
676 { label: "AI Gateway", link: "/cloudflare/ai/ai-gateway" },
677 {
678 label: "AI Search (AutoRAG)",
679 link: "/cloudflare/ai/ai-search",
680 },
681 { label: "Effect AI", link: "/cloudflare/ai/effect-ai" },
682 { label: "Vectorize", link: "/cloudflare/ai/vectorize" },
683 {
684 label: "Release agent",
685 link: "/cloudflare/ai/release-agent",
686 },
687 ],
688 },
689 {
690 label: "Security & secrets",
691 items: [
692 {
693 label: "Secrets & env",
694 link: "/cloudflare/security/secrets-env",
695 },
696 {
697 label: "Secrets Store",
698 link: "/cloudflare/security/secrets-store",
699 },
700 { label: "Turnstile", link: "/cloudflare/security/turnstile" },
701 ],
702 },
703 {
704 label: "Observability",
705 items: [
706 {
707 label: "Native tracing",
708 link: "/cloudflare/observability/workers-tracing",
709 },
710 {
711 label: "Axiom telemetry",
712 link: "/cloudflare/observability/axiom-observability",
713 },
714 {
715 label: "Analytics Engine",
716 link: "/cloudflare/observability/analytics-engine",
717 },
718 ],
719 },
720 {
721 label: "Networking",
722 items: [
723 {
724 label: "Domains & DNS",
725 link: "/cloudflare/networking/domains",
726 },
727 {
728 label: "Custom domains & routes",
729 link: "/cloudflare/networking/custom-domains",
730 },
731 { label: "Tunnel", link: "/cloudflare/networking/tunnel" },
732 ],
733 },
734 providerResourcesEntry("Cloudflare"),
735 ],
736 },
737 {
738 label: "AWS",
739 items: [
740 { label: "Overview", link: "/aws" },
741 { label: "Setup", link: "/aws/setup" },
742 { label: "Local development", link: "/aws/local-development" },
743 {
744 label: "Tutorial",
745 items: [{ autogenerate: { directory: "aws/tutorial" } }],
746 },
747 {
748 label: "Compute",
749 items: [
750 {
751 label: "Choosing a runtime",
752 link: "/aws/compute/choosing-a-runtime",
753 },
754 { label: "Lambda", link: "/aws/compute/lambda" },
755 { label: "ECS", link: "/aws/compute/ecs" },
756 { label: "EC2", link: "/aws/compute/ec2" },
757 { label: "EKS", link: "/aws/compute/eks" },
758 { label: "HyperPod", link: "/aws/compute/hyperpod" },
759 { label: "Lambda MicroVMs", link: "/aws/compute/microvms" },
760 ],
761 },
762 {
763 label: "Frontend",
764 items: sortFrontendItems([
765 {
766 label: "Overview",
767 link: "/aws/frontend/websites",
768 },
769 { label: "Astro", link: "/aws/frontend/astro" },
770 { label: "Foldkit", link: "/aws/frontend/foldkit" },
771 {
772 label: "Full-stack RPC + Drizzle",
773 link: "/aws/frontend/full-stack-tanstack-rpc-drizzle",
774 },
775 { label: "Next.js", link: "/aws/frontend/nextjs" },
776 { label: "Vinext", link: "/aws/frontend/vinext" },
777 { label: "Nuxt", link: "/aws/frontend/nuxt" },
778 { label: "Octane", link: "/aws/frontend/octane" },
779 {
780 label: "React Router",
781 link: "/aws/frontend/react-router",
782 },
783 { label: "React SPA", link: "/aws/frontend/vite-spa" },
784 {
785 label: "SolidStart",
786 link: "/aws/frontend/solidstart",
787 },
788 {
789 label: "Static sites",
790 link: "/aws/frontend/static-site",
791 },
792 {
793 label: "SvelteKit",
794 link: "/aws/frontend/sveltekit",
795 },
796 {
797 label: "TanStack Start",
798 link: "/aws/frontend/tanstack-start",
799 },
800 { label: "Vite", link: "/aws/frontend/vite" },
801 { label: "Vue", link: "/aws/frontend/vue" },
802 { label: "Waku", link: "/aws/frontend/waku" },
803 ]),
804 },
805 {
806 label: "APIs",
807 items: [
808 {
809 label: "Schemaless RPC",
810 link: "/aws/apis/schemaless-rpc",
811 },
812 { label: "Effect RPC", link: "/aws/apis/effect-rpc" },
813 {
814 label: "Effect HTTP",
815 link: "/aws/apis/effect-http-api",
816 },
817 { label: "API Gateway", link: "/aws/apis/api-gateway" },
818 ],
819 },
820 {
821 label: "Data",
822 items: [
823 { label: "DynamoDB", link: "/aws/data/dynamodb" },
824 { label: "S3", link: "/aws/data/s3" },
825 { label: "RDS & Aurora", link: "/aws/data/rds" },
826 { label: "Drizzle + Aurora", link: "/aws/data/drizzle-aurora" },
827 { label: "Drizzle + DSQL", link: "/aws/data/drizzle-dsql" },
828 ],
829 },
830 {
831 label: "AI",
832 items: [
833 { label: "Bedrock & Effect AI", link: "/aws/ai/bedrock" },
834 ],
835 },
836 {
837 label: "Messaging & events",
838 items: [
839 { label: "SQS", link: "/aws/messaging/sqs" },
840 { label: "SNS", link: "/aws/messaging/sns" },
841 { label: "Kinesis", link: "/aws/messaging/kinesis" },
842 {
843 label: "EventBridge & Scheduler",
844 link: "/aws/messaging/eventbridge",
845 },
846 {
847 label: "DynamoDB Streams",
848 link: "/aws/messaging/dynamodb-streams",
849 },
850 { label: "S3 events", link: "/aws/messaging/s3-events" },
851 ],
852 },
853 {
854 label: "Email",
855 items: [
856 {
857 label: "Sending & managing email",
858 link: "/aws/email/sending",
859 },
860 {
861 label: "Receiving inbound email",
862 link: "/aws/email/receiving",
863 },
864 ],
865 },
866 {
867 label: "Security & secrets",
868 items: [
869 { label: "Secrets & env", link: "/aws/security/secrets-env" },
870 ],
871 },
872 {
873 label: "Observability",
874 items: [
875 { label: "CloudWatch", link: "/aws/observability/cloudwatch" },
876 ],
877 },
878 {
879 label: "Networking",
880 items: [
881 { label: "VPC & networking", link: "/aws/networking" },
882 {
883 label: "Custom domains",
884 link: "/aws/networking/custom-domains",
885 },
886 ],
887 },
888 providerResourcesEntry("AWS"),
889 ],
890 },
891 {
892 label: "GCP",
893 items: [
894 { label: "Overview", link: "/gcp" },
895 { label: "Setup", link: "/gcp/setup" },
896 {
897 label: "Guides",
898 items: [
899 {
900 label: "Serve an API on Cloud Run",
901 link: "/gcp/guides/cloud-run-api",
902 },
903 {
904 label: "Ingest events into BigQuery",
905 link: "/gcp/guides/event-pipeline",
906 },
907 {
908 label: "Cache with Memorystore",
909 link: "/gcp/guides/memorystore",
910 },
911 {
912 label: "How bindings grant IAM",
913 link: "/gcp/guides/bindings",
914 },
915 ],
916 },
917 providerResourcesEntry("GCP"),
918 ],
919 },
920 {
921 label: "Hetzner",
922 items: [
923 { label: "Overview", link: "/hetzner" },
924 { label: "Setup", link: "/hetzner/setup" },
925 {
926 label: "Tutorial",
927 items: [{ autogenerate: { directory: "hetzner/tutorial" } }],
928 },
929 {
930 label: "Frontend",
931 items: sortFrontendItems([
932 {
933 label: "Overview",
934 link: "/hetzner/frontend/websites",
935 },
936 { label: "Astro", link: "/hetzner/frontend/astro" },
937 { label: "Foldkit", link: "/hetzner/frontend/foldkit" },
938 { label: "Next.js", link: "/hetzner/frontend/nextjs" },
939 { label: "Vinext", link: "/hetzner/frontend/vinext" },
940 { label: "Nuxt", link: "/hetzner/frontend/nuxt" },
941 { label: "Octane", link: "/hetzner/frontend/octane" },
942 {
943 label: "React Router",
944 link: "/hetzner/frontend/react-router",
945 },
946 {
947 label: "SolidStart",
948 link: "/hetzner/frontend/solidstart",
949 },
950 {
951 label: "Static sites",
952 link: "/hetzner/frontend/static-site",
953 },
954 {
955 label: "SvelteKit",
956 link: "/hetzner/frontend/sveltekit",
957 },
958 {
959 label: "TanStack Start",
960 link: "/hetzner/frontend/tanstack-start",
961 },
962 { label: "Vite", link: "/hetzner/frontend/vite" },
963 { label: "Vocs", link: "/hetzner/frontend/vocs" },
964 { label: "Waku", link: "/hetzner/frontend/waku" },
965 ]),
966 },
967 {
968 label: "Compute",
969 items: [
970 { label: "Servers", link: "/hetzner/compute/servers" },
971 { label: "Services", link: "/hetzner/compute/services" },
972 ],
973 },
974 {
975 label: "Data",
976 items: [
977 { label: "Volumes", link: "/hetzner/data/volumes" },
978 {
979 label: "Drizzle + Postgres",
980 link: "/hetzner/data/drizzle-postgres",
981 },
982 ],
983 },
984 {
985 label: "Networking",
986 items: [
987 { label: "Networking", link: "/hetzner/networking" },
988 { label: "DNS", link: "/hetzner/networking/dns" },
989 ],
990 },
991 providerResourcesEntry("Hetzner"),
992 ],
993 },
994 {
995 label: "Fly",
996 items: [
997 { label: "Overview", link: "/fly" },
998 { label: "Setup", link: "/fly/setup" },
999 {
1000 label: "Tutorial",
1001 items: [{ autogenerate: { directory: "fly/tutorial" } }],
1002 },
1003 {
1004 label: "Frontend",
1005 items: sortFrontendItems([
1006 {
1007 label: "Overview",
1008 link: "/fly/frontend/websites",
1009 },
1010 { label: "Astro", link: "/fly/frontend/astro" },
1011 { label: "Foldkit", link: "/fly/frontend/foldkit" },
1012 { label: "Next.js", link: "/fly/frontend/nextjs" },
1013 { label: "Vinext", link: "/fly/frontend/vinext" },
1014 { label: "Nuxt", link: "/fly/frontend/nuxt" },
1015 { label: "Octane", link: "/fly/frontend/octane" },
1016 {
1017 label: "React Router",
1018 link: "/fly/frontend/react-router",
1019 },
1020 {
1021 label: "SolidStart",
1022 link: "/fly/frontend/solidstart",
1023 },
1024 {
1025 label: "Static sites",
1026 link: "/fly/frontend/static-site",
1027 },
1028 {
1029 label: "SvelteKit",
1030 link: "/fly/frontend/sveltekit",
1031 },
1032 {
1033 label: "TanStack Start",
1034 link: "/fly/frontend/tanstack-start",
1035 },
1036 { label: "Vite", link: "/fly/frontend/vite" },
1037 { label: "Vocs", link: "/fly/frontend/vocs" },
1038 { label: "Waku", link: "/fly/frontend/waku" },
1039 ]),
1040 },
1041 {
1042 label: "Compute",
1043 items: [
1044 { label: "Apps", link: "/fly/compute/apps" },
1045 { label: "Machines", link: "/fly/compute/machines" },
1046 { label: "Services", link: "/fly/compute/services" },
1047 {
1048 label: "Connect Services",
1049 link: "/fly/compute/connecting-services",
1050 },
1051 {
1052 label: "Blue/green deployments",
1053 link: "/fly/compute/deployments",
1054 },
1055 { label: "Sprites", link: "/fly/compute/sprites" },
1056 { label: "Regions", link: "/fly/compute/regions" },
1057 ],
1058 },
1059 {
1060 label: "Data",
1061 items: [
1062 { label: "Volumes", link: "/fly/data/volumes" },
1063 { label: "Postgres", link: "/fly/data/postgres" },
1064 {
1065 label: "Drizzle + Postgres",
1066 link: "/fly/data/drizzle-postgres",
1067 },
1068 { label: "Redis", link: "/fly/data/redis" },
1069 { label: "Tigris", link: "/fly/data/tigris" },
1070 { label: "Secrets", link: "/fly/data/secrets" },
1071 ],
1072 },
1073 {
1074 label: "Networking",
1075 items: [{ label: "IPs & certificates", link: "/fly/networking" }],
1076 },
1077 providerResourcesEntry("Fly"),
1078 ],
1079 },
1080 {
1081 label: "Railway",
1082 items: [
1083 { label: "Overview", link: "/railway" },
1084 { label: "Setup", link: "/railway/setup" },
1085 {
1086 label: "Tutorial",
1087 items: [{ autogenerate: { directory: "railway/tutorial" } }],
1088 },
1089 {
1090 label: "Frontend",
1091 items: sortFrontendItems([
1092 {
1093 label: "Overview",
1094 link: "/railway/frontend/websites",
1095 },
1096 { label: "Astro", link: "/railway/frontend/astro" },
1097 { label: "Foldkit", link: "/railway/frontend/foldkit" },
1098 { label: "Next.js", link: "/railway/frontend/nextjs" },
1099 { label: "Vinext", link: "/railway/frontend/vinext" },
1100 { label: "Nuxt", link: "/railway/frontend/nuxt" },
1101 { label: "Octane", link: "/railway/frontend/octane" },
1102 {
1103 label: "React Router",
1104 link: "/railway/frontend/react-router",
1105 },
1106 {
1107 label: "SolidStart",
1108 link: "/railway/frontend/solidstart",
1109 },
1110 {
1111 label: "Static sites",
1112 link: "/railway/frontend/static-site",
1113 },
1114 {
1115 label: "SvelteKit",
1116 link: "/railway/frontend/sveltekit",
1117 },
1118 {
1119 label: "TanStack Start",
1120 link: "/railway/frontend/tanstack-start",
1121 },
1122 { label: "Vite", link: "/railway/frontend/vite" },
1123 { label: "Vocs", link: "/railway/frontend/vocs" },
1124 { label: "Waku", link: "/railway/frontend/waku" },
1125 ]),
1126 },
1127 {
1128 label: "Compute",
1129 items: [
1130 { label: "Projects", link: "/railway/compute/projects" },
1131 { label: "Services", link: "/railway/compute/services" },
1132 {
1133 label: "Functions, templates & VMs",
1134 link: "/railway/compute/functions",
1135 },
1136 {
1137 label: "Environments",
1138 link: "/railway/compute/environments",
1139 },
1140 { label: "Regions", link: "/railway/compute/regions" },
1141 ],
1142 },
1143 {
1144 label: "Data",
1145 items: [
1146 { label: "Volumes", link: "/railway/data/volumes" },
1147 { label: "Postgres", link: "/railway/data/postgres" },
1148 { label: "MySQL", link: "/railway/data/mysql" },
1149 {
1150 label: "Drizzle + Postgres",
1151 link: "/railway/data/drizzle-postgres",
1152 },
1153 {
1154 label: "Drizzle + MySQL",
1155 link: "/railway/data/drizzle-mysql",
1156 },
1157 { label: "Mongo", link: "/railway/data/mongo" },
1158 { label: "Redis", link: "/railway/data/redis" },
1159 { label: "Variables", link: "/railway/data/variables" },
1160 { label: "Buckets", link: "/railway/data/buckets" },
1161 ],
1162 },
1163 {
1164 label: "Networking",
1165 items: [
1166 {
1167 label: "Custom domains, TCP & private networks",
1168 link: "/railway/networking",
1169 },
1170 ],
1171 },
1172 providerResourcesEntry("Railway"),
1173 ],
1174 },
1175 {
1176 label: "PlanetScale",
1177 items: [
1178 { label: "Overview", link: "/planetscale" },
1179 { label: "Setup", link: "/planetscale/setup" },
1180 {
1181 label: "Data",
1182 items: [
1183 { label: "Postgres", link: "/planetscale/data/postgres" },
1184 { label: "MySQL", link: "/planetscale/data/mysql" },
1185 { label: "Migrations", link: "/planetscale/data/migrations" },
1186 { label: "Credentials", link: "/planetscale/data/credentials" },
1187 { label: "Backups", link: "/planetscale/data/backups" },
1188 ],
1189 },
1190 {
1191 label: "Guides",
1192 items: [
1193 {
1194 label: "Preview branches per PR",
1195 link: "/planetscale/guides/preview-branches",
1196 },
1197 {
1198 label: "Drizzle ORM",
1199 link: "/planetscale/guides/drizzle",
1200 },
1201 ],
1202 },
1203 providerResourcesEntry("Planetscale"),
1204 ],
1205 },
1206 {
1207 label: "Neon",
1208 items: [
1209 { label: "Overview", link: "/neon" },
1210 { label: "Setup", link: "/neon/setup" },
1211 { label: "Organization governance", link: "/neon/governance" },
1212 {
1213 label: "Frontend",
1214 items: [
1215 { label: "Vite", link: "/neon/frontend/vite" },
1216 { label: "Astro", link: "/neon/frontend/astro" },
1217 { label: "Next.js", link: "/neon/frontend/nextjs" },
1218 { label: "Nuxt", link: "/neon/frontend/nuxt" },
1219 { label: "SvelteKit", link: "/neon/frontend/sveltekit" },
1220 { label: "React Router", link: "/neon/frontend/react-router" },
1221 { label: "SolidStart", link: "/neon/frontend/solidstart" },
1222 {
1223 label: "TanStack Start",
1224 link: "/neon/frontend/tanstack-start",
1225 },
1226 { label: "Waku", link: "/neon/frontend/waku" },
1227 { label: "Octane", link: "/neon/frontend/octane" },
1228 { label: "Foldkit", link: "/neon/frontend/foldkit" },
1229 { label: "Vocs", link: "/neon/frontend/vocs" },
1230 { label: "Static Site", link: "/neon/frontend/static-site" },
1231 ],
1232 },
1233 {
1234 label: "Upload tutorial",
1235 items: [
1236 { label: "Overview", link: "/neon/tutorial" },
1237 { label: "Backend", link: "/neon/tutorial/backend" },
1238 { label: "Functions", link: "/neon/tutorial/functions" },
1239 { label: "Browser", link: "/neon/tutorial/frontend" },
1240 { label: "Preview branches", link: "/neon/tutorial/previews" },
1241 ],
1242 },
1243 {
1244 label: "Data",
1245 items: [
1246 { label: "Branching", link: "/neon/data/branching" },
1247 { label: "Connections", link: "/neon/data/connections" },
1248 { label: "Migrations", link: "/neon/data/migrations" },
1249 ],
1250 },
1251 {
1252 label: "Guides",
1253 items: [
1254 {
1255 label: "Preview branches per PR",
1256 link: "/neon/guides/preview-branches",
1257 },
1258 { label: "AI Gateway setup", link: "/neon/guides/ai-gateway" },
1259 {
1260 label: "Production Auth",
1261 link: "/neon/guides/production-auth",
1262 },
1263 {
1264 label: "Private networking",
1265 link: "/neon/guides/private-networking",
1266 },
1267 {
1268 label: "Custom domains",
1269 link: "/neon/guides/custom-domains",
1270 },
1271 {
1272 label: "State and recovery",
1273 link: "/neon/guides/state-recovery",
1274 },
1275 { label: "Drizzle ORM", link: "/neon/guides/drizzle" },
1276 ],
1277 },
1278 providerResourcesEntry("Neon"),
1279 ],
1280 },
1281 {
1282 label: "Prisma",
1283 items: [
1284 { label: "Overview", link: "/prisma" },
1285 { label: "Setup", link: "/prisma/setup" },
1286 {
1287 label: "Tutorial",
1288 items: [{ autogenerate: { directory: "prisma/tutorial" } }],
1289 },
1290 {
1291 label: "Compute",
1292 items: [
1293 { label: "Apps", link: "/prisma/compute/apps" },
1294 { label: "Deployments", link: "/prisma/compute/deployments" },
1295 ],
1296 },
1297 {
1298 label: "Data",
1299 items: [
1300 { label: "Postgres", link: "/prisma/data/postgres" },
1301 {
1302 label: "Drizzle + Postgres",
1303 link: "/prisma/data/drizzle-postgres",
1304 },
1305 { label: "Branches", link: "/prisma/data/branches" },
1306 { label: "Connections", link: "/prisma/data/connections" },
1307 { label: "Buckets", link: "/prisma/data/buckets" },
1308 ],
1309 },
1310 {
1311 label: "Frontend",
1312 items: sortFrontendItems([
1313 { label: "Overview", link: "/prisma/frontend/websites" },
1314 { label: "Astro", link: "/prisma/frontend/astro" },
1315 { label: "Foldkit", link: "/prisma/frontend/foldkit" },
1316 { label: "Next.js", link: "/prisma/frontend/nextjs" },
1317 { label: "Vinext", link: "/prisma/frontend/vinext" },
1318 { label: "Nuxt", link: "/prisma/frontend/nuxt" },
1319 { label: "Octane", link: "/prisma/frontend/octane" },
1320 {
1321 label: "React Router",
1322 link: "/prisma/frontend/react-router",
1323 },
1324 { label: "SolidStart", link: "/prisma/frontend/solidstart" },
1325 { label: "Static sites", link: "/prisma/frontend/static-site" },
1326 { label: "SvelteKit", link: "/prisma/frontend/sveltekit" },
1327 {
1328 label: "TanStack Start",
1329 link: "/prisma/frontend/tanstack-start",
1330 },
1331 { label: "Vite", link: "/prisma/frontend/vite" },
1332 { label: "Vocs", link: "/prisma/frontend/vocs" },
1333 { label: "Waku", link: "/prisma/frontend/waku" },
1334 ]),
1335 },
1336 {
1337 label: "Guides",
1338 items: [
1339 {
1340 label: "Connect from Workers",
1341 link: "/prisma/guides/cloudflare-workers",
1342 },
1343 ],
1344 },
1345 providerApiReferenceEntry("Prisma"),
1346 ],
1347 },
1348 {
1349 label: "Stripe",
1350 items: [
1351 { label: "Overview", link: "/stripe" },
1352 { label: "Setup", link: "/stripe/setup" },
1353 {
1354 label: "Guides",
1355 items: [
1356 {
1357 label: "Sell a subscription",
1358 link: "/stripe/guides/subscriptions",
1359 },
1360 {
1361 label: "Onboard merchants with Connect",
1362 link: "/stripe/guides/connect",
1363 },
1364 {
1365 label: "React to Stripe events",
1366 link: "/stripe/guides/webhooks",
1367 },
1368 ],
1369 },
1370 providerResourcesEntry("Stripe"),
1371 ],
1372 },
1373 {
1374 label: "Better Auth",
1375 items: [
1376 { label: "Overview", link: "/better-auth" },
1377 {
1378 label: "Tutorial",
1379 collapsed: false,
1380 items: [{ autogenerate: { directory: "better-auth/tutorial" } }],
1381 },
1382 {
1383 label: "Sign-in providers",
1384 items: [
1385 {
1386 autogenerate: { directory: "better-auth/sign-in-providers" },
1387 },
1388 ],
1389 },
1390 {
1391 label: "Databases",
1392 items: [{ autogenerate: { directory: "better-auth/databases" } }],
1393 },
1394 {
1395 label: "Guides",
1396 items: [
1397 {
1398 label: "Config and secrets",
1399 link: "/better-auth/guides/configuration",
1400 },
1401 {
1402 label: "HTTP API middleware",
1403 link: "/better-auth/guides/http-api-middleware",
1404 },
1405 {
1406 label: "Secondary storage",
1407 link: "/better-auth/guides/secondary-storage",
1408 },
1409 { label: "Migrations", link: "/better-auth/guides/migrations" },
1410 {
1411 label: "Upgrading from 1.6 to 1.7.5",
1412 link: "/better-auth/upgrades/from-1-6-to-1-7",
1413 },
1414 ],
1415 },
1416 { ...providerResourcesEntry("BetterAuth"), label: "Reference" },
1417 ],
1418 },
1419 {
1420 label: "Axiom",
1421 items: [
1422 { label: "Overview", link: "/axiom" },
1423 { label: "Setup", link: "/axiom/setup" },
1424 {
1425 label: "Data",
1426 items: [
1427 { label: "Datasets & ingest", link: "/axiom/data/ingest" },
1428 ],
1429 },
1430 {
1431 label: "Guides",
1432 items: [
1433 { label: "Alerting", link: "/axiom/guides/alerting" },
1434 { label: "Dashboards", link: "/axiom/guides/dashboards" },
1435 { label: "Annotations", link: "/axiom/guides/annotations" },
1436 ],
1437 },
1438 providerResourcesEntry("Axiom"),
1439 ],
1440 },
1441 {
1442 label: "GitHub",
1443 items: [
1444 { label: "Overview", link: "/github" },
1445 { label: "Setup", link: "/github/setup" },
1446 { label: "Repositories", link: "/github/repository" },
1447 {
1448 label: "Actions secrets & variables",
1449 link: "/github/actions-config",
1450 },
1451 { label: "Webhooks & events", link: "/github/events" },
1452 providerResourcesEntry("GitHub"),
1453 ],
1454 },
1455 {
1456 label: "Git",
1457 items: [
1458 { label: "Overview", link: "/git" },
1459 { label: "Getting Started", link: "/git/getting-started" },
1460 {
1461 label: "Tutorial",
1462 items: [
1463 {
1464 label: "Part 1: Push your first repository",
1465 link: "/git/tutorial/part-1",
1466 },
1467 {
1468 label: "Part 2: Control access",
1469 link: "/git/tutorial/part-2",
1470 },
1471 {
1472 label: "Part 3: Publish a repository",
1473 link: "/git/tutorial/part-3",
1474 },
1475 {
1476 label: "Part 4: Give users their own credentials",
1477 link: "/git/tutorial/part-4",
1478 },
1479 {
1480 label: "Part 5: Add your application's API",
1481 link: "/git/tutorial/part-5",
1482 },
1483 {
1484 label: "Part 6: Protect a branch",
1485 link: "/git/tutorial/part-6",
1486 },
1487 ],
1488 },
1489 {
1490 label: "Using your host",
1491 items: [
1492 { label: "Cloning & pushing", link: "/git/clone-and-push" },
1493 { label: "Repositories", link: "/git/repositories" },
1494 { label: "Pull requests", link: "/git/pull-requests" },
1495 {
1496 label: "GitHub API compatibility",
1497 link: "/git/github-api",
1498 },
1499 ],
1500 },
1501 {
1502 label: "Building blocks",
1503 items: [
1504 { label: "Overview", link: "/git/blocks" },
1505 { label: "HTTP routes", link: "/git/blocks/server" },
1506 { label: "Engine operations", link: "/git/blocks/engine" },
1507 { label: "Repository", link: "/git/blocks/repositories" },
1508 { label: "Registry", link: "/git/blocks/registry" },
1509 { label: "Blob Store", link: "/git/blocks/blob-store" },
1510 { label: "Hasher", link: "/git/blocks/hasher" },
1511 { label: "Auth", link: "/git/blocks/auth" },
1512 ],
1513 },
1514 {
1515 label: "Recipes",
1516 items: [
1517 { label: "Overview", link: "/git/recipes" },
1518 { label: "Scaling", link: "/git/recipes/scaling" },
1519 { label: "All on Cloudflare", link: "/git/recipes/cloudflare" },
1520 {
1521 label: "Bytes in S3, hashing on Lambda",
1522 link: "/git/recipes/cloudflare-aws",
1523 },
1524 {
1525 label: "Bring your own store",
1526 link: "/git/recipes/your-own-store",
1527 },
1528 ],
1529 },
1530 ],
1531 },
1532 {
1533 label: "Docker",
1534 items: [
1535 { label: "Overview", link: "/docker" },
1536 { label: "Setup", link: "/docker/setup" },
1537 { label: "Run local services", link: "/docker/local-services" },
1538 { label: "Build & push images", link: "/docker/build-and-push" },
1539 providerResourcesEntry("Docker"),
1540 ],
1541 },
1542 {
1543 label: "Kubernetes",
1544 items: [
1545 { label: "Overview", link: "/kubernetes" },
1546 { label: "Setup", link: "/kubernetes/setup" },
1547 {
1548 label: "Tutorial",
1549 items: [{ autogenerate: { directory: "kubernetes/tutorial" } }],
1550 },
1551 {
1552 label: "Clusters",
1553 items: [
1554 {
1555 label: "Connecting to clusters",
1556 link: "/kubernetes/clusters/connecting",
1557 },
1558 {
1559 label: "Container registries",
1560 link: "/kubernetes/clusters/registries",
1561 },
1562 { label: "Local clusters", link: "/kubernetes/clusters/local" },
1563 { label: "Amazon EKS", link: "/kubernetes/clusters/eks" },
1564 {
1565 label: "Cluster adapters",
1566 link: "/kubernetes/clusters/cluster-adapters",
1567 },
1568 ],
1569 },
1570 {
1571 label: "Workloads",
1572 items: [
1573 {
1574 label: "Deployments",
1575 link: "/kubernetes/workloads/deployments",
1576 },
1577 {
1578 label: "Jobs & CronJobs",
1579 link: "/kubernetes/workloads/jobs",
1580 },
1581 {
1582 label: "Container images",
1583 link: "/kubernetes/workloads/images",
1584 },
1585 {
1586 label: "Configuration & bindings",
1587 link: "/kubernetes/workloads/bindings",
1588 },
1589 {
1590 label: "How objects are managed",
1591 link: "/kubernetes/workloads/object-lifecycle",
1592 },
1593 ],
1594 },
1595 {
1596 label: "Objects",
1597 items: [
1598 { label: "Manifests", link: "/kubernetes/objects/manifests" },
1599 {
1600 label: "Helm charts",
1601 link: "/kubernetes/objects/helm-charts",
1602 },
1603 ],
1604 },
1605 providerApiReferenceEntry("Kubernetes"),
1606 ],
1607 },
1608 {
1609 label: "SQL",
1610 items: [
1611 { label: "Overview", link: "/sql" },
1612 { label: "Databases", link: "/sql/databases" },
1613 {
1614 label: "Effect SQL",
1615 items: [
1616 { label: "Postgres", link: "/sql/effect-sql/postgres" },
1617 { label: "MySQL", link: "/sql/effect-sql/mysql" },
1618 { label: "D1", link: "/sql/effect-sql/d1" },
1619 { label: "Migrations", link: "/sql/effect-sql/migrations" },
1620 {
1621 label: "Connection lifecycle",
1622 link: "/sql/effect-sql/lifecycle",
1623 },
1624 ],
1625 },
1626 {
1627 label: "Drizzle",
1628 items: [
1629 { label: "Postgres", link: "/sql/drizzle/postgres" },
1630 { label: "MySQL", link: "/sql/drizzle/mysql" },
1631 { label: "D1", link: "/sql/drizzle/d1" },
1632 { label: "Migrations", link: "/sql/drizzle/migrations" },
1633 ],
1634 },
1635 {
1636 label: "Prisma ORM",
1637 items: [
1638 { label: "Postgres", link: "/sql/prisma/postgres" },
1639 { label: "Contracts", link: "/sql/prisma/contracts" },
1640 { label: "Migrations", link: "/sql/prisma/migrations" },
1641 ],
1642 },
1643 providerApiReferenceEntry("SQL", "Drizzle"),
1644 ],
1645 },
1646 {
1647 label: "Command",
1648 items: [
1649 { label: "Overview", link: "/command" },
1650 {
1651 label: "Memoized builds & commands",
1652 link: "/command/memoization",
1653 },
1654 { label: "Dev servers", link: "/command/dev-servers" },
1655 providerResourcesEntry("Command"),
1656 ],
1657 },
1658 {
1659 label: "ACME",
1660 items: [
1661 { label: "Overview", link: "/acme" },
1662 { label: "Getting started", link: "/acme/getting-started" },
1663 {
1664 label: "Certificate authorities",
1665 link: "/acme/certificate-authorities",
1666 },
1667 { label: "DNS validation", link: "/acme/dns-validation" },
1668 { label: "Renewal & revocation", link: "/acme/renewal" },
1669 { label: "Runtime issuance", link: "/acme/runtime" },
1670 { label: "Using certificates", link: "/acme/using-certificates" },
1671 { label: "Troubleshooting", link: "/acme/troubleshooting" },
1672 providerResourcesEntry("ACME"),
1673 ],
1674 },
1675 providersSidebarEntry(),
1676 ],
1677 // starlight-blog feeds this many posts into the sidebar's "Recent"
1678 // group, which `src/blog-sidebar.ts` re-buckets into Releases/Posts.
1679 // We want every post listed, so set it effectively unlimited.
1680 plugins: [starlightBlog({ recentPostCount: Number.MAX_SAFE_INTEGER })],
1681 routeMiddleware: ["./src/blog-sidebar.ts", "./src/docs-tabs-sidebar.ts"],
1682 }),
1683 mdx(),
1684 ],
1685 vite: {
1686 plugins: [
1687 copyEditor({
1688 handlers: {
1689 jsdoc: jsdocCopyHandler(),
1690 md: markdownFiles(markdownCopy),
1691 },
1692 markdownStyles: { [JSDOC_COPY_STYLE]: jsdocMarkdownStyle },
1693 }),
1694 tailwindcss(),
1695 ],
1696 server: {
1697 // Dev-only: allow sharing the dev server through cloudflared quick
1698 // tunnels (random *.trycloudflare.com hostnames).
1699 allowedHosts: [".trycloudflare.com"],
1700 },
1701 ssr: {
1702 // Sätteri (Astro 7's markdown processor) loads a platform-native
1703 // binding via CJS require. Bundling its JS loader into the prerender
1704 // chunks breaks that resolution under bun's isolated node_modules
1705 // layout, so keep it external — it resolves from website/node_modules
1706 // (declared as a direct dependency) instead.
1707 external: ["satteri", "takumi-js"],
1708 },
1709 },
1710 experimental: {
1711 incrementalBuild: true,
1712 },
1713});