Web Stack¶
Tools¶
| Purpose | Tool | Config |
|---|---|---|
| Package management | npm | package.json |
| Browser app | Vite + React | vite.config.ts |
| API and hosting | Cloudflare Workers + Static Assets | wrangler.jsonc |
| Relational persistence | Cloudflare D1 | migrations/ |
| Formatter | Prettier | package.json scripts |
| Linter | ESLint flat config | eslint.config.mjs |
| Type checker | TypeScript | tsconfig.json |
| Test runner | Vitest | vitest.config.ts |
The generated stack targets Node 22 through .mise.toml and installs npm
dependencies during mise run setup.
Optional web variants are available at init time:
harness-scaffold init --non-interactive --name my-dashboard --shape single --stack web --web-ui plain --web-db d1
harness-scaffold init --non-interactive --name my-dashboard --shape single --stack web --web-ui tailwind --web-db d1
harness-scaffold init --non-interactive --name my-dashboard --shape single --stack web --web-ui shadcn --web-db drizzle-d1
--web-ui shadcn implies Tailwind setup and generates a small shadcn-compatible
Button, cn() helper, and components.json. --web-db drizzle-d1 still uses
the Cloudflare D1 binding and migration file; it changes the Worker access layer
from raw prepared SQL to Drizzle's D1 adapter.
Generated layout¶
my-dashboard/
├── package.json
├── wrangler.jsonc
├── index.html
├── src/ # React app
├── worker/ # Cloudflare Worker API routes
├── migrations/ # D1 schema migrations
├── tests/ # Vitest coverage
└── public/ # Static assets and seed data
When --web-ui shadcn is selected, the generated layout also includes:
When --web-db drizzle-d1 is selected, the generated layout also includes:
The worker includes a small /api/health route and saved-run endpoints:
GET /api/runs/minePOST /api/runs
The saved-run table is intentionally generic: an owner id, a title, a lineup
JSON blob, a result JSON blob, and timestamps. That gives generated web apps a
relational home for login-owned saved runs without committing the template to a
specific auth provider. The default --web-db d1 route code uses raw D1 prepared
statements; --web-db drizzle-d1 uses the same table and D1 binding through
drizzle-orm/d1.
Task contract¶
mise run setup # npm install --package-lock=false
mise run fmt # prettier --write on app/worker/test/config files
mise run lint # eslint src worker tests vite/vitest configs
mise run typecheck # tsc --noEmit
mise run test # vitest run
mise run build # vite build
mise run check # fmt-check + lint + typecheck + test
mise run verify # check + build + wrangler deploy dry-run
mise run test writes test-results/vitest.txt for CI artifact upload.
Cloudflare bindings¶
Generated wrangler.jsonc includes:
assets.directory = "./dist"andassets.binding = "ASSETS"for the Vite static build- a D1 binding named
DB workers_dev = truefor first deploys before a custom domain is configured
Custom domains, production D1 database ids, Cloudflare Access, and external auth providers are intentionally left as follow-up configuration. The template keeps those boundaries explicit instead of faking a provider-specific setup.