This is Hexi's Vibe Websites repository, a pnpm monorepo for experimental website demos.
The hub works as the demo directory page, each demo is deployed as an independent Cloudflare Worker project, and the root workspace handles shared scripts, registry generation, and CI/CD.
- Manages multiple website demos in one monorepo
- Allows each demo to use a different tech stack
- Deploys each demo independently to Cloudflare Workers
- Provides one hub page to list and link published demos
- Supports dynamic GitHub Actions deployment without hard-coding demo names
apps/hubThe demo directory page. It reads generated demo metadata and renders the list.apps/demos/*Independent demo projects. Each demo owns its build config, Wrangler config, and deploy command.packages/demo-registryGenerated metadata consumed by the hub.scripts/generate-demo-registry.mjsScansapps/demos/*/demo.config.jsonand generates the registry file.scripts/new-demo.mjsInteractive scaffolder for creating new demos.scripts/detect-deploy-targets.mjsDetects which demos need deployment and whether the hub should deploy in GitHub Actions..github/workflows/deploy.ymlDynamic CI/CD workflow for hub and demos.
pnpm installInstall workspace dependencies.pnpm devGenerate the registry and start local development services for hub and demos in parallel.pnpm buildBuild the hub and all demos independently.pnpm deployDeploy currently targeted published demos plus the hub.pnpm deploy:demosDeploy demos only.pnpm deploy:hubDeploy the hub only.pnpm run generate:registryRegenerate the demo registry file.pnpm new:demoStart the interactive demo scaffolder.
Run:
pnpm new:demoThe script will ask for:
slugtemplate
Currently supported templates:
reactReact + Vite + Cloudflare WorkersnextjsNext.js + OpenNext + Cloudflare Workers + Tailwind CSS
You can also pass arguments directly:
pnpm new:demo my-demo
pnpm new:demo my-demo react
pnpm new:demo my-next-demo nextjsEach demo lives under apps/demos/<slug> and should include at least:
demo.config.json- a framework build config such as
vite.config.tsornext.config.ts wrangler.jsonc- the framework-specific app entry files
demo.config.json is the source of truth for both hub listing and deployment behavior.
Typical fields:
slugtitledescriptionstackstatuslocalUrlproductionUrl
Rules:
- Only demos with
status: "published"appear on the hub - Only demos with
status: "published"are included in automatic CI deployment productionUrlis the URL used by the hub onlinelocalUrlis the URL used by the hub during local development
The hub consumes generated data from:
This file is generated from every demo's demo.config.json, so it should not be edited manually.
You typically need to regenerate it:
- after creating a new demo
- after changing a demo's
status - after updating a demo's
productionUrl
In normal usage, root build and dev already run registry generation for you.
This repository does not bundle all demos into a single Worker.
Instead:
- each demo is its own Worker project
- the hub is another Worker project
- the hub uses registry metadata to render and link demos
Benefits:
- deployments are isolated per demo
- avoids concentrating everything into one Worker bundle
- adding new demos does not require changing the deployment model
The built-in workflow lives at:
Its flow is:
detect-changeschecks which files changed- It extracts affected demo slugs from paths like
apps/demos/<slug>/... - It filters to demos with
status: "published" deploy-demosuses a dynamic matrix to deploy only those changed demos- If hub-related files or demo metadata changed,
deploy-hubruns afterward
Why this supports dynamic scaling:
- demo names are not hard-coded in the workflow
- the demo list is calculated from changed paths at runtime
- any new demo under
apps/demos/<slug>is automatically picked up
Manual full deployment is also supported via workflow_dispatch.
Set these repository secrets in GitHub:
CLOUDFLARE_API_TOKENCLOUDFLARE_ACCOUNT_ID
Without them, Wrangler cannot deploy in CI.
If you deploy locally with Wrangler, make sure your shell has Cloudflare credentials:
export CLOUDFLARE_API_TOKEN=...
export CLOUDFLARE_ACCOUNT_ID=...Then run:
pnpm run deploy- If
package.jsonchanges, remember to commit the updatedpnpm-lock.yaml - If you use
git commit --amendand thenpush -f, the workflow still works because deploy target detection now falls back when the old SHA is unavailable - Hub visibility depends on
status: "published", not just whetherproductionUrlis filled