Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
d35e614
feat(website): restructure docs into per-cloud hubs with horizontal t…
sam-goodwin Jul 2, 2026
2328dea
feat(website): mockup-fidelity docs chrome + AWS tutorial and runtime…
sam-goodwin Jul 2, 2026
5cfaf72
fix(website): flatten docs chrome onto one surface
sam-goodwin Jul 2, 2026
f429d71
fix(website): page-wide accent wash instead of sidebar-only gradient
sam-goodwin Jul 2, 2026
29d3208
feat(website): per-cloud Resources section in hub sidebars
sam-goodwin Jul 2, 2026
f9cc65c
fix(website): nested sidebar group labels use entry typography
sam-goodwin Jul 2, 2026
d4c7438
feat(website): auto-expand the hub Resources section
sam-goodwin Jul 2, 2026
6d74c2f
fix(website): drop "pick one" from the AWS Compute sidebar group
sam-goodwin Jul 2, 2026
6465478
feat(website): provider hubs for PlanetScale, Neon, Axiom, GitHub + M…
sam-goodwin Jul 2, 2026
f365d7b
docs(website): wave 1 — P0 pages from the content plan
sam-goodwin Jul 2, 2026
6394906
docs(website): wave 2 — P1 pages from the content plan
sam-goodwin Jul 2, 2026
4f0dc13
docs(website): wave 3 — long tail + native database-provider guides
sam-goodwin Jul 2, 2026
470e39c
fix(website): flatten small providers' reference trees
sam-goodwin Jul 2, 2026
836585a
docs(website): Containers building-block page in Cloudflare Compute
sam-goodwin Jul 2, 2026
9a279d7
chore(website): PR screenshots for the docs redesign
sam-goodwin Jul 2, 2026
9baeb44
docs(website): drop the "Building blocks" grouping
sam-goodwin Jul 2, 2026
65081f9
chore(website): script to refresh PR screenshots
sam-goodwin Jul 2, 2026
9a92440
chore(website): refresh PR screenshots
sam-goodwin Jul 2, 2026
178e83a
docs(website): add Containers to the Cloudflare overview Compute section
sam-goodwin Jul 2, 2026
3f1c0cf
chore(website): refresh PR screenshots
sam-goodwin Jul 2, 2026
85282d6
docs(website): shared category taxonomy across hubs — guides dump dis…
sam-goodwin Jul 2, 2026
3512cb7
chore(website): refresh PR screenshots
sam-goodwin Jul 2, 2026
437cc63
docs(website): Docs tab taxonomy — Concepts and Guides groups dissolved
sam-goodwin Jul 2, 2026
e16dbd1
chore(website): refresh PR screenshots
sam-goodwin Jul 2, 2026
d50a355
docs(website): final Docs taxonomy — IaC/IaE, State Store, Project st…
sam-goodwin Jul 2, 2026
35e0274
chore(website): refresh PR screenshots
sam-goodwin Jul 2, 2026
c04054e
docs(website): split the Monorepo guide — tree-first, three pages
sam-goodwin Jul 2, 2026
e3fabed
chore(website): refresh PR screenshots
sam-goodwin Jul 2, 2026
85eb933
docs(website): rework Stack References — three forms, zero fluff
sam-goodwin Jul 2, 2026
0e94d36
chore(website): refresh PR screenshots
sam-goodwin Jul 2, 2026
888bbec
docs(website): Stack References leads with the cross-stage form
sam-goodwin Jul 2, 2026
0af9e93
docs(website): fold Stack References into the References concept page
sam-goodwin Jul 2, 2026
c93f2a9
chore(website): refresh PR screenshots
sam-goodwin Jul 2, 2026
5bb37d9
docs(website): file-layout opens with the target file tree
sam-goodwin Jul 2, 2026
4a30f4d
docs(website): split Testing into concept, walkthrough, providers, ha…
sam-goodwin Jul 2, 2026
33e5439
chore(website): refresh PR screenshots
sam-goodwin Jul 2, 2026
bb50206
docs(website): CLI hub tab — 694-line reference becomes 15 pages by role
sam-goodwin Jul 2, 2026
5463583
chore(website): refresh PR screenshots
sam-goodwin Jul 2, 2026
41589e5
docs(website): align file locations with the section taxonomy
sam-goodwin Jul 2, 2026
52b8f7c
docs(website): rename the Docs tab to Core
sam-goodwin Jul 2, 2026
5318104
chore(website): refresh PR screenshots
sam-goodwin Jul 2, 2026
238a731
docs(website): migrating-from-v1 tutorial CTA offers both clouds
sam-goodwin Jul 2, 2026
541329e
docs(website): use Console.log inside Effect.gen on the Stack page
sam-goodwin Jul 2, 2026
7369621
docs(website): IaE reorg, RPC section, Auth Providers, Effectful Cons…
sam-goodwin Jul 2, 2026
1a5e946
chore(website): refresh PR screenshots
sam-goodwin Jul 2, 2026
b3626b6
docs(website): APIs section parity, progressive Effect RPC/HTTP, Fetc…
sam-goodwin Jul 2, 2026
83577bc
chore(website): refresh PR screenshots
sam-goodwin Jul 2, 2026
a72cdba
docs(website): re-cut APIs pages — cloud-neutral Core, platform detai…
sam-goodwin Jul 2, 2026
b9d3e73
chore(website): refresh PR screenshots
sam-goodwin Jul 2, 2026
cad4013
docs(website): Cloudflare Frontend redesign — support matrix + per-fr…
sam-goodwin Jul 3, 2026
2fa8404
chore(website): refresh PR screenshots
sam-goodwin Jul 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .github/pr-assets/721/aws-hub.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/pr-assets/721/axiom-hub.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/pr-assets/721/cloudflare-hub-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/pr-assets/721/cloudflare-hub.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/pr-assets/721/docs-tab.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/pr-assets/721/landing.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/pr-assets/721/planetscale-hub.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/pr-assets/721/reference.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
49 changes: 49 additions & 0 deletions .github/pr-assets/update-screenshots.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/usr/bin/env bash
# Regenerate the PR screenshots from the built site and commit them if changed.
# Usage: .github/pr-assets/update-screenshots.sh [pr-number]
# Assumes website/dist is freshly built (DOCS_FAST=1 bun astro build).
set -euo pipefail

PR="${1:-721}"
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
OUT="$ROOT/.github/pr-assets/$PR"
DIST="$ROOT/website/dist"
CHROME="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
PORT=4899

[ -d "$DIST" ] || { echo "website/dist missing — build first"; exit 1; }
mkdir -p "$OUT"

python3 -m http.server "$PORT" --directory "$DIST" >/dev/null 2>&1 &
SERVER_PID=$!
trap 'kill $SERVER_PID 2>/dev/null || true' EXIT
sleep 1

shot() { # shot <file> <path> [extra chrome flags...]
local file="$1" path="$2"; shift 2
"$CHROME" --headless=new --disable-gpu --hide-scrollbars \
--window-size=1600,1000 --virtual-time-budget=6000 \
"$@" --screenshot="$OUT/$file" "http://localhost:$PORT$path" 2>/dev/null
echo "$file: $(wc -c <"$OUT/$file" | tr -d ' ') bytes"
}

shot landing.png /
shot docs-tab.png /getting-started/
shot cloudflare-hub.png /cloudflare/
shot aws-hub.png /aws/
shot planetscale-hub.png /planetscale/
shot axiom-hub.png /axiom/
shot reference.png /providers/cloudflare/workers/durableobject/
shot cloudflare-hub-dark.png /cloudflare/ --force-dark-mode --blink-settings=preferredColorScheme=2

cd "$ROOT"
if git status --porcelain -- ".github/pr-assets/$PR" | grep -q .; then
git add ".github/pr-assets/$PR"
git commit -q -m "chore(website): refresh PR screenshots

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>"
git push
echo "screenshots refreshed and pushed"
else
echo "screenshots unchanged"
fi
3 changes: 3 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 5 additions & 9 deletions packages/alchemy/src/Cloudflare/Website/Vite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export interface ViteProps<Bindings extends WorkerBindingProps = {}>
* ```
*
* @section SSR Frameworks
* For SSR frameworks like TanStack Start, SolidStart, or Nuxt, enable
* For SSR frameworks like TanStack Start or SolidStart, enable
* `nodejs_compat` so the server bundle can use Node.js APIs.
*
* @example TanStack Start
Expand All @@ -65,9 +65,7 @@ export interface ViteProps<Bindings extends WorkerBindingProps = {}>
* compatibility: {
* flags: ["nodejs_compat"],
* },
* assets: {
* config: { runWorkerFirst: true },
* },
* assets: { runWorkerFirst: true },
* });
* ```
*
Expand All @@ -81,7 +79,7 @@ export interface ViteProps<Bindings extends WorkerBindingProps = {}>
*
* @example React Router with RSC
* ```typescript
* const app = yield* Cloudflare.Vite("ReactRouterRSC", {
* const app = yield* Cloudflare.Website.Vite("ReactRouterRSC", {
* compatibility: {
* flags: ["nodejs_compat"],
* },
Expand All @@ -103,10 +101,8 @@ export interface ViteProps<Bindings extends WorkerBindingProps = {}>
* flags: ["nodejs_compat"],
* },
* assets: {
* config: {
* htmlHandling: "auto-trailing-slash",
* notFoundHandling: "single-page-application",
* },
* htmlHandling: "auto-trailing-slash",
* notFoundHandling: "single-page-application",
* },
* });
* ```
Expand Down
2 changes: 1 addition & 1 deletion packages/alchemy/src/Cloudflare/Workflows/Workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ export class WorkflowScope extends Context.Service<
* ```
*
* @resource
* @product Workers
* @product Workflows
* @category Workers & Compute
*
* @section Defining a Workflow
Expand Down
129 changes: 111 additions & 18 deletions scripts/generate-api-reference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,12 @@ function renderPage(doc: PageDoc): string {
/** Providers shown first in the sidebar; the rest follow alphabetically. */
const PROVIDER_ORDER = ["AWS", "Cloudflare"];

/**
* Uncategorized providers with at most this many pages render as a flat
* resource list instead of per-service folders (see buildProvidersSidebar).
*/
const FLAT_PROVIDER_MAX_PAGES = 16;

interface SidebarLeaf {
label: string;
link: string;
Expand Down Expand Up @@ -411,26 +417,28 @@ function orderedKeys(keys: string[], order: string[]): string[] {

/**
* Build sidebar items for one set of pages sharing a provider+category:
* every service (product) is its own collapsible folder containing its
* resource pages, mirroring how Cloudflare's API reference gives each
* product its own section — even single-page products like D1 or
* Organization — so the grouping is uniform.
* every product is its own collapsible folder containing its resource
* pages, mirroring how Cloudflare's API reference gives each product its
* own section — even single-page products like D1 or Organization — so
* the grouping is uniform.
*
* Grouping is by resolved product LABEL (`@product`, falling back to the
* service dir name), not by directory: two directories declaring the same
* product merge into one group instead of rendering duplicate siblings.
*/
function buildServiceItems(pages: PageEntry[]): SidebarItem[] {
const byService = new Map<string, PageEntry[]>();
const byLabelKey = new Map<string, PageEntry[]>();
for (const p of pages) {
const key = p.service || p.resource;
if (!byService.has(key)) byService.set(key, []);
byService.get(key)!.push(p);
const key = p.product || p.service || p.resource;
if (!byLabelKey.has(key)) byLabelKey.set(key, []);
byLabelKey.get(key)!.push(p);
}
const items: SidebarItem[] = [];
for (const [service, servicePages] of byService) {
// Prefer the human product name from `@product`; fall back to the dir name.
const label = servicePages.find((p) => p.product)?.product || service;
for (const [label, productPages] of byLabelKey) {
items.push({
label,
collapsed: true,
items: servicePages
items: productPages
.map((p) => ({ label: p.resource, link: p.link }))
.sort(byLabel),
});
Expand All @@ -448,12 +456,25 @@ function buildProvidersSidebar(entries: PageEntry[]): SidebarItem[] {
const providers: SidebarGroup[] = [];
for (const provider of orderedKeys([...byProvider.keys()], PROVIDER_ORDER)) {
const pages = byProvider.get(provider)!;

// `@category` is per-file; a documented file that omits it must not fall
// out of its service's category and render a duplicate service group at
// the provider root. Inherit the category any sibling page of the same
// service dir declares.
const categoryByService = new Map<string, string>();
for (const p of pages) {
if (p.service && p.category && !categoryByService.has(p.service)) {
categoryByService.set(p.service, p.category);
}
}

const categorized = new Map<string, PageEntry[]>();
const uncategorized: PageEntry[] = [];
for (const p of pages) {
if (p.category) {
if (!categorized.has(p.category)) categorized.set(p.category, []);
categorized.get(p.category)!.push(p);
const category = p.category || categoryByService.get(p.service) || "";
if (category) {
if (!categorized.has(category)) categorized.set(category, []);
categorized.get(category)!.push(p);
} else {
uncategorized.push(p);
}
Expand All @@ -469,15 +490,55 @@ function buildProvidersSidebar(entries: PageEntry[]): SidebarItem[] {
items: buildServiceItems(categorized.get(cat)!),
});
}
// Pages without a category fall back to service grouping directly under
// the provider (this is how AWS renders until it gets categorized).
items.push(...buildServiceItems(uncategorized));
if (categorized.size === 0 && pages.length <= FLAT_PROVIDER_MAX_PAGES) {
// Small uncategorized providers (Neon, Planetscale, Axiom, GitHub, …)
// render as a flat resource list — per-service folders around one or
// two pages ("Branch > Branch") are redundant nesting, and prefixed
// resource names (MySQLBranch/PostgresBranch) already carry the
// grouping information.
items.push(
...uncategorized
.map((p) => ({ label: p.resource, link: p.link }))
.sort(byLabel),
);
} else {
// Pages without a category fall back to service grouping directly under
// the provider (this is how AWS renders until it gets categorized).
items.push(...buildServiceItems(uncategorized));
}

providers.push({ label: provider, collapsed: true, items });
}

assertNoDuplicateSiblings(providers, []);
return providers;
}

/**
* Duplicate sibling labels are always a tagging bug (e.g. two products
* resolving to the same name in one category) and render as confusing
* twin sections — fail the generation instead of shipping them.
*/
function assertNoDuplicateSiblings(items: SidebarItem[], path: string[]) {
const seen = new Map<string, number>();
for (const item of items) {
seen.set(item.label, (seen.get(item.label) ?? 0) + 1);
}
const dups = [...seen.entries()].filter(([, n]) => n > 1);
if (dups.length > 0) {
throw new Error(
`Duplicate sidebar sibling label(s) under "${path.join(" > ") || "(root)"}": ${dups
.map(([label, n]) => `"${label}" ×${n}`)
.join(", ")} — fix the @product/@category tags on the offending files.`,
);
}
for (const item of items) {
if ("items" in item) {
assertNoDuplicateSiblings(item.items, [...path, item.label]);
}
}
}

async function main() {
const entries = await discoverFiles();
console.log(`Discovered ${entries.length} source files.`);
Expand Down Expand Up @@ -555,6 +616,38 @@ async function main() {
}

const sidebar = buildProvidersSidebar(pageEntries);

// Landing page for the Reference tab (/providers). Regenerated with the
// rest of the tree on every run.
const countByProvider = new Map<string, number>();
for (const e of pageEntries) {
countByProvider.set(e.provider, (countByProvider.get(e.provider) ?? 0) + 1);
}
const referenceIndex = [
"---",
"title: API Reference",
"description: Generated API reference for every alchemy resource, organized by provider.",
"---",
"",
"The complete, generated reference for every resource alchemy can manage,",
"extracted from the source JSDoc. Pick a provider in the sidebar, or search",
"with `⌘K`.",
"",
...orderedKeys([...countByProvider.keys()], PROVIDER_ORDER).map(
(provider) =>
`- **${provider}** — ${countByProvider.get(provider)} resources`,
),
"",
"Looking for curated docs instead? Each cloud's tab (Cloudflare, AWS) has",
"setup, tutorials, and building-block pages that link into this reference.",
"",
].join("\n");
await fs.writeFile(
path.join(config.outRoot, "index.md"),
referenceIndex,
"utf8",
);

const sidebarPath = path.join(
websiteRoot,
"src/generated/providers-sidebar.json",
Expand Down
Loading
Loading