| 1 | import mdx from "@astrojs/mdx"; |
| 2 | import react from "@astrojs/react"; |
| 3 | import sitemap from "@astrojs/sitemap"; |
| 4 | import starlight from "@astrojs/starlight"; |
| 5 | import tailwindcss from "@tailwindcss/vite"; |
| 6 | import type { AstroIntegration } from "astro"; |
| 7 | import { defineConfig } from "astro/config"; |
| 8 | import { |
| 9 | copyEditor, |
| 10 | markdownBlocks, |
| 11 | markdownFiles, |
| 12 | type MarkdownFilesOptions, |
| 13 | } from "@alchemy.run/vite-plugin-copy-editor"; |
| 14 | import { promises as fs } from "node:fs"; |
| 15 | import path from "node:path"; |
| 16 | import { fileURLToPath } from "node:url"; |
| 17 | import starlightBlog from "starlight-blog"; |
| 18 | import { buildOutputChecks, noindexPaths } from "./plugins/build-output.ts"; |
| 19 | import { jsdocCopyHandler, jsdocMarkdownStyle } from "./plugins/jsdoc-copy.ts"; |
| 20 | import { JSDOC_COPY_STYLE } from "../scripts/jsdoc-blocks.ts"; |
| 21 | import providersSidebar from "./src/generated/providers-sidebar.json" with { type: "json" }; |
| 22 | import { 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 | */ |
| 30 | function 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 | */ |
| 65 | function 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 | |
| 87 | function 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 | |
| 98 | type ReferenceItem = |
| 99 | | { label: string; link: string } |
| 100 | | { label: string; items: readonly ReferenceItem[] }; |
| 101 | |
| 102 | function 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 | */ |
| 139 | const markdownCopy: MarkdownFilesOptions = { |
| 140 | root: fileURLToPath(new URL(".", import.meta.url)), |
| 141 | exclude: /\/src\/content\/docs\/providers\//, |
| 142 | style: JSDOC_COPY_STYLE, |
| 143 | }; |
| 144 | |
| 145 | function 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 | |
| 163 | function 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 | |
| 227 | export 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 | }); |